Thay đổi kích thước lớp php

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

Thay đổi kích thước lớp php

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è

Thay đổi kích thước lớp php

Hừm, nhìn không ổn nhỉ lắm

Thay đổi kích thước lớp php
. Hình khá thiếu nét so với ảnh gốc. Đó là do API này vốn không được thiết kế để thay đổi kích thước ảnh với các thuật toán điều chỉnh như những công cụ chỉnh sửa ảnh hoặc thư viện phía máy chủ vì lý do thực hiện. Do đó, khi thay đổi kích thước với kích thước khác biệt quá nhiều sẽ khiến cho hình ảnh bị lược như vậy. Để hạn chế, chúng ta sẽ thay đổi kích thước từ mỗi lần 1 tí để tránh kích thước mỗi lần thay đổi kích thước cách nhau quá nhiều. Thay đổi kích thước nhiều lần cũng có thể làm giảm chất lượng đi, nhưng sẽ tránh được trạng thái răng cưa như trên. Mình sửa code 1 tí như này, mỗi lần sẽ resize còn 1 nửa, tăng dần theo kích thước mình muố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;

This is results

Thay đổi kích thước lớp php

Khá hơn nhiều nhỉ

Thay đổi kích thước lớp php
.

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

Thay đổi kích thước lớp php

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

Thay đổi kích thước lớp php
.

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

Thay đổi kích thước lớp php
). createImageBitmap cho phép tạo
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;
4 để lưu dữ liệu ảnh từ ảnh gốc (image or canvas). Trong quá trình tạo còn hỗ trợ tính năng thay đổi kích thước nữa. Và đặc biệt là nó trả về một
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;
5 nên chúng ta không phải lo nó sẽ làm trình duyệt bị lag khi đang kết xuất. Resize 1 cái ảnh thì vẫn chưa thấy gì khác biệt, nhưng bạn sẽ thấy sự khác biệt ở đoạn sau khi chúng ta resize một lúc nhiều ảnh. Cũng chắc nhờ vậy nên nó cho phép sử dụng các thuật toán thay đổi kích thước hơn.

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

Thay đổi kích thước lớp php

Đâ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

Thay đổi kích thước lớp php

Đâ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

Thay đổi kích thước lớp php

Sẽ chỉ được mỗi khung đấu đầu tiên

Thay đổi kích thước lớp php

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

Thay đổi kích thước lớp php
.

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

Thay đổi kích thước lớp php
, mã tách tệp gif thành khung như thế này nhé.

________số 8

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

Thay đổi kích thước lớp php

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

Thay đổi kích thước lớp php

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

Thay đổi kích thước lớp php

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

Thay đổi kích thước lớp php
.

Thay đổi kích thước lớp php

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