您可以结合使用设置 clipTo 函数和在循环中调用 setLeft / setTop。在 fabric.Image 构造函数的选项中,您传递属性 clipTo 并告诉fabric 剪切图像的特定部分。然后,使用 setTop / setLeft 在循环内触发重绘,从而调用 clipTo 并同时重新定位剪切图像,使其始终保持在同一个位置。
我遇到了同样的问题,并将逻辑提取到两个函数中。选项概览:
spriteWidth - 精灵一帧动画的宽度
spriteHeight - 精灵一帧动画的高度
totalWidth - 整个精灵图像的宽度
totalHeight - 整个精灵图像的高度
animationFrameDuration - 一个精灵帧应该多长显示
startRandom - 如果您不想立即开始动画,而是在 1 秒内随机开始
left - 就像 fabric 的正常左侧选项一样.Image
top - 就像 fabric.Image 的正常顶部选项
同步版本(通过 HTMLImageElement):
/**
* @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;
}
异步版本(传递图片 URL):
/**
* @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);