在实践过bodymovin之后,发现如果需要在动画里面加逻辑比较麻烦,效果也不好。因此后面找到了另外一个开源的工具,就是YY开源出来的svga,有android,ios,web版本,还有对应的AE插件,用法请看官方文档
之所以选择svga:
- 比较强大的api;
- 据说性能比bodymovin好(不敢下定论,因为按照目前接触过bodymovin跟svga的情况来看,感觉如果只是纯播放,bodymovin的性能会流畅一点,当然,这个跟我的引用场景有关系,一个比较蛋疼的原因,就是需要在本地播放。);
问题:因为需要本地播放,所以Parse.load传的是svga文件转成base64之后的字符串,load会多了一步readBlobAsArrayBuffer的过程,这个很耗浏览器性能。
svga
设计通过AE+svga插件,导出一个动画的svga格式的一个二进制文件,虽然看不到里面的内容,但是把他扔到svga预览,是可以看到svga文件的一些信息的(转换之后)
svga基本信息
{
"version": "2.0",
"FPS": 30,
"frames": 110,
"videoSize": {
"width": 750,
"height": 750
}
}
//素材信息
A --- {"width":600,"height":493}
Aleft --- {"width":82,"height":67}
Aright --- {"width":82,"height":67}
Avatar --- {"width":72,"height":72}
BG --- {"width":750,"height":750}
Bleft --- {"width":120,"height":97}
Bright --- {"width":120,"height":97}
Planet --- {"width":80,"height":60}
flower --- {"width":63,"height":26}
hole --- {"width":113,"height":109}
img_491 --- {"width":38,"height":38}
medal --- {"width":376,"height":415}
medal-bling-l --- {"width":66,"height":66}
复制代码
当然,文件里面的信息肯定不只包含上面的信息,看svga-web的源码,里面的reader那一部分的逻辑,应该是有包含具体到哪一帧用canvas绘制上面样的内容的一些信息(这个好像是废话)。
需求:宝箱1.0
设计大佬会给一个宝箱的svga,这边需要做的就是通过设计大佬给一个imagekey+svga的setImage去设置宝箱开处理的礼物图片。
动画gif如下:
部分代码如下:
var parser = new svgaplayerweb.Parser('#canvas')
var player = new svgaplayerweb.Player('#canvas')
//之所以用这个方法去做适配是因为官方的库设置fillMode属性有在ios7p/8p 上面有bug,那时候没去提issuse,不知道现在修复了没
function setFill(){
var $_canvas = document.getElementById('canvas'),
w = window.innerWidth,
h = window.innerHeight,
screen_proportion = h/w,
svga_proportion = 16/9;
if(screen_proportion > svga_proportion){//长屏幕
$_canvas.style.width = h/svga_proportion+'px';
$_canvas.style.left = (w-h/svga_proportion)/2+'px';
}else{
$_canvas.style.height = w*svga_proportion+'px';
$_canvas.style.top = (h-w*svga_proportion)/2+'px';
}
}
//初始化
function svgaInitial(giftSrc){
// svga ready
parser.load(svgabase64,function(videoItem){
//设置宝箱开出的礼物图片
player.setImage(giftSrc, 'gift')
player.loops = 1;
player.setVideoItem(videoItem);
player.onFinished(function(){
//播放结束后触发离开逻辑
leave();
})
enter();
},function(err){
// alert(err.message);
//报错直接触发离开逻辑
leave();
})
}
复制代码
需求:宝箱1.1
宝箱部分礼物需要开出图片,一部分需要开出特效,这个时候就比较为难,最后决定的解决方案是在宝箱里面放置了好几段特效,分别是:宝箱打开+普通礼物(1.0)+宝箱打开+大礼物特效*N(这样导致了一个特效包最大的差不多2M)
部分代码如下:
//相对于1.0的版本,1.1的版本多了好几个svga文件,多了以下判断逻辑,通过参数去觉得播放哪一个svga
if(giftId == 7){
svgabase64 = box_JL;
}
else if(giftId == 8){
svgabase64 = box_HD;
}
else if(giftId == 9){
svgabase64 = box_CS;
}
问题:虽然只load一份base64,不过几个base64文件打包到一个js里面去,会导致js的体积很大
复制代码
需求:宝箱1.2
宝箱需要开出的礼物有不同的类别(最大五种)+不同的数量,如果这个效果按照1.1的形式去做的话,得需要有五段不一样的svga,那么整个特效包的大小肯定会超过2M。在去看文档的时候,发现了startAnimationWithRange这个方法,播放svga动画的某一个区间的动画,但还是满足不了,无奈之下fork一下仓库,自己实现一些功能
- 多段拼接播放(startAnimationWithRangeDouble)
- 特殊字体,字体颜色渐变,阴影的支持
player.setText({
text:form,
size:form > 99 ? "160px" :"200px",
color:"linear-gradient(to right, #e1d7b4, #bfae7d)",
family:"Impact",
offset:{
x:0,
y:-10
},
textShadow:{
color:'rgba(0, 0, 0, 0.01)',
offsetX:0,
offsetY:3,
blur:3
}
},"numberone");
复制代码
动画gif如下:
部分代码如下:
function svgaInitial(){
var len = gift.length;
var releaseGift = function(data,len){
data.forEach((item,index) => {
player.setImage(item.src,`gift-${len == 1 ? "" : len+"-"}${index+1}`);
player.setText({
text:`X${item.count}`,
size:item.count>999 ? "40px":"54px",
family:"BaiZhouRenZhe",
color: "#FFF2C8",
offset: {x: 0, y: 0}
},`gift-count-${len == 1 ? "" : len+"-"}${index+1}`)
})
}
// svga ready
parser.load(svgabase64,function(videoItem){
player.loops = 1;
releaseGift(gift,giftLen);
player.setVideoItem(videoItem);
player.onFinished(function(){
leave();
})
if(len > 0){
enter();
}
},function(err){
leave();
})
}
function enter() {
var releaseSvga = function(gift,len){
switch(len){
case 1:
player.startAnimationWithRange({location:0,length:200});
break;
case 2:
player.startAnimationWithRangeDouble([{location:0,length:150},{location:200,length:50}]);
break;
case 3:
player.startAnimationWithRangeDouble([{location:0,length:150},{location:250,length:50}]);
break;
case 4:
player.startAnimationWithRangeDouble([{location:0,length:150},{location:300,length:50}]);
break;
case 5:
player.startAnimationWithRangeDouble([{location:0,length:150},{location:350,length:50}]);
break;
}
}
releaseSvga(gift,giftLen)
}
//相对于1.1的版本,这次1.2的版本减少了打包之后整体的体积,但是因为是全部动画都在一个svga文件,单个的svga的文件比较大,因此load起来的速度也会慢一些。
复制代码
总结
- 关于bodymovin 跟 svga的性能,网上有分析,不过我还是觉得要自己分析一下才能下定论。
- 由于需要本地播放,因此对我来说,svga2base64的体积比svga的体积会大25%左右,如果是bodymovin的话,一份json的体积其实还是挺小的,就是api支持不够
- 对于android4.x的手机来说,不支持jszip,还有blob的,还得导入这两个库,这一部分的体积应该是在100kb左右,当然,这个是可以做成离线缓存的,这个又是另外一个话题了
bodymovin,svga帮助我们解脱了看着mov写特效,然后设计师坐在旁边花费很多时间去调试到满意的结果的日子。让前端可以更注重写逻辑这块的代码。
以后
- 目前bodymovin跟svga都不支持粒子效果,看下能不能撇开插件那块,在svga-web里面去做这一部分。主要是设计+产品的想法是永远都无法满足的 T T
本来是想在每个版本上面都加上动画的gif的,奈何md的写法添加图片比较烦,或者需要一个图片服务器上传图片才好,又不想用富文本编辑器去写,所以就没放了。