我们一般会引入anime.js来改变svg动画,但是anime.js源码有一千多行,但我们只需要修改svg这部分的时候,可以通过我们自己手写一段代码来实现svg动画,来优化性能。
首先来看先anime.js是如何改变svg动画:
1、在script标签中引入anime.js:;
2、svg中有path属性,通过改变path中的d、fill、stroke来改变动画效果。
Document
我们创建new-anime.js文件,来实现animejs中实现svg的部分,细节详见代码的备注部分:
function anime(params) {
// instance:相关属性仍然挂载在一个对象下,代表当前动画
// raf ;代表当前标识
var instance = {},raf;
instance.duration = 0;
// 1. 将动画需要改变的属性存储起来
// properties中存储d、fill、stroke这三个属性
instance.properties = [];
instance.animations = [];
for (var key in params) {
if (key !== 'targets') {
instance.properties.push(key)
} else {
instance[key] = params[key];
}
}
// 2. 将属性和目标元素匹配,生成动画数据
instance.properties.forEach(function (item) {
// animation:每拿到一个属性,就会创建一个动画数据,用animation来接收
var animation = {};
animation.property = item;
// from:是d、fill、stroke中value的第一个值 、fill中value针对颜色我们还要进行转换
// 如果是颜色值,我们还需要对颜色进行转换成rgb,然后再存储起来
// to:是d、fill、stroke中value的第二个值
// isHex:判断是否为颜色值
// hexToRgba:转换为rgb的格式
animation.from = isHex(params[item].value[0]) ? hexToRgba(params[item].value[0]) : params[item].value[0];
animation.to = isHex(params[item].value[1]) ? hexToRgba(params[item].value[1]) : params[item].value[1];
// 将每个值单独取出来,进行处理.
// 比如将rgb中的r取出来r跟r计算 / g跟g计算...
animation.from = valueNormalize(animation.from);
animation.to = valueNormalize(animation.to);
// 存储属性
animation.duration = params[item].duration;
animation.easing = params[item].easing;
// 将持续时间取最大值赋值给
instance.duration = instance.duration > animation.duration ? instance.duration : animation.duration;
instance.animations.push(animation);
})
// 处理每一帧
instance.tick = function (t) {
if (t > instance.duration) {
return;
}
instance.animations.forEach(function (animation) {
if (t <= animation.duration) {
var value = [];
// animation.from.numbers:这里就是把属性的r拆开了,把d中的数字都拆开了
animation.from.numbers.forEach(function (item, index) {
// 计算改变量:颜色值是不允许出现小数的,但是d的点的坐标是可以的,所以要做区分
if (isRgb(animation.from.original)) {
// easeInQuart:运动的改变量
// 将其转为整数,颜色的值不可以带小数,然后存储到value中
value.push(parseInt(easeInQuart(t,animation.from.numbers[index],animation.to.numbers[index] - animation.from.numbers[index],animation.duration)));
} else {
value.push(easeInQuart(t,animation.from.numbers[index],animation.to.numbers[index] - animation.from.numbers[index],animation.duration));
}
})
}
var strings = animation.from.strings;
var stringsLength = strings.length;
var progress = strings[0];
for (var s = 0; s < stringsLength; s++) {
var b = strings[s + 1];
var n$1 = value[s];
if (!isNaN(n$1)) {
if (!b) {
progress += n$1 + ' ';
} else {
progress += n$1 + b;
}
}
}
// 设置属性
// d是作为path的属性的
// fill和stock是style下的
if (isRgb(progress)) {
instance.targets.style[animation.property] = progress;
} else {
instance.targets.setAttribute([animation.property], progress);
}
})
}
// 动画引擎:
if(!raf){
play();
}
function play () {
raf = requestAnimationFrame(step);
}
// 处理每一帧
function step(t) {
instance.tick(t);
if(t > instance.duration){
return;
}
play()
}
}
// 匀速运动的改变:(当前的时间t / 当前的持续时间) * (from - to的时间差)
// 但是由于 easing: 'easeInOutQuart',这是一个缓动的算法,我们使用前人总结的如下算法来计算运动的改变:
// t:当前时间、b:起始值、c:改变量(也就是终止值和起始值的差)、d:总的持续时间
function easeInQuart (t, b, c, d) {
return c * (t /= d) * t * t * t + b;
}
// hexToRgba:对颜色进行rgb的转换
function hexToRgba(hexValue) {
var rgx = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
var hex = hexValue.replace(rgx, function (m, r, g, b) { return r + r + g + g + b + b; });
var rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
var r = parseInt(rgb[1], 16);
var g = parseInt(rgb[2], 16);
var b = parseInt(rgb[3], 16);
return ("rgba(" + r + "," + g + "," + b + ",1)");
}
// isHex:判断是否为颜色值
function isHex(a) { return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a); }
// isRgb:判断是否为rgb
function isRgb(a) { return /^rgb/.test(a); }
// 把每个值单独取出来
function valueNormalize(val) {
var rgx = /[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g; // handles exponents notation
return {
original: val,
numbers: val.match(rgx) ? val.match(rgx).map(Number) : [0],
// 用来存单位
strings: val.length ? val.split(rgx) : []
}
}
创建完成,这时我们同样通过script标签引入我们自己实现的包
由于没有进行targets的处理,这里需要使用原生js方法来获取目标元素,除了这一点与animejs不同,其余效果我们可以看到完全相同,并且只书写了一百多行代码,性能得到了提升:
Document
这里我再进行一下总结,实现svg的内容主要分为一下4个步骤:
1. 处理参数属性
2. 将参数属性处理成动画数据
3. 设置帧
4. 动画引擎