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

我们一般会引入anime.js来改变svg动画,但是anime.js源码有一千多行,但我们只需要修改svg这部分的时候,可以通过我们自己手写一段代码来实现svg动画,来优化性能。

首先来看先anime.js是如何改变svg动画:

1、在script标签中引入anime.js:<script src="anime.js"></script>;

2、svg中有path属性,通过改变path中的d、fill、stroke来改变动画效果。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <svg class="social" xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
        <path class="path"
            style="fill:#3b5998;fill-rule:evenodd;stroke:#3b5998;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
            d="m 41.416254,90 c -0.327378,-7.4702 0.20833,-32.7284 0,-39.901 -5.386902,-0.2083 -4.521603,0.3274 -9.848987,0 0.20833,-5.50595 0.36436,-7.66666 0.126269,-13.32142 4.646472,0.0181 3.439989,-0.009 9.848987,-0.1894 0.09586,-3.7736 0.133082,-3.0791 0.126269,-7.38674 0.18259,-3.73943 -0.486609,-10.54308 4.293149,-14.96288 4.779758,-4.4198 13.606811,-3.64808 22.223356,-3.53554 -0.04417,5.73754 -0.03936,9.37986 0,12.87945 -5.049924,0.46388 -7.309188,-0.33689 -10.85914,1.26269 -1.403378,3.17794 -1.569601,4.80531 -1.262691,11.93242 3.147964,-0.13336 8.201788,-0.1378 12.626907,0 -0.995158,6.00899 -0.948285,7.62376 -1.767767,13.06882 -3.676625,0.088 -5.605721,-0.1488 -11.111678,0 -0.148814,6.756 0.357147,33.0107 0,40.1536 -6.428576,0.1786 -8.174438,-0.03 -14.394674,0 z" />
    </svg>
    <script src="anime.js"></script>
    <script>
        anime({
                targets: '.path',
                d: {
                    // 改变d,从一种状态改变到另一种状态
                    value: [
                        "m 41.416254,90 c -0.327378,-7.4702 0.20833,-32.7284 0,-39.901 -5.386902,-0.2083 -4.521603,0.3274 -9.848987,0 0.20833,-5.50595 0.36436,-7.66666 0.126269,-13.32142 4.646472,0.0181 3.439989,-0.009 9.848987,-0.1894 0.09586,-3.7736 0.133082,-3.0791 0.126269,-7.38674 0.18259,-3.73943 -0.486609,-10.54308 4.293149,-14.96288 4.779758,-4.4198 13.606811,-3.64808 22.223356,-3.53554 -0.04417,5.73754 -0.03936,9.37986 0,12.87945 -5.049924,0.46388 -7.309188,-0.33689 -10.85914,1.26269 -1.403378,3.17794 -1.569601,4.80531 -1.262691,11.93242 3.147964,-0.13336 8.201788,-0.1378 12.626907,0 -0.995158,6.00899 -0.948285,7.62376 -1.767767,13.06882 -3.676625,0.088 -5.605721,-0.1488 -11.111678,0 -0.148814,6.756 0.357147,33.0107 0,40.1536 -6.428576,0.1786 -8.174438,-0.03 -14.394674,0 z",
                        "m 10.44335,90 c 11.073313,0.3952 19.483106,-1.8358 23.901837,-7.1603 -7.9736,-1.4292 -11.832311,-4.1933 -15.078321,-11.0837 3.459698,0.8219 5.795894,0.6358 7.606781,-0.607 -7.19593,-1.719 -12.734543,-6.7971 -13.741664,-15.836 2.766355,1.55307 5.466848,2.66623 7.828682,2.0203 -4.336544,-2.92911 -9.838998,-10.47636 -5.555839,-22.47589 8.400675,11.87052 23.824269,17.67568 33.840111,17.67767 -0.936406,-9.74688 5.88057,-19.46521 15.302849,-19.97853 8.13118,-0.50719 10.57457,4.01944 12.476346,4.82624 3.644547,0.13419 7.393301,-1.74401 10.354063,-3.53553 -1.380842,4.47157 -5.06769,5.62903 -6.313453,8.58629 5.42317,0.41513 5.891376,-1.53111 8.333758,-2.0203 -2.071414,3.75017 -5.393863,5.00034 -7.323606,8.08122 -1.633654,16.12573 -5.16049,27.57123 -14.647212,36.36553 -13.825764,11.3764 -34.755458,17.369 -56.984332,5.14 z"
                    ],
                    // 动画持续时间
                    duration: 2000,
                    // 动画缓动效果
                    easing: 'easeInOutQuart'
                },
                // 填充的颜色
                fill: {
                    value: ['#3b5998', '#4099ff'],
                    duration: 2000,
                    easing: 'easeInOutQuart'
                },
                // stroke是线的颜色
                stroke: {
                    value: ['#3b5998', '#4099ff'],
                    duration: 2000,
                    easing: 'easeInOutQuart'
                },
              
            })
    </script>
</body>

</html>

我们创建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标签引入我们自己实现的包

<script src="new-anime.js"></script>

由于没有进行targets的处理,这里需要使用原生js方法来获取目标元素,除了这一点与animejs不同,其余效果我们可以看到完全相同,并且只书写了一百多行代码,性能得到了提升: 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <svg class="social" xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
        <path class="path"
            style="fill:#3b5998;fill-rule:evenodd;stroke:#3b5998;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
            d="m 41.416254,90 c -0.327378,-7.4702 0.20833,-32.7284 0,-39.901 -5.386902,-0.2083 -4.521603,0.3274 -9.848987,0 0.20833,-5.50595 0.36436,-7.66666 0.126269,-13.32142 4.646472,0.0181 3.439989,-0.009 9.848987,-0.1894 0.09586,-3.7736 0.133082,-3.0791 0.126269,-7.38674 0.18259,-3.73943 -0.486609,-10.54308 4.293149,-14.96288 4.779758,-4.4198 13.606811,-3.64808 22.223356,-3.53554 -0.04417,5.73754 -0.03936,9.37986 0,12.87945 -5.049924,0.46388 -7.309188,-0.33689 -10.85914,1.26269 -1.403378,3.17794 -1.569601,4.80531 -1.262691,11.93242 3.147964,-0.13336 8.201788,-0.1378 12.626907,0 -0.995158,6.00899 -0.948285,7.62376 -1.767767,13.06882 -3.676625,0.088 -5.605721,-0.1488 -11.111678,0 -0.148814,6.756 0.357147,33.0107 0,40.1536 -6.428576,0.1786 -8.174438,-0.03 -14.394674,0 z" />
    </svg>
    <script src="new-anime.js"></script>
    <script>
        anime({
            targets: document.querySelector('path'),
            d: {
                value: [
                    "m 41.416254,90 c -0.327378,-7.4702 0.20833,-32.7284 0,-39.901 -5.386902,-0.2083 -4.521603,0.3274 -9.848987,0 0.20833,-5.50595 0.36436,-7.66666 0.126269,-13.32142 4.646472,0.0181 3.439989,-0.009 9.848987,-0.1894 0.09586,-3.7736 0.133082,-3.0791 0.126269,-7.38674 0.18259,-3.73943 -0.486609,-10.54308 4.293149,-14.96288 4.779758,-4.4198 13.606811,-3.64808 22.223356,-3.53554 -0.04417,5.73754 -0.03936,9.37986 0,12.87945 -5.049924,0.46388 -7.309188,-0.33689 -10.85914,1.26269 -1.403378,3.17794 -1.569601,4.80531 -1.262691,11.93242 3.147964,-0.13336 8.201788,-0.1378 12.626907,0 -0.995158,6.00899 -0.948285,7.62376 -1.767767,13.06882 -3.676625,0.088 -5.605721,-0.1488 -11.111678,0 -0.148814,6.756 0.357147,33.0107 0,40.1536 -6.428576,0.1786 -8.174438,-0.03 -14.394674,0 z",
                    "m 10.44335,90 c 11.073313,0.3952 19.483106,-1.8358 23.901837,-7.1603 -7.9736,-1.4292 -11.832311,-4.1933 -15.078321,-11.0837 3.459698,0.8219 5.795894,0.6358 7.606781,-0.607 -7.19593,-1.719 -12.734543,-6.7971 -13.741664,-15.836 2.766355,1.55307 5.466848,2.66623 7.828682,2.0203 -4.336544,-2.92911 -9.838998,-10.47636 -5.555839,-22.47589 8.400675,11.87052 23.824269,17.67568 33.840111,17.67767 -0.936406,-9.74688 5.88057,-19.46521 15.302849,-19.97853 8.13118,-0.50719 10.57457,4.01944 12.476346,4.82624 3.644547,0.13419 7.393301,-1.74401 10.354063,-3.53553 -1.380842,4.47157 -5.06769,5.62903 -6.313453,8.58629 5.42317,0.41513 5.891376,-1.53111 8.333758,-2.0203 -2.071414,3.75017 -5.393863,5.00034 -7.323606,8.08122 -1.633654,16.12573 -5.16049,27.57123 -14.647212,36.36553 -13.825764,11.3764 -34.755458,17.369 -56.984332,5.14 z"
                ],
                duration: 2000,
                easing: 'easeInOutQuart'
            },
            fill: {
                value: ['#3b5998', '#4099ff'],
                duration: 2000,
                easing: 'easeInOutQuart'
            },
            stroke: {
                value: ['#3b5998', '#4099ff'],
                duration: 2000,
                easing: 'easeInOutQuart'
            },

        })
    </script>
</body>

</html>

这里我再进行一下总结,实现svg的内容主要分为一下4个步骤:

1. 处理参数属性

2. 将参数属性处理成动画数据

3. 设置帧

4. 动画引擎

猜你喜欢

转载自blog.csdn.net/qq_50497708/article/details/128429767