Javascript Fabric.js画布上的动画GIF
我正在做一个项目,要求我在fabric.js画布上支持动画GIF 根据,我按照建议定期渲染,使用fabric.util.requestAnimFrame。使用此方法,视频渲染效果很好,但GIF似乎不会更新Javascript Fabric.js画布上的动画GIF,javascript,canvas,fabricjs,animated-gif,Javascript,Canvas,Fabricjs,Animated Gif,我正在做一个项目,要求我在fabric.js画布上支持动画GIF 根据,我按照建议定期渲染,使用fabric.util.requestAnimFrame。使用此方法,视频渲染效果很好,但GIF似乎不会更新 var canvas = new fabric.StaticCanvas(document.getElementById('stage')); fabric.util.requestAnimFrame(function render() { canvas.renderAll();
var canvas = new fabric.StaticCanvas(document.getElementById('stage'));
fabric.util.requestAnimFrame(function render() {
canvas.renderAll();
fabric.util.requestAnimFrame(render);
});
var myGif = document.createElement('img');
myGif.src = 'http://i.stack.imgur.com/e8nZC.gif';
if(myGif.height > 0){
addImgToCanvas(myGif);
} else {
myGif.onload = function(){
addImgToCanvas(myGif);
}
}
function addImgToCanvas(imgToAdd){
var obj = new fabric.Image(imgToAdd, {
left: 105,
top: 30,
crossOrigin: 'anonymous',
height: 100,
width:100
});
canvas.add(obj);
}
请点击此处:
任何建议都将不胜感激!我一直在到处搜索,但没有找到有效的解决方案。根据Canvas 2RenderingContextdrawImage
方法
具体而言,当CanvasImageSource对象表示动画
图像在HTMLImageElement中,用户代理必须使用默认值
动画图像(将使用格式定义的图像)
不支持或禁用动画时),或
这样的图像,动画的第一帧,在渲染图像时
用于CanvasRenderingContext2D API
这意味着在画布上只绘制动画画布的第一帧。这是因为我们无法控制img标记中的动画 fabricjs基于canvas API,因此受相同规则的监管 然后,解决方案是解析动画gif中的所有静态图像,并将其导出为精灵表。 然后,您可以轻松地在fabricjs中设置动画,这要归功于。
var canvas=newfabric.canvas(document.getElementById('stage');
var url='1〕https://themadcreator.github.io/gifler/assets/gif/run.gif';
fabric.Image.fromURL(url,函数(img){
img.刻度宽度(80);
仪表刻度高度(80);
img.left=105;
img.top=30;
gif(url、函数(帧、延迟){
var framesIndex=0,
动物间隔;
img.dirty=true;
img._render=函数(ctx){
ctx.drawImage(frames[framesIndex],-this.width/2,--this.height/2,this.width,this.height);
}
img.play=函数(){
if(typeof(animInterval)==“未定义”){
animInterval=setInterval(函数(){
framesIndex++;
if(framesIndex==frames.length){
framesIndex=0;
}
},延误);
}
}
img.stop=函数(){
clearInterval(animInterval);
时间间隔=未定义;
}
img.play();
canvas.add(img);
})
})
函数gif(url,回调){
var tempCanvas=document.createElement('canvas');
var tempCtx=tempCanvas.getContext('2d');
var gifCanvas=document.createElement('canvas');
var gifCtx=gifCanvas.getContext('2d');
var-imgs=[];
var xhr=new XMLHttpRequest();
xhr.open('get',url,true);
xhr.responseType='arraybuffer';
xhr.onload=函数(){
var tempBitmap={};
tempBitmap.url=url;
var arrayBuffer=xhr.response;
if(arrayBuffer){
var gif=新的gif(arrayBuffer);
var frames=gif.decompressFrames(真);
gifCanvas.width=帧[0]。dims.width;
gifCanvas.height=帧[0]。dims.height;
对于(变量i=0;i
#阶段{
边框:实心1px#中交;
}
这是我的实现,对于小GIF非常有效,对于大GIF(内存限制)则不太好 现场演示: 使用两个文件/方法 一,
gifToSprite.js
:导入、解析gif并将其与库解压为帧,创建精灵表并返回其数据URL。您可以设置maxWidth
,maxHeight
以缩放gif,设置maxDuration
以毫秒为单位以减少帧数
import { parseGIF, decompressFrames } from "gifuct-js";
/**
* gifToSprite "async"
* @param {string|input File} gif can be a URL, dataURL or an "input File"
* @param {number} maxWidth Optional, scale to maximum width
* @param {number} maxHeight Optional, scale to maximum height
* @param {number} maxDuration Optional, in milliseconds reduce the gif frames to a maximum duration, ex: 2000 for 2 seconds
* @returns {*} {error} object if any or a sprite sheet of the converted gif as dataURL
*/
export const gifToSprite = async (gif, maxWidth, maxHeight, maxDuration) => {
let arrayBuffer;
let error;
let frames;
// if the gif is an input file, get the arrayBuffer with FileReader
if (gif.type) {
const reader = new FileReader();
try {
arrayBuffer = await new Promise((resolve, reject) => {
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsArrayBuffer(gif);
});
} catch (err) {
error = err;
}
}
// else the gif is a URL or a dataUrl, fetch the arrayBuffer
else {
try {
arrayBuffer = await fetch(gif).then((resp) => resp.arrayBuffer());
} catch (err) {
error = err;
}
}
// Parse and decompress the gif arrayBuffer to frames with the "gifuct-js" library
if (!error) frames = decompressFrames(parseGIF(arrayBuffer), true);
if (!error && (!frames || !frames.length)) error = "No_frame_error";
if (error) {
console.error(error);
return { error };
}
// Create the needed canvass
const dataCanvas = document.createElement("canvas");
const dataCtx = dataCanvas.getContext("2d");
const frameCanvas = document.createElement("canvas");
const frameCtx = frameCanvas.getContext("2d");
const spriteCanvas = document.createElement("canvas");
const spriteCtx = spriteCanvas.getContext("2d");
// Get the frames dimensions and delay
let [width, height, delay] = [
frames[0].dims.width,
frames[0].dims.height,
frames.reduce((acc, cur) => (acc = !acc ? cur.delay : acc), null)
];
// Set the Max duration of the gif if any
// FIXME handle delay for each frame
const duration = frames.length * delay;
maxDuration = maxDuration || duration;
if (duration > maxDuration) frames.splice(Math.ceil(maxDuration / delay));
// Set the scale ratio if any
maxWidth = maxWidth || width;
maxHeight = maxHeight || height;
const scale = Math.min(maxWidth / width, maxHeight / height);
width = width * scale;
height = height * scale;
//Set the frame and sprite canvass dimensions
frameCanvas.width = width;
frameCanvas.height = height;
spriteCanvas.width = width * frames.length;
spriteCanvas.height = height;
frames.forEach((frame, i) => {
// Get the frame imageData from the "frame.patch"
const frameImageData = dataCtx.createImageData(
frame.dims.width,
frame.dims.height
);
frameImageData.data.set(frame.patch);
dataCanvas.width = frame.dims.width;
dataCanvas.height = frame.dims.height;
dataCtx.putImageData(frameImageData, 0, 0);
// Draw a frame from the imageData
if (frame.disposalType === 2) frameCtx.clearRect(0, 0, width, height);
frameCtx.drawImage(
dataCanvas,
frame.dims.left * scale,
frame.dims.top * scale,
frame.dims.width * scale,
frame.dims.height * scale
);
// Add the frame to the sprite sheet
spriteCtx.drawImage(frameCanvas, width * i, 0);
});
// Get the sprite sheet dataUrl
const dataUrl = spriteCanvas.toDataURL();
// Clean the dom, dispose of the unused canvass
dataCanvas.remove();
frameCanvas.remove();
spriteCanvas.remove();
return {
dataUrl,
frameWidth: width,
framesLength: frames.length,
delay
};
};
二,fabricGif.js
:主要是gifToSprite
的包装,使用相同的参数返回fabric.Image
的实例,覆盖\u render
方法在每次延迟后重新绘制画布,向play
添加三种方法,暂停和停止
import { fabric } from "fabric";
import { gifToSprite } from "./gifToSprite";
const [PLAY, PAUSE, STOP] = [0, 1, 2];
/**
* fabricGif "async"
* Mainly a wrapper for gifToSprite
* @param {string|File} gif can be a URL, dataURL or an "input File"
* @param {number} maxWidth Optional, scale to maximum width
* @param {number} maxHeight Optional, scale to maximum height
* @param {number} maxDuration Optional, in milliseconds reduce the gif frames to a maximum duration, ex: 2000 for 2 seconds
* @returns {*} {error} object if any or a 'fabric.image' instance of the gif with new 'play', 'pause', 'stop' methods
*/
export const fabricGif = async (gif, maxWidth, maxHeight, maxDuration) => {
const { error, dataUrl, delay, frameWidth, framesLength } = await gifToSprite(
gif,
maxWidth,
maxHeight,
maxDuration
);
if (error) return { error };
return new Promise((resolve) => {
fabric.Image.fromURL(dataUrl, (img) => {
const sprite = img.getElement();
let framesIndex = 0;
let start = performance.now();
let status;
img.width = frameWidth;
img.height = sprite.naturalHeight;
img.mode = "image";
img.top = 200;
img.left = 200;
img._render = function (ctx) {
if (status === PAUSE || (status === STOP && framesIndex === 0)) return;
const now = performance.now();
const delta = now - start;
if (delta > delay) {
start = now;
framesIndex++;
}
if (framesIndex === framesLength || status === STOP) framesIndex = 0;
ctx.drawImage(
sprite,
frameWidth * framesIndex,
0,
frameWidth,
sprite.height,
-this.width / 2,
-this.height / 2,
frameWidth,
sprite.height
);
};
img.play = function () {
status = PLAY;
this.dirty = true;
};
img.pause = function () {
status = PAUSE;
this.dirty = false;
};
img.stop = function () {
status = STOP;
this.dirty = false;
};
img.getStatus = () => ["Playing", "Paused", "Stopped"][status];
img.play();
resolve(img);
});
});
};
三,。实施:
import { fabric } from "fabric";
import { fabricGif } from "./fabricGif";
async function init() {
const c = document.createElement("canvas");
document.querySelector("body").append(c)
const canvas = new fabric.Canvas(c);
canvas.setDimensions({
width: window.innerWidth,
height: window.innerHeight
});
const gif = await fabricGif(
"https://media.giphy.com/media/11RwocOdukxqN2/giphy.gif",
200,
200
);
gif.set({ top: 50, left: 50 });
canvas.add(gif);
fabric.util.requestAnimFrame(function render() {
canvas.renderAll();
fabric.util.requestAnimFrame(render);
});
}
init();
你找到一个好的解决方案了吗?@3244611用户我最终选择了另一条路线,使用了非画布解决方案,因为我找不到答案。我也有同样的问题。请问@PhoenixRizin你最终选择了什么方法?@fongoh martin看到了我之前的回复。他没有进一步追查此事。