API การแสดงผล CSS

ความสามารถใหม่ๆ ใน 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