【AnimeJs】——仿Animejs徒手实现SVG动画

我们一般会引入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. 动画引擎

你可能感兴趣的:(前端,javascript,css,html,前端,动画)