前言
该代码是基于 libgif
的基础上进行修改而成的。
简单说明 libgif
的实现
libgif
的实现主要是创建一个 canvas
的 dom
元素,然后开启计时器,不断的往 canvas
中绘画处理好的每一帧的图片。
而我们根据该插件拿到的则是一个自带计时器,可以一直在绘画的 canvas
。
问题
一开始我不了解该插件,从网上搜来如何在 canvas
中绘画gif,而网上的各种使用方法,大多都是借助 libgif
,并且在后面会调用 play
方法,而这显然存在一定的内存泄漏的问题,因为这计时器是一直开着的,当然也可以通过它里面的 pause
方法来控制暂停,但是这样的使用方式对于我来说心智负担就有点重了,因为我原本只是需要获得一张gif的多帧图片而已。
而且在 worker
中不支持 dom
的方法,并且可以采用了 OffscreenCanvas
代替 canvas
。
最终实现
基于 libgif
的处理,判断了是否在 worker
下,将返回的数据进行一些处理,直接将每一帧数据都保存为一个全新的 canvas
最后得到一个可以直接用于绘画的数组。
最终实现的代码也参考了这篇文章
在html和js文件中简单使用
html文件
<!DOCTYPE html>
<html lang='zh-cn'>
<head>
<meta charset='UTF-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Js</title>
</head>
<body>
<canvas id="canvas" width="1000" height="450"></canvas>
<script>
var worker = null, canvasBitmap;
function init() {
canvasBitmap = document.getElementById('canvas');
var offscreen = canvasBitmap.transferControlToOffscreen();
worker = new Worker('drawgif.js');
worker.postMessage({ msg: 'init', canvas: offscreen }, [offscreen]);
}
init()
</script>
</body>
</html>
drawgif.js
loadGifToCanvas
- 参数:
第一个参数传入 url
,第二个参数传入 是否处于 worker 环境下
。
- 返回值:(具体可以看下方的ts类型介绍)
返回一个promise
的对象数组,该对象由一个 delay
(图片应该持续的帧数) 和 img
(可直接用于canvas绘画的图片资源) 组成。
// 将一个或多个脚本同步导入到worker的作用域中
importScripts('./worker-libgif.js')
var offscreen,ctx;
onmessage = function (e) {
var data = e.data;
if(e.data.msg == 'init'){
offscreen = data.canvas;
ctx = offscreen.getContext("2d");
draw();
}else if(e.data.msg == 'draw'){
draw();
}
}
async function draw() {
ctx.clearRect(0,0,offscreen.width,offscreen.height);
const imgList = await loadGifToCanvas('../img/zombies_1_move.gif', true);
const imgList2 = await loadGifToCanvas('../img/sun.gif', true);
const drawimg1 = drawGif(imgList);
const drawimg2 = drawGif(imgList2);
(function go() {
ctx.clearRect(0,0,offscreen.width,offscreen.height);
drawimg1()
drawimg2()
requestAnimationFrame(go)
})()
}
function drawGif(list) {
let index = 0;
const len = list.length * 4;
return () => {
ctx.drawImage(list[Math.floor(index / 4)].img, 100, 200);
index++;
if (index >= len) {
index = 0;
}
}
}
在 vue 等项目中使用
安装依赖
npm i lhh-utils
ts类型
function loadGifToCanvas(url: string, isWorker: boolean): Promise<SourceImgObj[]>
type SourceImgObj = {
delay: number
img: OffscreenCanvas | CanvasImageSource
}
将gif图片转为可供canvas绘画的数组(支持worker)
// vue文件
<script setup lang='ts'>
import { onMounted, ref } from 'vue';
// 引入对应的 worker 文件
import Worker from "@/workers/test.ts?worker"
const workerRef = ref<Worker>()
const initWorker = () => {
const canvasBitmap = document.getElementById('canvas') as HTMLCanvasElement;
const offscreen = canvasBitmap.transferControlToOffscreen();
workerRef.value = new Worker()
// 该vue文件(主线程)往 worker (另外的线程) 传递数据
workerRef.value.postMessage({ init: true, canvas: offscreen }, [offscreen]);
// worker(另外的线程) 往 该vue文件(主线程)传递数据
workerRef.value.onmessage = e => {
console.log(e.data)
}
}
onMounted(() => {
initWorker()
})
</script>
<template>
<div class='worker'>
<canvas id="canvas" width="1000" height="450" :style="{border: '1px solid #fff'}"></canvas>
</div>
</template>
// workers/test.ts (worker的文件)
import { loadGifToCanvas, SourceImgObj } from "lhh-utils";
const state = {
ctx: null,
offscreen: null,
}
addEventListener('message', e => {
const { data } = e;
// 这里可以接收到传递进来的消息
console.log('data: ', data);
state.offscreen = data.canvas;
// 获取到canvas的上下文
state.ctx = state.offscreen.getContext('2d');
getGifImgList()
})
function getGifImgList() {
const imgList = await loadGifToCanvas('../img/zombies_1_move.gif', true);
console.log(imgList)
// 根据这个数组绘画就行了
}
export default {}
根据 imgList
数组绘画,具体使用方式跟上方的 drawgif.js
文件同理