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 สีที่มีชื่อว่า myPainter เราต้องโหลดไฟล์เวิร์กเล็ตการแสดงผล CSS โดยใช้ 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 สีสามารถเข้าถึงพร็อพเพอร์ตี้ 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);

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

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

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

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

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

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

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

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

สี Polyfill ของสี CSS

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

Use Case

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

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

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

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

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

องค์ประกอบ DOM ที่มีรูปร่างเหมือนข้าวหลามตัด
องค์ประกอบ DOM ที่มีรูปร่างเหมือนข้าวหลามตัด

mask-image ถ่ายภาพที่มีขนาดเดียวกับองค์ประกอบ บริเวณที่รูปภาพมาสก์โปร่งใส องค์ประกอบจะโปร่งใส บริเวณที่รูปภาพมาสก์ทึบ โดยองค์ประกอบจะทึบแสง

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

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