Javascript Fabric.js-如何从精灵工作表设置动画

Javascript Fabric.js-如何从精灵工作表设置动画,javascript,animation,html5-canvas,fabricjs,sprite-sheet,Javascript,Animation,Html5 Canvas,Fabricjs,Sprite Sheet,JavaScript语法: context.drawImage(img,srcX,srcY,srcWidth,srcHeight,x,y,width,height); 在Javascript中,如果我想为下面的spritesheet设置动画,我只需更新srcX和srcY每个动画帧,以捕获图像的片段 这将导致每个帧被剪裁并分别显示在画布上,当以固定帧速率更新时,将生成流体精灵动画,如下所示: 如何使用“Fabric.js”库实现这一点 注意:实现这一点的一种方法是设置canvasSize=f

JavaScript语法:

context.drawImage(img,srcX,srcY,srcWidth,srcHeight,x,y,width,height);
在Javascript中,如果我想为下面的spritesheet设置动画,我只需更新
srcX
srcY
每个动画帧,以捕获图像的片段

这将导致每个帧被剪裁并分别显示在
画布上
,当以固定帧速率更新时,将生成流体精灵动画,如下所示:

如何使用“Fabric.js”库实现这一点


注意:实现这一点的一种方法是设置
canvasSize=frameSize
以便在任何给定时间只能看到一个帧。然后通过移动图像,可以在画布内放置不同的帧以模拟动画。但是,对于大画布或可变的帧大小,这将不起作用。

看看这个,它也会做同样的事情

行走的人形。 Fabric.js,图像动画。

var URL='1〕http://i.stack.imgur.com/M06El.jpg';
var canvas=newfabric.canvas('canvas');
变量位置={
上步:2,
左步骤:4
};
canWalk(URL、位置);
函数canWalk(URL、位置){
var myImage=新图像();
myImage.src=URL;
myImage.onload=函数(){
var topStep=myImage.naturalHeight/positions.topSteps;
var leftStep=myImage.naturalWidth/positions.leftSteps;
var docCanvas=document.getElementById('canvas');
docCanvas.height=上台阶;
docCanvas.width=leftStep;
fabricImageFromURL(0,0);
var y=0;
var x=0;
setInterval(函数(){
if(x==positions.leftSteps)
{
x=0;
y++;
if(y==位置.上台阶)
{
y=0;
}
}
fabricImageFromURL(-y*上步,-x*左步);
x++;
},100);
};
}
函数fabricImageFromURL(顶部,左侧)
{
控制台日志(顶部,左侧);
fabric.Image.fromURL(URL,函数(oImg){
oImg.set('左',左)。set('顶',顶);
oImg.hasControls=false;
oImg.hasBorders=false;
oImg.selective=false;
canvas.add(oImg);
canvas.renderAll();
},{“左”:0,“上”:0,“scaleX”:1,“scaleY”:1});
}

默认情况下,Fabric不会这样做。您需要在fabric.Image对象和扩展fabric.Image\u呈现方法中呈现“源”属性。原版看起来是这样的:

/**
*@私人
*@param{CanvasRenderingContext2D}要在其上渲染的ctx上下文
*@param{Boolean}nottransform
*/
_呈现:函数(ctx,不转换){
变量x,y,imageMargins=这个。_findMargins(),elementToDraw;
x=(不转换?此.左:-this.宽/2);
y=(不转换?此.top:-this.height/2);
if(this.meetOrSlice==='slice'){
ctx.beginPath();
ctx.rect(x,y,this.width,this.height);
ctx.clip();
}
if(this.isMoving==false&&this.resizeFilters.length&&this.\u需要大小(){
this.\u lastScaleX=this.scaleX;
this.\u lastScaleY=this.scaleY;
elementToDraw=this.applyFilters(null,this.resizeFilters,this.\u filteredEl | this.\u originalElement,true);
}
否则{
elementToDraw=此。\元素;
}
elementToDraw&&ctx.drawImage(elementToDraw,
x+imageMargins.marginX,
y+imageMargins.marginY,
图像边距。宽度,
身高
);
这是一个冲程(ctx);
这是._renderStroke(ctx);

},
您可以在循环中结合使用设置clipTo函数和调用setLeft/setTop。在fabric.Image构造函数的选项中,传递属性clipTo并告诉fabric剪切图像的特定部分。然后,当setTop/setLeft位于循环内时,触发重新绘制,从而调用clipTo,同时重新定位剪切图像,使其始终保持在同一位置

我遇到了同样的问题,并将逻辑提取为两个函数。选项的快速汇总:
精灵宽度-精灵的一个动画帧的宽度
精灵高度-精灵的一个动画帧的高度
总宽度-整个精灵图像的宽度
总高度-整个精灵图像的高度
animationFrameDuration-一个精灵帧应显示多长时间
startRandom-如果您不想立即启动动画,而是想在1秒内随机启动动画
-就像正常的fabric左选项一样。Image
上衣-就像普通面料的上衣一样。图片

同步版本(传递HTMLImageElement):

异步版本(传递图像URL):

请注意,上述函数不会重新渲染画布,您必须自己执行。
您可以使用上述类似的代码并排设置精灵两次动画(一次同步版本,一次异步版本):


您一定要使用fabricsj吗?我的意思是,这可以通过单独使用css3动画来实现@MohitBhardwaj:我不使用CSS动画有几个原因。下面的链接提供了一些解释。。。我正在创建的游戏相对较大,需要良好的性能和灵活性。感谢提供的信息链接:)很好的黑客操作方式;)不幸的金枪鱼
/**
 * @param imgObj HTMLImageElement
 * @param options {
 *                  spriteWidth: number
 *                  spriteHeight: number
 *                  totalWidth: number
 *                  totalHeight: number
 *                  animationFrameDuration: number
 *                  startRandom: boolean (optional)
 *                  left: number (optional)
 *                  top: number (optional)
 *                }
 * @returns fabric.Image
 */
function animateImg(imgObj, options) {
    const left = options.left || 0;
    const top = options.top || 0;
    let x = 0;
    let y = 0;
    const image = new fabric.Image(imgObj, {
        width: options.totalWidth,
        height: options.totalHeight,
        left: left,
        top: top,
        clipTo: ctx => {
            ctx.rect(-x - options.totalWidth / 2, -y - options.totalHeight / 2, options.spriteWidth, options.spriteHeight);
        }
    });
    setTimeout(() => {
        setInterval(() => {
            x = (x - options.spriteWidth) % options.totalWidth;
            if (x === 0) {
                y = (y - options.spriteHeight) % options.totalHeight;
            }
            image.setLeft(x + left);
            image.setTop(y + top);
        }, options.animationFrameDuration)
    }, options.startRandom ? Math.random() * 1000 : 0);
    return image;
}
/**
 * @param imgURL string
 * @param options {
 *                  spriteWidth: number
 *                  spriteHeight: number
 *                  totalWidth: number
 *                  totalHeight: number
 *                  animationFrameDuration: number
 *                  startRandom: boolean (optional)
 *                  left: number (optional)
 *                  top: number (optional)
 *                }
 * @param callback (image : fabric.Image) => void
 */
function animateImgFromURL(imgURL, options, callback) {
    const left = options.left || 0;
    const top = options.top || 0;
    let x = 0;
    let y = 0;
    fabric.Image.fromURL(
        imgURL,
        image => {
            setTimeout(() => {
                setInterval(() => {
                    x = (x - options.spriteWidth) % options.totalWidth;
                    if (x === 0) {
                        y = (y - options.spriteHeight) % options.totalHeight;
                    }
                    image.setLeft(x);
                    image.setTop(y);
                }, options.animationFrameDuration)
            }, options.startRandom ? Math.random() * 1000 : 0);
            callback(image);
        }, {
            width: options.totalWidth,
            height: options.totalHeight,
            left: 0,
            top: 0,
            left: left,
            top: top,
            clipTo: ctx => {
                ctx.rect(-x - options.totalWidth / 2, -y - options.totalHeight / 2, options.spriteWidth, options.spriteHeight);
            }
        });
// Assuming:
// 1. canvas was created
// 2. Sprite is in html with id 'walking'
// 3. Sprite is within folder 'images/walking.jpg'
const img1 = animateImg(document.getElementById('walking'), {
    spriteWidth: 125,
    spriteHeight: 125,
    totalWidth: 500,
    totalHeight: 250,
    startRandom: true,
    animationFrameDuration: 150,
    left: 125,
    top: 0
});
canvas.add(img1);
animateImgFromURL('images/walking.jpg', {
    spriteWidth: 125,
    spriteHeight: 125,
    totalWidth: 500,
    totalHeight: 250,
    startRandom: true,
    animationFrameDuration: 150
}, image => canvas.add(image));
// hacky way of invoking renderAll in a loop:
setInterval(() => canvas.renderAll(), 10);