Chrome hỗ trợ createImageBitmap() trong Chrome 50

Việc giải mã hình ảnh để sử dụng với canvas là khá phổ biến, cho dù đó là để cho phép người dùng tuỳ chỉnh hình đại diện, cắt hình ảnh hay chỉ phóng to một bức ảnh. Vấn đề với việc giải mã hình ảnh là quá trình này có thể tốn nhiều CPU, đôi khi có thể gây ra hiện tượng giật hoặc dạng ô. Kể từ Chrome 50 (và trong Firefox 42 trở lên), bạn hiện có một tuỳ chọn khác: createImageBitmap(). API này cho phép bạn giải mã hình ảnh ở chế độ nền và truy cập vào một ImageBitmap gốc mới. Bạn có thể vẽ ImageBitmap gốc này vào canvas theo cách tương tự như khi vẽ một phần tử <img>, một canvas khác hoặc một video.

Vẽ các blob bằng createImageBitmap()

Giả sử bạn tải hình ảnh blob xuống bằng fetch() (hoặc XHR) và muốn vẽ hình ảnh đó vào canvas. Nếu không có createImageBitmap(), bạn sẽ phải tạo một phần tử hình ảnh và một URL Blob để chuyển hình ảnh sang định dạng mà bạn có thể sử dụng. Với tính năng này, bạn có thể vẽ trực tiếp hơn nhiều:

fetch(url)
    .then(response => response.blob())
    .then(blob => createImageBitmap(blob))
    .then(imageBitmap => ctx.drawImage(imageBitmap, 0, 0));

Phương pháp này cũng sẽ hoạt động với hình ảnh được lưu trữ dưới dạng blob trong IndexedDB, giúp blob trở thành một định dạng trung gian thuận tiện. Chrome 50 cũng hỗ trợ phương thức .toBlob() trên các phần tử canvas. Điều này có nghĩa là bạn có thể tạo các blob từ các phần tử canvas.

Sử dụng createImageBitmap() trong worker web

Một trong những tính năng thú vị nhất của createImageBitmap() là tính năng này cũng có trong worker, nghĩa là giờ đây bạn có thể giải mã hình ảnh ở bất cứ đâu bạn muốn. Nếu có nhiều hình ảnh cần giải mã mà bạn cho là không cần thiết, bạn sẽ gửi URL của các hình ảnh đó đến một Worker trên web. Worker này sẽ tải xuống và giải mã các hình ảnh đó khi có thời gian. Sau đó, luồng này sẽ chuyển các đối tượng đó trở lại luồng chính để vẽ vào canvas.

Luồng dữ liệu với createImageBitmap và worker web.

Mã để thực hiện việc này có thể có dạng như sau:

// In the worker.
fetch(imageURL)
    .then(response => response.blob())
    .then(blob => createImageBitmap(blob))
    .then(imageBitmap => {
    // Transfer the imageBitmap back to main thread.
    self.postMessage({ imageBitmap }, [imageBitmap]);
    }, err => {
    self.postMessage({ err });
    });

// In the main thread.
worker.onmessage = (evt) => {
    if (evt.data.err)
    throw new Error(evt.data.err);

    canvasContext.drawImage(evt.data.imageBitmap, 0, 0);
}

Hôm nay, nếu bạn gọi createImageBitmap() trên luồng chính, thì đó chính là nơi quá trình giải mã sẽ diễn ra. Tuy nhiên, kế hoạch là Chrome sẽ tự động giải mã trong một luồng khác, giúp giảm tải cho luồng chính. Tuy nhiên, trong thời gian chờ đợi, bạn nên lưu ý việc giải mã trên luồng chính, vì đây là công việc chuyên sâu có thể chặn các tác vụ thiết yếu khác, chẳng hạn như JavaScript, tính toán kiểu, bố cục, vẽ hoặc kết hợp.

Thư viện trợ giúp

Để đơn giản hoá một chút, tôi đã tạo một thư viện trợ giúp để xử lý việc giải mã trên một worker, đồng thời gửi lại hình ảnh đã giải mã về luồng chính và vẽ hình ảnh đó vào canvas. Tất nhiên, bạn có thể tự do kỹ thuật đảo ngược và áp dụng mô hình này cho các ứng dụng của riêng mình. Lợi ích chính là có nhiều quyền kiểm soát hơn, nhưng điều đó (như thường lệ) đi kèm với nhiều mã hơn, nhiều lỗi gỡ lỗi hơn và nhiều trường hợp cạnh hơn cần được xem xét so với việc sử dụng phần tử <img>.

Nếu bạn cần có nhiều quyền kiểm soát hơn đối với việc giải mã hình ảnh, createImageBitmap() sẽ là người bạn đồng hành mới tốt nhất của bạn. Hãy dùng thử tính năng này trong Chrome 50 và cho chúng tôi biết cảm nhận của bạn!