API CSS Paint

Khả năng mới trong Chrome 65

CSS Paint API (còn gọi là “CSS Custom Paint” hoặc “worklet Paint của Houdini”) được bật theo mặc định kể từ Chrome 65. Định nghĩa Bạn có thể làm gì với công cụ này? Cách hoạt động của tính năng này Chà, đọc tiếp, bạn sẽ...

CSS Paint API cho phép bạn tạo hình ảnh theo phương thức lập trình bất cứ khi nào thuộc tính CSS yêu cầu hình ảnh. Các thuộc tính như background-image hoặc border-image thường dùng với url() để tải tệp hình ảnh hoặc với các hàm tích hợp sẵn CSS như linear-gradient(). Thay vì sử dụng các hàm đó, giờ đây bạn có thể sử dụng paint(myPainter) để tham chiếu worklet Painter.

Viết worklet sơn

Để xác định một worklet sơn có tên là myPainter, chúng ta cần tải một tệp worklet vẽ CSS bằng cách sử dụng CSS.paintWorklet.addModule('my-paint-worklet.js'). Trong tệp đó, chúng ta có thể sử dụng hàm registerPaint để đăng ký một lớp worklet vẽ:

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

registerPaint('myPainter', MyPainter);

Bên trong lệnh gọi lại paint(), chúng ta có thể sử dụng ctx giống như cách chúng ta sử dụng CanvasRenderingContext2D như đã biết trong <canvas>. Nếu biết cách vẽ trong <canvas>, bạn có thể vẽ trong một worklet sơn! geometry cho chúng tôi biết chiều rộng và chiều cao của canvas mà chúng tôi có thể tuỳ ý sử dụng. properties Tôi sẽ giải thích sau trong bài viết này.

Làm ví dụ giới thiệu, hãy viết một tệp worklet sơn bàn cờ và sử dụng nó làm hình nền của <textarea>. (Tôi đang sử dụng một vùng văn bản vì vùng đó có thể thay đổi kích thước theo mặc định.):

<!-- 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);

Nếu trước đây bạn đã sử dụng <canvas>, thì mã này trông quen thuộc. Xem bản minh hoạ trực tiếp tại đây.

Vùng văn bản có hoa văn bàn cờ làm hình nền
Textarea có hoa văn bàn cờ làm hình nền.

Sự khác biệt so với việc sử dụng hình nền phổ biến ở đây là mẫu sẽ được vẽ lại theo yêu cầu, bất cứ khi nào người dùng đổi kích thước vùng văn bản. Điều này có nghĩa là hình nền luôn có kích thước chính xác cần thiết, bao gồm cả phần bù cho màn hình có mật độ hiển thị cao.

Thật thú vị nhưng cũng khá tĩnh lặng. Chúng ta có muốn viết một worklet mới mỗi khi muốn có cùng một mẫu nhưng với các hình vuông có kích thước khác nhau không? Câu trả lời là không!

Tham số hoá Worklet

May mắn thay, worklet sơn có thể truy cập vào các thuộc tính CSS khác, đây là nơi tham số bổ sung properties phát huy tác dụng. Bằng cách cung cấp cho lớp này một thuộc tính inputProperties tĩnh, bạn có thể đăng ký thực hiện các thay đổi đối với bất kỳ thuộc tính CSS nào, bao gồm cả các thuộc tính tuỳ chỉnh. Các giá trị sẽ được cấp cho bạn thông qua thông số 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);

Giờ đây, chúng ta có thể sử dụng cùng một mã cho mọi loại bàn cờ. Nhưng thậm chí còn tốt hơn nữa, giờ đây, chúng ta có thể chuyển vào Công cụ cho nhà phát triển và điều chỉnh với các giá trị cho đến khi tìm thấy giao diện phù hợp.

Các trình duyệt không hỗ trợ vẽ worklet

Tại thời điểm viết bài này, chỉ Chrome mới triển khai worklet vẽ. Mặc dù có những tín hiệu tích cực từ tất cả các nhà cung cấp trình duyệt khác, nhưng không có nhiều tiến bộ. Để cập nhật thông tin, hãy thường xuyên kiểm tra trang Is Houdini Sẵn sàng chưa?. Trong thời gian chờ đợi, hãy nhớ sử dụng tính năng nâng cao tăng dần để tiếp tục chạy mã ngay cả khi không hỗ trợ worklet vẽ. Để đảm bảo mọi thứ hoạt động như mong đợi, bạn phải điều chỉnh mã ở hai nơi: CSS và JS.

Bạn có thể phát hiện tính năng hỗ trợ vẽ worklet trong JS bằng cách kiểm tra đối tượng CSS: js if ('paintWorklet' in CSS) { CSS.paintWorklet.addModule('mystuff.js'); } Đối với phía CSS, bạn có 2 lựa chọn. Bạn có thể dùng @supports:

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

Một mẹo nhỏ gọn hơn là sử dụng thực tế là CSS vô hiệu hoá và sau đó bỏ qua toàn bộ phần khai báo thuộc tính nếu có một hàm không xác định trong đó. Nếu chỉ định một thuộc tính hai lần – lần đầu tiên không có worklet sơn, sau đó là với worklet sơn – bạn sẽ nhận được tính năng nâng cao tăng dần:

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

Trong các trình duyệt hỗ trợ worklet vẽ, nội dung khai báo thứ hai của background-image sẽ ghi đè nội dung khai báo đầu tiên. Trong các trình duyệt không hỗ trợ tính năng vẽ worklet, nội dung khai báo thứ hai là không hợp lệ và sẽ bị loại bỏ, khiến nội dung khai báo đầu tiên có hiệu lực.

Chất liệu polyfill của CSS

Trong nhiều mục đích sử dụng, bạn cũng có thể sử dụng CSS Paint Polyfill, một công cụ sẽ thêm CSS Custom Paint và Paint Worklet hỗ trợ cho các trình duyệt hiện đại.

Trường hợp sử dụng

Có nhiều trường hợp sử dụng công việc sơn, một số trường hợp trong số đó rõ ràng hơn các trường hợp khác. Một trong những điều dễ thấy hơn là sử dụng worklet sơn để giảm kích thước của DOM. Thông thường, các phần tử chỉ được thêm vào để tạo điểm nhấn cho trang trí bằng cách sử dụng CSS. Ví dụ: trong Material Design Lite, nút có hiệu ứng gợn sóng chứa 2 phần tử <span> bổ sung để triển khai chính hiệu ứng gợn sóng. Nếu bạn có nhiều nút, việc này có thể thêm tới khá nhiều phần tử DOM và có thể làm giảm hiệu suất trên thiết bị di động. Thay vào đó, nếu bạn triển khai hiệu ứng gợn sóng bằng cách sử dụng worklet sơn, thì bạn sẽ có thêm 0 phần tử và chỉ có một worklet vẽ. Ngoài ra, bạn có một thành phần dễ tuỳ chỉnh và xác định tham số hơn nhiều.

Một ưu điểm khác của việc sử dụng worklet vẽ là trong hầu hết các trường hợp, giải pháp sử dụng worklet vẽ có kích thước nhỏ về mặt byte. Tất nhiên có một sự đánh đổi: mã sơn của bạn sẽ chạy bất cứ khi nào kích thước của canvas hoặc bất kỳ tham số nào thay đổi. Vì vậy, nếu mã của bạn phức tạp và mất nhiều thời gian, thì có thể gây ra hiện tượng giật. Chrome đang nỗ lực di chuyển các worklet vẽ ra khỏi luồng chính để ngay cả các worklet vẽ chạy trong thời gian dài cũng không ảnh hưởng đến khả năng phản hồi của luồng chính.

Đối với tôi, triển vọng thú vị nhất là worklet sơn cho phép thực hiện polyfill hiệu quả các tính năng CSS mà một trình duyệt chưa có. Một ví dụ là sử dụng hiệu ứng chuyển màu conic bằng nhiều màu cho đến khi chúng truy cập vào Chrome như ban đầu. Một ví dụ khác: trong một cuộc họp CSS, chúng tôi đã quyết định rằng giờ đây bạn có thể có nhiều màu đường viền. Trong khi cuộc họp này vẫn diễn ra, đồng nghiệp của tôi Ian Kilpatrick đã viết một polyfill cho hành vi mới này của CSS bằng cách sử dụng worklet sơn.

Suy nghĩ sáng tạo

Hầu hết mọi người bắt đầu nghĩ đến hình nền và hình ảnh đường viền khi tìm hiểu về worklet vẽ. Một trường hợp sử dụng kém trực quan hơn đối với worklet sơn là mask-image để làm cho các phần tử DOM có hình dạng tuỳ ý. Ví dụ: kim cương:

Một phần tử DOM trong hình dạng một viên kim cương.
Một phần tử DOM có hình kim cương.

mask-image chụp một hình ảnh có kích thước của phần tử. Các khu vực có hình ảnh mặt nạ trong suốt và phần tử trong suốt. Các khu vực có hình ảnh mặt nạ mờ, phần tử mờ.

Hiện đã có trên Chrome

Worklet Paint đã có trong Chrome Canary một thời gian. Với Chrome 65, tính năng này được bật theo mặc định. Hãy tiếp tục và thử các khả năng mới mà công cụ vẽ tranh mở ra và cho chúng tôi thấy những gì bạn đã xây dựng! Để có thêm nguồn cảm hứng, hãy xem bộ sưu tập của Vincent De Oliveira.