Hôm nay mình sẽ làm demo Tối ưu hóa dung lượng ảnh trước khi gửi lên server của Laravel. Công việc này chắc chắn các bạn làm back-end vẫn thường xuyên phải xử lý và khá đau đầu phải không nào?
Không phải là một điều xa lạ mỗi khi bạn gặp trường hợp với 2 tấm ảnh cùng kích thước 720x1280 (HD) nhưng 1 cái dung lượng 1Mb còn cái kia up tới 3Mb mặc dù việc hiển thị là giống nhau. Đương nhiên thời gian tải cái số 2 sẽ lâu hơn và băng thông sẽ nhiều hơn
Vì vậy chỉ cần đặt const img = new Image(); img.onload = function() { const canvas const canvas = newCanvas(width, height); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, 301, 450); document.body.appendChild(canvas); }; img.src = url; 9 và function resize(img, width, height, quality = null) { const canvas = newCanvas(width, height); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height); return canvas; } const img = new Image(); img.onload = function() { let canvas = img; while (canvas.width * .5 > 301) { canvas = resize(canvas, canvas.width / 2, canvas.height / 2); } canvas = resize(canvas, 301, 450); }; img.src = url; 0 thành kích thước mà chúng ta muốn thay đổi kích thước là sẽ thay đổi kích thước ảnh. Please doing try watching nhé. Ví dụ mình có cấu hình này
Kích thước ảnh gốc là 1207 x 1800. Giờ mình sẽ resize thành 301 x 450 nhé. Code of them ta as this
const img = new Image(); img.onload = function() { const canvas const canvas = newCanvas(width, height); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, 301, 450); document.body.appendChild(canvas); }; img.src = url;Đây là kết quả nè
Hừm, nhìn không ổn nhỉ lắm
This is results
Khá hơn nhiều nhỉ
Ngoài ra còn có 1 tính năng thử nghiệm hiện có trên Chrome có thể tùy chỉnh chất lượng thay đổi kích thước là tùy chọn imageSmoothingQuality của canvas context 2D. Mình đang dùng thử thì được kết quả như ảnh này
Thôi không khác cái trên là nhỉ mấy, mình thấy cái trên còn đẹp hơn ấy
Use function resize(img, width, height, quality = null) { const canvas = newCanvas(width, height); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height); return canvas; } const img = new Image(); img.onload = function() { let canvas = img; while (canvas.width * .5 > 301) { canvas = resize(canvas, canvas.width / 2, canvas.height / 2); } canvas = resize(canvas, 301, 450); }; img.src = url; 3
Vẫn là Canvas API nhưng lần này chúng ta có một phương thức khác, vẫn chỉ là hỗ trợ trên chrome (Firefox có hỗ trợ nhưng lại là crop thay vì thay đổi kích thước
We time we used to try view, code as this
const img = new Image(); img.onload = function() { createImageBitmap(img, { resizeWidth: 301, resizeHeight: 450, resizeQuality: 'high', }).then((data) => { const canvas = newCanvas(width, height); canvas.getContext('2d').drawImage(data, 0, 0); document.body.appendChild(canvas); }; }); } img.src = url;This is results
Đây là ảnh mình thử resize bằng tool chỉnh sửa ảnh để thấy chất lượng cũng không khác gì lắm luôn
Đây là mã của tất cả các cách trên để bạn so sánh. Bạn mở ví dụ đầy đủ lên so sánh cho dễ nhé. Kết quả có thể khác nhau tùy trình duyệt bạn sử dụng
Liên kết codepen. https. // codepen. io/thphuong/pen/qBOeMRb
Thay đổi kích thước GIF
If you try way on with GIF image will not found. Ví dụ mình thay đổi kích thước hình này
Sẽ chỉ được mỗi khung đấu đầu tiên
Có thể bạn đã biết rồi ảnh gif được tạo thành từ các khung hình. hôm trước mình cũng mới viết bài tạo ảnh gif ở đây. Vì vậy, nếu chúng ta có thể tách ảnh gif ra thành từng khung, sau đó thay đổi kích thước từng khung, sao đó ghép lại như cũ là ảnh gif đã được thay đổi kích thước rồi
Mình sẽ sử dụng gif-frames để tách gif thành cách frame và gif. js để ghép các frame lại thành gif
Không giải thích thư viện sử dụng như bất kỳ nữa để bạn khỏi mất thời gian
Còn cái này để ghép các khung lại thành gif
function renderGif(frames) { const gif = new GIF({ workers: 2, quality: 1, transparent: 'rgba(0, 0, 0, 0)', }); return new Promise((resolve, reject) => { gif.on("finished", resolve); try { gif.render(); } catch (e) { reject(e); } }); }Hàm resize thì như phần trước nhé, ghép lại và chạy thì mình được kết quả như thế này
Hmm, lại bị cái viền đen rồi. Như bạn đã thấy thì cái ảnh gif gốc có nền trong suốt. Với png thì để có nền trong suốt chúng ta sẽ chọn những pixel trong suốt có giá trị kênh alpha = 0 (a trong rgba). Ở chỗ khung để cho đoạn chuyển từ có màu sang trong suốt sẽ mượt mà, không bị răng cưa thì sẽ có rất nhiều px với độ trong suốt giảm dần, kiểu như thế này
Như bạn thấy là lúc thay đổi kích thước canvas, vì nó render ảnh định dạng png nên thuận tay render giúp mình những pixel ghép thành trong suốt thay vì giữ nguyên như ảnh gốc. Ảnh gif thì lại không có kênh alpha như png (chỉ có RGB), mỗi pixel chỉ có thể có màu hoặc không có màu, không có kiểu Nhìn mờ nhìn xuyên qua như png. Không gian gần kề cũng giống như png thôi
Khi chúng ta ghép các khung hình lại thì do không có kênh alpha nên những pixel trong suốt có màu là function resize(img, width, height, quality = null) { const canvas = newCanvas(width, height); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height); return canvas; } const img = new Image(); img.onload = function() { let canvas = img; while (canvas.width * .5 > 301) { canvas = resize(canvas, canvas.width / 2, canvas.height / 2); } canvas = resize(canvas, 301, 450); }; img.src = url; 6 sẽ được chuyển thành function resize(img, width, height, quality = null) { const canvas = newCanvas(width, height); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height); return canvas; } const img = new Image(); img.onload = function() { let canvas = img; while (canvas.width * .5 > 301) { canvas = resize(canvas, canvas.width / 2, canvas.height / 2); } canvas = resize(canvas, 301, 450); }; img.src = url; 7 (màu đen). Vì bạn thấy đoạn mã ở trên chúng ta cần có tùy chọn function resize(img, width, height, quality = null) { const canvas = newCanvas(width, height); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height); return canvas; } const img = new Image(); img.onload = function() { let canvas = img; while (canvas.width * .5 > 301) { canvas = resize(canvas, canvas.width / 2, canvas.height / 2); } canvas = resize(canvas, 301, 450); }; img.src = url; 8 để biết pixel nào nên được chuyển thành trong suốt. Ngoài ra thì những điểm ảnh gần sát viền có màu là function resize(img, width, height, quality = null) { const canvas = newCanvas(width, height); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height); return canvas; } const img = new Image(); img.onload = function() { let canvas = img; while (canvas.width * .5 > 301) { canvas = resize(canvas, canvas.width / 2, canvas.height / 2); } canvas = resize(canvas, 301, 450); }; img.src = url; 7 và const img = new Image(); img.onload = function() { createImageBitmap(img, { resizeWidth: 301, resizeHeight: 450, resizeQuality: 'high', }).then((data) => { const canvas = newCanvas(width, height); canvas.getContext('2d').drawImage(data, 0, 0); document.body.appendChild(canvas); }; }); } img.src = url; 0 gần bằng 0 nên vẫn được chuyển thành màu đen. Đó là lí do chúng ta có ít viền đen như hình trên kia
Vì vậy giờ chúng ta có 2 lựa chọn. Tô màu nền để không có mảng trong suốt nữa như mình làm bài trước. p. Hoặc là chúng ta sửa bằng cách sửa lại những pixel có kênh alpha thành không trong suốt đúng theo yêu cầu của gif
Để sửa dữ liệu của ảnh vẽ trong canvas, chúng ta sử dụng const img = new Image(); img.onload = function() { createImageBitmap(img, { resizeWidth: 301, resizeHeight: 450, resizeQuality: 'high', }).then((data) => { const canvas = newCanvas(width, height); canvas.getContext('2d').drawImage(data, 0, 0); document.body.appendChild(canvas); }; }); } img.src = url; 1. Dữ liệu hình ảnh sẽ là 1 mảng nguyên số 8 bit (0-255). Mỗi 4 số sẽ là 1 pixel, có thể hiển thị 4 giá trị trong const img = new Image(); img.onload = function() { createImageBitmap(img, { resizeWidth: 301, resizeHeight: 450, resizeQuality: 'high', }).then((data) => { const canvas = newCanvas(width, height); canvas.getContext('2d').drawImage(data, 0, 0); document.body.appendChild(canvas); }; }); } img.src = url; 2. Thì giờ mình loop trong stack mảng pixel đấy và thấy cái nào có màu mà value a nhỏ hơn 255 thì set lại thành 255. Ngoài những pixel mà có kênh alpha quá nhỏ (< 127) thì mình cũng set = 0 luôn để đỡ bị răng nanh. Code tí nữa các bạn xem hàm const img = new Image(); img.onload = function() { createImageBitmap(img, { resizeWidth: 301, resizeHeight: 450, resizeQuality: 'high', }).then((data) => { const canvas = newCanvas(width, height); canvas.getContext('2d').drawImage(data, 0, 0); document.body.appendChild(canvas); }; }); } img.src = url; 3 trong demo nhé
Quả quả ngon hơn nhiều rồi. So resize bằng thư viện phẫu thuật như Imagemagick thì mình thấy cũng không kém đâu
Code và demo ở đây bạn nhé
Codesandbox. https. //codesandbox. io/s/gif-thay đổi kích thước-zn6bnl
Thử nghiệm. https. // zn6bn. csb. ứng dụng
Nếu bạn để ý thì mình đã sử dụng function resize(img, width, height, quality = null) { const canvas = newCanvas(width, height); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height); return canvas; } const img = new Image(); img.onload = function() { let canvas = img; while (canvas.width * .5 > 301) { canvas = resize(canvas, canvas.width / 2, canvas.height / 2); } canvas = resize(canvas, 301, 450); }; img.src = url; 3 khi có thể. Khi chạy trên trình duyệt không hỗ trợ function resize(img, width, height, quality = null) { const canvas = newCanvas(width, height); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height); return canvas; } const img = new Image(); img.onload = function() { let canvas = img; while (canvas.width * .5 > 301) { canvas = resize(canvas, canvas.width / 2, canvas.height / 2); } canvas = resize(canvas, 301, 450); }; img.src = url; 3 như Firefox thì khi nhấn nút thay đổi kích thước sẽ có một khoảng thời gian trình duyệt bị đơ do thay đổi kích thước quá thời gian