Chrome hỗ trợ createImageBitmap() trong Chrome 50

Việc giải mã hình ảnh để sử dụng bằng canvas 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 ảnh. Vấn đề với việc giải mã hình ảnh là hình ảnh có thể cần nhiều CPU, và đôi khi có thể có nghĩa là hiện tượng giật hoặc lướt web. Kể từ Chrome 50 (và trong Firefox 42 trở lên), bạn hiện có một tuỳ chọn khác: createImageBitmap(). Nhờ đó, bạn có thể giải mã hình ảnh trong nền và truy cập vào một nguyên gốc ImageBitmap mới mà bạn có thể vẽ vào canvas tương tự như cách bạn thực hiện với phần tử <img>, một canvas khác hoặc video.

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

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

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ẽ áp dụng cho các hình ảnh được lưu trữ dưới dạng blob trong IndexedDB, giúp các blob có định dạng trung gian tiện lợi. Như vậy, Chrome 50 cũng hỗ trợ phương thức .toBlob() đối với các thành phần canvas, nghĩa là bạn có thể tạo các blob từ các phần tử canvas.

Sử dụng createImageBitmap() trong trình thực thi web

Một trong những tính năng thú vị nhất của createImageBitmap() là API này cũng có sẵn 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 bạn có nhiều hình ảnh cần giải mã nhưng bạn cho là không cần thiết, bạn sẽ gửi URL của chúng đến một Web Worker để nhân viên web tải xuống và giải mã chúng khi cho phép. Sau đó, thao tác này sẽ chuyển các hình ảnh này trở lại chuỗi chính để vẽ vào canvas.

Luồng dữ liệu với createImageBitmap và trình thực thi web.

Mã để thực hiện việc này có thể giố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 bạn sẽ hoàn tất quá trình giải mã. Tuy nhiên, các kế hoạch là để Chrome tự động giải mã trong một luồng khác, giúp giảm tải khối lượng công việc của luồng chính. Tuy nhiên, trong thời gian chờ đợi, bạn nên chú ý giải mã trên luồng chính, vì thao tác này cần nhiều công sức và 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

Để làm cho cuộc sống đơn giản hơn một chút, tôi đã tạo thư viện trợ giúp xử lý việc giải mã trên một worker và gửi lại hình ảnh đã giải mã đến luồng chính rồi vẽ hình ảnh đó vào canvas. Tất nhiên, bạn cứ thoải mái thiết kế đảo ngược và áp dụng mô hình này cho các ứng dụng của mình. Lợi ích chính là 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 trường hợp gỡ lỗi hơn và nhiều trường hợp hiếm gặp hơn cần xem xét so với việc sử dụng phần tử <img>.

Nếu bạn cần kiểm soát nhiều hơn với tính năng giải mã hình ảnh, createImageBitmap() 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 trải nghiệm của bạn!