API การแสดงผล CSS

Surma
Surma

ความสามารถใหม่ๆ ใน Chrome 65

เปิดใช้ CSS Paint API (หรือที่เรียกว่า “CSS Custom Paint” หรือ “Houdini’s Paint Worklet) โดยค่าเริ่ม���้นตั้งแต่ Chrome 65 เป็นต้นไป สิ่งนี้คืออะไร คุณสามารถทำอะไร กับโมเดลนี้ได้บ้าง และทำงานอย่างไร อ่านต่อเลย แล้วจะ...

CSS Paint API ช่วยให้คุณสร้างรูปภาพแบบเป็นโปรแกรมได้ทุกครั้งที่พร็อพเพอร์ตี้ CSS ต้องการรูปภาพ โดยปกติแล้ว พร็อพเพอร์ตี้อย่างเช่น background-image หรือ border-image จะใช้กับ url() เพื่อโหลดไฟล์ภาพ หรือใช้กับฟังก์ชัน CSS ในตัว เช่น linear-gradient() ซึ่งตอนนี้คุณสามารถใช้ paint(myPainter) เพื่ออ้างอิงเวิร์กเล็ตชุดสีแทน

การเขียนชิ้นงานสี

หากต้องการกำหนด Worklet Paint ที่ชื่อ myPainter เราต้องโหลดไฟล์ CSS Paint Worklet โดยใช้ CSS.paintWorklet.addModule('my-paint-worklet.js') ในไฟล์ดังกล่าว เราใช้ฟังก์ชัน registerPaint เพื่อลงทะเบียนคลาส Paint Worklet ได้ ดังนี้

class MyPainter {
  paint(ctx, geometry, properties) {
    // ...
  }
}

registerPaint('myPainter', MyPainter);

ภายในการเรียกกลับ paint() เราสามารถใช้ ctx ในลักษณะเดียวกับที่ใช้ CanvasRenderingContext2D อย่างที่เรารู้จักจาก <canvas> หากคุณรู้วิธีวาดใน <canvas> คุณสามารถวาดในเวิร์กเล็ตได้! geometry จะบอกความกว้างและความสูงของภาพพิมพ์แคนวาสที่เราเลือกเอง properties ซึ่งฉันจะอธิบายภายหลังในบทความนี้

สำหรับตัวอย่างเบื้องต้น เรามาลองเขียนสมุดระบายสีลายตารางหมากรุกและใช้เป็นภาพพื้นหลังของ <textarea> กัน (ฉันใช้พื้นที่ข้อความเพราะ ปรับขนาดได้โดยค่าเร��่����้น):

<!-- index.html -->
<!doctype html>
<style>
  textarea {
    background-image: paint(checkerboard);
  }
</style>
<textarea></textarea>
<script>
  CSS.paintWorklet.addModule('checkerboard.js');
</script>
// checkerboard.js
class CheckerboardPainter {
  paint(ctx, geom, properties) {
    // Use `ctx` as if it was a normal canvas
    const colors = ['red', 'green', 'blue'];
    const size = 32;
    for(let y = 0; y < geom.height/size; y++) {
      for(let x = 0; x < geom.width/size; x++) {
        const color = colors[(x + y) % colors.length];
        ctx.beginPath();
        ctx.fillStyle = color;
        ctx.rect(x * size, y * size, size, size);
        ctx.fill();
      }
    }
  }
}

// Register our class under a specific name
registerPaint('checkerboard', CheckerboardPainter);

หากคุณเคยใช้ <canvas> มาก่อน โค้ดนี้น่าจะดูคุ้นเคย ดู การสาธิต สดที่นี่

สิ่งที่แตกต่างจากการใช้ภาพพื้นหลังทั่วไปตรงนี้คือ จะมีการวาดรูปแบบดังกล่าวใหม่ตามคำขอ เมื่อใดก็ตามที่ผู้ใช้ปรับขนาดพื้นที่ข้อความ ซึ่งหมายความว่าภาพพื้นหลังจะมีขนาดใหญ่ตามต้องการเสมอ รวมถึงค่าชดเชยสำหรับจอแสดงผลความหนาแน่นสูง

เยี่ยมไปเลย แต่ก็เป็นภาพนิ่งด้วย เราจะเขียนเวิร���กเล็ตใหม่ ทุกครั้งที่ต้องการรูปแบบเดียวกันแต่มีสี่เหลี่ยมจัตุรัส ขนาดต่างกันไหม คำตอบคือไม่

พารามิเตอร์ของ Worklet

โชคดีที่ Worklet สำหรับ Paint สามารถเข้าถึงพร็อพเพอร์ตี้ CSS อื่นๆ ได้ ซึ่งเป็นที่มาของพารามิเตอร์ properties เพิ่มเติม การกำหนดแอตทริบิวต์ inputProperties แบบคงที่ให้กับคลาสจะช่วยให้คุณติดตามการเปลี่ยนแปลงในพร็อพเพอร์ตี้ CSS รวมถึงพร็อพเพอร์ตี้ที่กำหนดเองได้ คุณจะได้รับค่านี้ผ่านพารามิเตอร์ properties

<!-- index.html -->
<!doctype html>
<style>
  textarea {
    /* The paint worklet subscribes to changes of these custom properties. */
    --checkerboard-spacing: 10;
    --checkerboard-size: 32;
    background-image: paint(checkerboard);
  }
</style>
<textarea></textarea>
<script>
  CSS.paintWorklet.addModule('checkerboard.js');
</script>
// checkerboard.js
class CheckerboardPainter {
  // inputProperties returns a list of CSS properties that this paint function gets access to
  static get inputProperties() { return ['--checkerboard-spacing', '--checkerboard-size']; }

  paint(ctx, geom, properties) {
    // Paint worklet uses CSS Typed OM to model the input values.
    // As of now, they are mostly wrappers around strings,
    // but will be augmented to hold more accessible data over time.
    const size = parseInt(properties.get('--checkerboard-size').toString());
    const spacing = parseInt(properties.get('--checkerboard-spacing').toString());
    const colors = ['red', 'green', 'blue'];
    for(let y = 0; y < geom.height/size; y++) {
      for(let x = 0; x < geom.width/size; x++) {
        ctx.fillStyle = colors[(x + y) % colors.length];
        ctx.beginPath();
        ctx.rect(x*(size + spacing), y*(size + spacing), size, size);
        ctx.fill();
      }
    }
  }
}

registerPaint('checkerboard', CheckerboardPainter);

ตอนนี้ เราสามารถใช้โค้ดเดียวกันสำหรับกระดานหมากรุกทุกประเภทได้ แต่ยิ่งไปกว่านั้น ในตอนนี้เราสามารถไปที่เครื่องมือสำหรับนักพัฒนาเว็บและหาค่าต่างๆ จนกว่าเราจะพบรูปแบบที่เหมาะสม

เบราว์เซอร์ที่ไม่รองรับ Worklet Paint

ในขณะที่เขียน มีเพียง Chrome เท่านั้นที่ติดตั้งใช้งาน Worklet สีได้ แม้ว่าผู้ให้บริการเบราว์เซอร์อื่นๆ ทั้งหมดจะมีสัญญาณเชิงบวก แต่ก็ยังไม่มีความคืบหน้ามากนัก หากต้องการดูข่าวสารล่าสุด โปรดดู Is Houdini Ready Yet? อย่างสม่ำเสมอ ในระหว่างนี้ โปรดใช้การเพิ่มประสิทธิภาพแบบต่อเนื่องเพื่อให้โค้ดทํางานต่อไปแม้ว่าจะไม่รองรับเวิร์กเล็ต Paint ก็ตาม เพื่อให้แน่ใจว่าการทำงานเป็นไปตามที่คาดไว้ คุณจะต้องปรับเปลี่ยนโค้ดใน 2 ที่ ได้แก่ CSS และ JS

การตรวจหาการรองรับ Paint Worklet ใน JS ทำได้โดยตรวจสอบออบเจ็กต์ CSS ดังนี้ js if ('paintWorklet' in CSS) { CSS.paintWorklet.addModule('mystuff.js'); } สำหรับฝั่ง CSS คุณมี 2 ตัวเลือก คุณใช้ @supports ได้โดยทำดังนี้

@supports (background: paint(id)) {
  /* ... */
}

เคล็ดลับที่กระชับกว่าคือการใช้ข้อเท็จจริงที่ว่า CSS ทำให้เป็นโมฆะ และจะไม่สนใจการประกาศพร็อพเพอร์ตี้ทั้งหมดในภายหลังหากมีฟังก์ชันที่ไม่รู้จัก หากระบุพร็อพเพอร์ตี้ 2 ครั้ง โดยครั้งแรกไม่มี Worklet สี ตามด้วย Worklet สี คุณจะได้รับการเพิ่มประสิทธิภาพแบบต่อเนื่องดังนี้

textarea {
  background-image: linear-gradient(0, red, blue);
  background-image: paint(myGradient, red, blue);
}

ในเบราว์เซอร์ที่มีการรองรับ Paint Worklet การประกาศครั้งที่ 2 ของ background-image จะเขียนทับรายการแรก ในเบราว์เซอร์ที่ไม่มีการรองรับ Worklet สำหรับ Paint การประกาศครั้งที่ 2 ไม่ถูกต้องและจะถูกนำออก โดยการประกาศแรกจะมีผล

โพลีฟิลล์สำหรับสี CSS

คุณยังใช้ CSS Paint Polyfill เพื่อการใช้งานหลายอย่างได้ ซึ่งจะเพิ่มการรองรับ CSS Custom Paint และ Paint Worklets ลงในเบราว์เซอร์ที่ทันสมัย

กรณีการใช้งาน

ตัวอย่างการใช้งานสำหรับ Paintlets มีมากมาย บางกรณีก็เห็นได้ชัดเจนกว่าเครื่องมืออื่นๆ หนึ่งในเทคนิคที่ชัดเจนกว่านั้นคือการใช้ Paint Worklet เพื่อลดขนาด DOM บ่อยครั้งที่มีการเพิ่มองค์ประกอบเพื่อสร้างการตกแต่งโดยใช้ CSS ตัวอย่างเช่น ใน Material Design Lite ปุ่มที่มีเอฟเฟกต์ระลอกคลื่นจะมีองค์ประกอบ <span> เพิ่มเติมอีก 2 รายการเพื่อนำคลื่นมาใช้เอง หากคุณมีปุ่มจำนวนมาก อาจทำให้เพิ่มองค์ประกอบ DOM ได้จำนวนมาก และอาจทำให้ประสิทธิภาพในอุปกรณ์เคลื่อนที่ลดลงได้ หากใช้เอฟเฟกต์ระลอกคลื่นโดยใช้เวิร์กเล็ต Paint แทน จะมีองค์ประกอบเพิ่มเติม 0 รายการและเวิร์กเล็ต Paint 1 รายการ นอกจากนี้ยังมีฟีเจอร์ที่ปรับแต่งและแยกพารามิเตอร์ได้ง่ายขึ้นอีกด้วย

ข้อดีอีกอย่างหนึ่งของการใช้ Paint Worklet ก็คือ โดยทั่วไปแล้วโซลูชันที่ใช้ Paint Worklet จะมีขนาดเล็กในแง่ของไบต์ แน่นอนว่าต้องมีการแลกเปลี่ยนกัน กล่าวคือ รหัสสีจะทำงานเมื่อขนาดของภาพพิมพ์แคนวาสหรือพารามิเตอร์เปลี่ยนแปลง ดังนั้นหากโค้ดของคุณซับซ้อนและใช้เวลานาน ก็อาจเกิดการกระตุกได้ Chrome กำลังดำเนินการย้าย Paint Worklet ออกจากเทรดหลัก เพื่อให้แม้แต่ไฟล์ Paint ที่ทำงานนานๆ ก็ไม่ส่งผลต่อการตอบสนองของเทรดหลัก

สำหรับผมแล้ว โอกาสที่น่าตื่นเต้นที่สุดคือ Paint Worklet ช่วยให้มีการใช้โพลีฟิลล์อย่างมีประสิทธิภาพสำหรับฟีเจอร์ CSS ที่เบราว์เซอร์ยังไม่มี ตัวอย่างหนึ่งก็คือการใช้ Polyfill การไล่ระดับสีแบบกรวย จนกว่าจะไปถึง Chrome ในเครื่อง อีกตัวอย่างหนึ่งคือในการประชุม CSS เราตัดสินใจว่าตอนนี้คุณมีเส้นขอบหลายสีได้แล้ว ขณะที่การประชุมนี้ดำเนินไป เพื่อ���ร่วมงานของฉัน Ian Kilpatrick ได้เขียน Polyfill สำหรับลักษณะการทำงานของ CSS ใหม่นี้โดยใช้ Paint Worklet

การคิดนอกกรอบ "กรอบ"

คนส่วนใหญ่เริ่มนึกถึงภาพพื้นหลังและภาพเส้นขอบเมื่อเรียนรู้เกี่ยวกับ Worklet Paint กรณีการใช้งานที่เข้าใจน้อยลงอย่างหนึ่งสำหรับ Paint Worklet คือ mask-image เพื่อทำให้องค์ประกอบ DOM มีรูปร่างที่กำหนดเอง ดังตัวอย่างต่อไปนี้ diamond

องค์ประกอบ DOM รูปเพชร
องค์ประกอบ DOM รูปเพชร

mask-image ใช้รูปภาพที่มีขนาดเท่ากับองค์ประกอบ พื้นที่ที่รูปภาพมาสก์โปร่งใส องค์ประกอบจะโปร่งใส พื้นที่ที่รูปภาพมาสก์ ทึบแสง องค์ประกอบจะทึบแสง

พร้อมใช้งานแล้วใน Chrome

Worklet สีอยู่ใน Chrome Canary มาระยะหนึ่งแล้ว ด้วย Chrome 65 จะมีการเปิดใช้โดยค่าเริ่มต้น เอาล่ะ เรามาลองสิ่งที่เป็นไปได้ใหม่ๆ ที่ โมเดลระบายสีจะปรากฏขึ้น และแสดงให้เราเห็นว่า คุณทำอะไรบ้าง! หากต้องการแรงบันดาลใจเพิ่มเติม โปรดดูคอลเล็กชันของ Vincent De Oliveira