一开始看到 spriteJs ,一脸懵逼,啥时候"雪碧图"出了个插件,后来看了介绍,不得不嘲笑自己的无知,呵呵,井底之蛙!!
spriteJs 是一款由 360 奇舞团开源的跨终端 canvas 绘图框架,可以基于 canvas 快速绘制结构化 UI、动画和交互效果,是跨平台的2D绘图对象模型库,它能够支持web、node、桌面应用和微信小程序的图形绘制和实现各种动画效果。
canvas本身的api不就挺好用的吗,为什么要用 spriteJs 呢?后来做了下对比,有兴趣的小伙伴可以看一下这里:https://www.v2ex.com/t/464279 把为什么使用它介绍的挺详细的
官方文档:http://spritejs.org/#/zh-cn/index
github:https://github.com/spritejs/spritejs
废话不多说,下面就用 spriteJs 来做一个简单的动画demo,先奉上效果,我称其为"机器人下蛋"(机器人来之 spriteJs 官方):
demo效果:https://liuxiaochaogit.github.io/demo/spriteJs/index.html
源码:https://github.com/liuxiaochaoGit/demo/tree/master/spriteJs
安装 spriteJs,因为是demo,我这直接引入的CDN:
首先我们引入场景 Scene,利用它来管理Layer,派发事件,实现屏幕适配,具体的文档可以看这里,很详细:http://spritejs.org/#/zh-cn/doc/scene
const {Scene} = spritejs;
// container 是容器
const scene = new Scene('#container')
然后引入Sprite,你可以将其理解为一个最基础的dom元素,有四种类型:分别是Sprite、Label、Path和Group,不多介绍,可以直接怼文档:http://spritejs.org/#/zh-cn/doc/sprite
const {Scene, Sprite} = spritejs;
// 创建layer对象,一个layer对应一个canvas
const layer = scene.layer();
// 引入图片,可以是相对路径,也可以是绝对路径,也可以是cdn
const bg = new Sprite('./img/bg.png');
// 初始化设置
bg.attr({
anchor: [0, 0],
x: 0,
y: 0,
// size: [1000, 600]
});
// 将图片添加到layer(canvas)
layer.append(bg);
是不是很熟悉的api,像attr,append这些跟jquery里面的方法很像,这不就是jquery么?
现在一个简单的背景图片已经被添加到layer(canvas)上了
同理我们可以将机器人和蛋都添加到layer上,代码如下:
(async function () {
// 这是spriteJs的预加载,毕竟像bg那么大的一张图片
await scene.preload({
id: 'robot',
src: './img/robot.png'
}, {
id: 'rabbit',
src: './img/rabbit.png'
}, {
id: 'egg1',
src: './img/dragon-egg1.png'
}, {
id: 'egg2',
src: './img/dragon-egg2.png'
}, {
id: 'bg',
src: './img/bgImage.png'
}, {
id: 'snow',
src: './img/snowflake.png'
}
)
})();
const robot = new Sprite('./img/robot.png');
const rabbit = new Sprite('rabbit');
const egg1 = new Sprite('egg1');
const egg2 = new Sprite('egg2');
const bg = new Sprite('bg');
// 背景,
bg.attr({
anchor: [0, 0],
x: 0,
y: 0,
// size: [1000, 600]
});
// 机器人
robot.attr({
anchor: [0.5, 0],
// 因为我们要机器人重右上角飞到中间,然后在从左上角飞走,所以出事位置是右上角
x: width,
y: 0,
// 设置图片宽度和高度,最好按比例修改,但是按比例的话,我获取不到机器人的高度了,
// 无奈之下,设定的固定值,有知道怎么能拿到高度高度值的小伙伴还请告知一下,谢谢
// scale:0.5
size: [182, 252]
});
// 获取机器人的身高
let robotHeight = robot.offsetSize[1];
// 不正经的兔子蛋
let rabbitP = await initEgg(rabbit);
// 正经的恐龙蛋
let egg1P = await initEgg(egg1);
let egg2P = await initEgg(egg2);
// 将它们添加到layer上
layer.append(bg);
layer.append(robot);
layer.append(rabbitP);
layer.append(egg1P);
layer.append(egg2P);
// 初始化蛋蛋,因为有三个蛋,所以将初始化方法封了一下
async function initEgg(egg) {
await egg.attr({
anchor: [0.5, 0.5],
x: 0,
y: 0,
opacity: 1
});
return egg;
}
然后我们要先让机器人动起来,这就需要用到spriteJs的animate了,怎么是不是感觉很熟悉,对,css里面的animate是有点像的,具体文档:http://spritejs.org/#/zh-cn/doc/animation
// 会飞的机器人
async function animateRobot() {
// 开始飞 效果是右上角飞到中间点三下,然后就从左上角飞走了
await robot.animate([
{x: width, y: 0},
{x: width / 2, y: height / 2},
{x: width / 2, y: height / 2 + 60},
{x: width / 2, y: height / 2},
{x: width / 2, y: height / 2 + 60},
{x: width / 2, y: height / 2},
{x: width / 2, y: height / 2 + 60},
{x: width / 2, y: height / 2},
{x: 0, y: 0}
], {
// 持续时长
duration: 8000,
// 是否循环,可以是数字,代表循环次数
// Infinity代表一直循环,类似于css animation infinite这个属性值
iterations: Infinity,
// 动画播放的方向,这里默认正向,有兴趣的小伙伴可以去看文档
direction: 'default',
});
}
调用这个方法,你会发现,机器人会飞了…
接下来就到了"下蛋"的时候了,我们先让蛋蛋从天而降话,至于会不会摔坏那不在我们关心范围:
// 蛋的动画
async function animateEgg(egg) {
egg.animate([
{
x: width / 2,
y: 0,
opacity: 1
},
{
x: width / 2,
y: height,
opacity: 1
}
], {
duration: 2000,
iterations: 1,
direction: 'default',
});
}
一次传入三个蛋,然后你就会发现天上在同一个位置掉下三个蛋
关键的来了,怎么将机器人和蛋的动画组合起来呢,这就需要用到 finsished 属性了,就是在动画结束之后再执行其他的动画或者随便什么东西我们将机器人和蛋的动画修改一下:
// 会飞并且会下蛋的机器人
async function animateRobot() {
await robot.animate([
{x: width, y: 0},
{x: width / 2, y: height / 2},
{x: width / 2, y: height / 2 + 60},
], {
duration: 3500,
iterations: 1,
direction: 'default',
}).finished;
// 到指定位置,下蛋
animateEgg(rabbit, 45);
await robot.animate([
{x: width / 2, y: height / 2 + 60},
{x: width / 2, y: height / 2},
{x: width / 2, y: height / 2 + 60},
], {
duration: 1500,
iterations: 1,
direction: 'default',
}).finished;
// 在下一个蛋
animateEgg(egg1, -45);
await robot.animate([
{x: width / 2, y: height / 2 + 60},
{x: width / 2, y: height / 2},
{x: width / 2, y: height / 2 + 60},
], {
duration: 1500,
iterations: 1,
direction: 'default',
}).finished;
// 保证最后一个
animateEgg(egg2, 0);
await robot.animate([
{x: width / 2, y: height / 2 + 60},
{x: width / 2, y: height / 2},
{x: 0, y: 0}
], {
duration: 2000,
iterations: 1,
direction: 'default',
}).finished;
}
// 蛋的动画
async function animateEgg(egg, deg) {
egg.animate([
{
x: width / 2,
y: (height / 2) + robotHeight + 60,
opacity: 1
},
{
x: width / 2 + deg,
y: (height / 2) + robotHeight + 360,
opacity: 1
},
{
rotate: deg
}
], {
duration: 2000,
iterations: 1,
direction: 'default',
fill: 'forwards'
});
}
这样调用机器人的动画,机器人就会从右上角飞到中间,然后下三个蛋蛋,再从左上角飞走,下面鸟窝里就有了两个蛋和一个兔子,
如果想让它一直下蛋就在执行完动画之后,重复执行该方法就好了
"机器人下蛋"完成了,但是我想要点不一样的天气,于是就添加了下雪的效果,于是就有了以下代码:
// 创建300片雪花,循环而不是一直创建,稍微节省点性能
for(let i = 0;i < 300;i++){
setTimeout(async () => {
let snow = new Sprite('snow');
// 初始化雪花
let snowP = await initSnow(snow);
// 雪花添加到layer
layer.append(snowP.sprite);
// 执行动画
animateSnow(snowP);
},100 * i);
}
// 初始化雪花
async function animateSnow(snow) {
snow.sprite.animate([
{
x:snow.attribute.x,
y:0
},
{
x:snow.attribute.x,
y:height * 2
}
], {
duration: 5000,
iterations: Infinity,
direction: 'default',
})
}
// 雪花的动画
async function initSnow(snowSpite) {
var sss = random(20,100);
let snow = {
width: sss,
height: sss,
x: random(0, width),
y: 0,
opacity: random(0, 1)
};
await snowSpite.attr({
anchor:[0.5,0.5],
x: snow.x,
y: snow.y,
size: [snow.width, snow.height],
opacity: snow.opacity
});
let obj = {
attribute:snow,
sprite:snowSpite
};
return obj;
}
"机器人在雪中下蛋"完成,动画很简单,但在写的过程中,对 spriteJs 了解也在不断加深,有时间会接着研究 spriteJs 的,性能很不错。
本文纯手打,有不当之处请留言!如果对小伙伴们有帮助的话,点赞啊,谢谢!