Tạo ảnh Gif Từ Sticker Facebook - Viblo
Có thể bạn quan tâm
Mặc dù app Facebook và Messenger tệ thật, nhưng mà nó lại có mấy cái sticker cute cực í 🤪. Nhưng bạn thử tải về mà xem, không có cách nào download về dưới dạng file gif cả 🤔. Dù sao thì nó cũng hiện ra rồi, kiểu gì chả có cách tải về. Thử inspect element xem, chúng ta sẽ thấy nó hiện hình là 1 cái spritesheet được set thành background-image và dùng background-position để thay đổi khung hình.
Đây là cái spritesheet nhé.
Vậy giờ chúng ta sẽ làm như này:
- Lấy các khung hình từ spritesheet
- Ghép lại thành file gif
Tách các khung hình từ spritesheet
Nhìn cái spritesheet trên kia bạn có thể thấy nó có 8 khung hình, mỗi cái kích thước 288px * 288px. Bạn có thể tự đem cắt ra bằng tay bằng app nào đó. Hoặc là chúng ta sẽ viết script để tự cắt ra. Chúng ta sẽ dùng Canvas API để render các khung hình từ spritesheet.
Giả sử chúng ta có spritesheet được load trong page như này
<img id="spritesheet" src="https://scontent.fhan5-7.fna.fbcdn.net/v/t39.1997-6/72568563_526222821444483_279572336263299072_n.png?_nc_cat=100&_nc_sid=0572db&_nc_oc=AQlkAKjDakbfs1blUQC66vLLLnC5bCz1Eh6KJf_9JCgjaxqJ4kO1GhPF-CAkq3MZqGX5m_ar6Gu7tbuCFn06FXnA&_nc_ht=scontent.fhan5-7.fna&oh=fff5b86d6e73bd2c11d359b4f5b63b96&oe=5EE5DA80">Để cắt ra mỗi frame, chúng ta sẽ tạo 1 canvas với kích thước 288px * 288px và render phần tương ứng trên sprite sheet lên canvas đó. Ví dụ để cắt khung hình đâu tiên thì chúng ta làm như này
const spritesheet = document.getElementById('spritesheet'); const canvas = document.createElement('canvas'); canvas.width = 288; canvas.height = 288; const ctx = canvas.getContext('2d'); ctx.drawImage(spritesheet, 0, 0, 288, 288, 0, 0); document.body.appendChild(canvas);Bạn sẽ thấy frame đầu tiên như này
Param thứ 2 và thứ 3 của drawImage sẽ là vị trí của khung hình trên spritesheet. Document chi tiết ở đây.
Để cắt tất cả khung hình thì chúng ta làm 1 cái vòng lặp như này
const frames = []; while (y < spritesheet.height) { x = 0; while (x < spritesheet.width) { const canvas = document.createElement('canvas'); canvas.width = canvasWidth; canvas.height = canvasHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(spritesheet, x, y, 288, 288, 0, 0); const isEmpty = ctx.getImageData(0, 0, canvasWidth, canvasHeight).data.every(channel => channel === 0); if (!isEmpty) { frames.push(canvas); } x += originalWidth; } y += originalHeight; }Bạn sẽ thấy là chúng ta thừa ra 1 frame cuối cùng trống không có gì, vậy nên mình phải thêm 1 đoạn check xem frame chúng ta vừa cắt ra có dữ liệu không trước khi thêm vào array frames. Chỉ đơn giản là check xem tất cả các pixel của nó có data hay không thôi.
const isEmpty = ctx.getImageData(0, 0, canvasWidth, canvasHeight).data.every(channel => channel === 0);Ghép các khung hình thành ảnh GIF
Có các khung hình rồi thì giờ mình ghép lại thôi. Mình sẽ dùng package gif.js để tạo ảnh gif nhé. Tạo ảnh từ các frame thì đơn giản như này thôi.
const fps = 8; const gif = new GIF({ workers: 2, quality: 1, }); frames.forEach(frame => gif.addFrame(frame, { delay: 1000 / fps, })); gif.on('finished', (blob) => { const url = URL.createObjectURL(blob); const img = document.createElement('img'); img.setAttribute('src', url); document.body.appendChild(img); }); gif.render();Chỉ cần add các frame và khoảng delay giữa các frame sau đó render. Để cho dễ tính thì chúng ta dùng khái niệm frame rate, thường thì các sticker của Facebook mình thấy có frame rate từ 8-12 fps. Kết quả trả về sẽ là raw data nên chúng ta dùng URL.createObjectURL để tạo 1 URL tạm thời. Kết quả của chúng ta như này.
Cũng ổn nhỉ, trừ cái background đen xì ra 🤔. Đó là vì ảnh của chúng ta có phần transparent nên render thành gif bị như vậy. Nếu thêm options transparent cho gif.js như này
const gif = new GIF({ workers: 2, quality: 1, transparent: 'rgba(0, 0, 0, 0)', });Thì chúng ta được kết quả như này.
Có background trong suốt rồi nhưng mà mấy chỗ đường viền không ổn lắm nhỉ. Cái này là do hạn chế của định dạng GIF. Bình thường thì với ảnh trong suốt như PNG chẳng hạn, đoạn chuyển từ chỗ có hình sang chỗ trong suốt sẽ là rất nhiều pixel với độ trong suốt giảm giần như thế này để cho đường viền của ảnh được mượt mà.
Tuy nhiên với định dạng GIF thì mỗi px chỉ có thể có màu hoặc trong suốt hoàn toàn, không có kiểu trong suốt 1 nửa như PNG. Nên chỗ đường viền sẽ trông như kiểu răng cưa chất lượng thấp. Vậy nên ảnh gif mà có background trong suốt thường có 1 đoạn nhỏ viền có màu trắng (hoặc màu gì đó trùng với màu nền mà mọi người định đặt cái gif lên) để làm cho đường viền trông mượt hơn. Để cho đơn giản thì mình sẽ cho cả cái background màu trắng luôn.
ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, canvasWidth, canvasHeight);Nhớ là phải tô màu trước khi drawImage nếu không nó sẽ đè lên ảnh. Kết quả của chúng ta như này.
Đây là toàn bộ code nếu bạn muốn nghịch nhé.
Link codepen nếu cái embed kia không load được 😔 https://codepen.io/thphuong/pen/qBOyRaz
Bonus
Về cách làm của Facebook, tại sao lại dùng spritesheet mà không dùng luôn ảnh GIF nhỉ. Làm như này cũng có vài lợi ích.
- Ảnh đẹp hơn, ảnh GIF chỉ có 256 màu thay vì 16 triệu màu như PNG.
- Không gặp vấn đề về transparent như mình vừa nói ở trên nữa.
- Khỏi bị copy (may be 🤔).
Nhưng chắc hẳn cũng có vài cái không có lợi rồi
- Không chỉnh được kích thước. Vì dùng background-image và background-position nên kích thước sticker là cố định, muốn thay đổi phải đổi spritesheet.
- Chạy tốn RAM với CPU 🙄.
Từ khóa » Hình ảnh Gif Cute
-
92 Gif Cute ý Tưởng | Hình Gif, Dễ Thương, Cánh Cụt Vua - Pinterest
-
210 Gif Cute ý Tưởng Trong 2022 | đang Yêu, Dễ Thương, Mèo
-
80+ Ảnh Gif, Ảnh Động Chân Thực, Sống Động Và Độc Nhất Vô Nhị
-
Ảnh Gif: Buồn, Cute, Ngộ Nghĩnh, Đẹp Nhất Quả Đất
-
50+ Ảnh GIF Cực đẹp
-
Ảnh động Cute đẹp - Thủ Thuật Phần Mềm
-
Hình ảnh Gif đẹp, Sống động, độc đáo Dành Cho Bạn - Haycafe.VN
-
150+ ảnh GIF Cute, Hình Nền động Anime Siêu Xinh Cho điện Thoại
-
Tải Ảnh Động Dễ Thương, Đáng Yêu, Vui Nhộn Nhất 2022
-
Top 19 Hình ảnh Gif đẹp Mới Nhất 2022 – Blog Thú Vị
-
Ảnh động đẹp, ảnh GIF đẹp - Thủ Thuật
-
Tổng Hợp Ảnh Gif Giá Rẻ, Bán Chạy Tháng 7/2022 - BeeCost
-
Top 19 Gif Cute Mới Nhất 2021 - EXP.GG