Các khả năng mới trong Chrome 65
CSS Paint API (còn gọi là "CSS Custom Paint" hoặc "worklet vẽ của Houdini") được bật theo mặc định kể từ Chrome 65. Giải pháp này là gì? 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 như thế nào? Hãy đọc tiếp nhé…
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 đến worklet Painter.
Viết một worklet sơn
Để xác định một worklet vẽ có tên là myPainter
, chúng ta cần tải tệp worklet vẽ CSS bằ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 công cụ 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
mà chúng ta biết từ <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 ta biết chiều rộng và chiều cao của canvas mà chúng ta có thể sử dụng. properties
Tôi sẽ giải thích ở phần sau của bài viết này.
Làm ví dụ giới thiệu, hãy viết một 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 textarea vì theo mặc định, textarea có thể đổi kích thước):
<!-- 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 bạn đã từng sử dụng <canvas>
, thì mã này sẽ trông quen thuộc. Xem
bản minh hoạ
trực tiếp tại đây.
Điểm khác biệt so với việc sử dụng hình nền thông thường ở đâ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 textarea. Điều này có nghĩa là hình nền luôn có kích thước chính xác như cần thiết, bao gồm cả việc bù cho màn hình có mật độ điểm ảnh cao.
Điều đó khá thú vị, nhưng cũng khá tĩnh. 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, công cụ vẽ 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 một thuộc tính inputProperties
tĩnh, bạn có thể đăng ký nhậ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 cung cấp cho bạn thông qua tham 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 tất cả các loại bàn cờ. Nhưng tốt hơn nữa, giờ đây, chúng ta có thể chuyển sang DevTools và tìm hiểu 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ợ công cụ vẽ
Tại thời điểm viết bài, chỉ Chrome mới triển khai được công cụ paint worklet. 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ộ. Để nắm bắt thông tin mới nhất, hãy thường xuyên kiểm tra bài viết 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ã của mình ở hai vị trí: CSS và JS.
Bạn có thể phát hiện tính năng hỗ trợ cho công cụ vẽ 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ó hai lựa chọn. Bạn có thể sử 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 sẽ vô hiệu hoá và sau đó bỏ qua toàn bộ nội dung 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ó công việc vẽ, sau đó có công việc vẽ – bạn sẽ nhận được tính năng nâng cao dần:
textarea {
background-image: linear-gradient(0, red, blue);
background-image: paint(myGradient, red, blue);
}
Trong các trình duyệt có hỗ trợ vẽ worklet, 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 Painter 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.
CSS Paint Polyfill
Đối với nhiều trường hợp sử dụng, bạn cũng có thể sử dụng CSS Paint Polyfill. Tính năng này sẽ thêm tính năng hỗ trợ CSS Custom Paint và Paint Worklets vào 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 cho các công cụ vẽ, một số trường hợp rõ ràng hơn các trường hợp khác. Một trong những giải pháp rõ ràng 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ử được thêm vào chỉ để tạo điểm nhấn bằng CSS. Ví dụ: trong Material Design Lite, nút có hiệu ứng gợn sóng chứa thêm 2 phần tử <span>
để triển khai hiệu ứng gợn sóng. Nếu bạn có nhiều nút, thì điều này có thể làm tăng đáng kể số lượng 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 công cụ vẽ là – trong hầu hết các trường hợp – giải pháp sử dụng công cụ vẽ có kích thước nhỏ tính theo byte. Tất nhiên, có một sự đánh đổi: mã vẽ 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ì mã đó có thể gây ra hiện tượng giật. Chrome đang nỗ lực di chuyển các công việc vẽ ra khỏi luồng chính để ngay cả các công việc 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à paint worklet cho phép polyfill hiệu quả các tính năng CSS mà trình duyệt chưa có. Ví dụ: bạn có thể polyfill hiệu ứng chuyển màu hình nón cho đến khi hiệu ứng này xuất hiện trong Chrome. 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 đang diễn ra, đồng nghiệp của tôi Ian Kilpatrick đã viết một polyfill cho hành vi CSS mới này bằng cách sử dụng công cụ paint worklet.
Tư duy vượt ra khỏi "hộp"
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ề công cụ 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:
mask-image
chụp một hình ảnh có kích thước của phần tử. Các vùng mà hình ảnh mặt nạ trong suốt, phần tử sẽ trong suốt. Các khu vực có hình ảnh mặt nạ mờ, phần tử mờ.
Giờ đây có trong Chrome
Worklet Paint đã có trong Chrome Canary được 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 thử nghiệm các khả năng mới mà công cụ vẽ mở ra và cho chúng tôi biết bạn đã tạo ra gì! Để có thêm nguồn cảm hứng, hãy xem bộ sưu tập của Vincent De Oliveira.