原生JS实现贝塞尔曲线DOM元素动画

433 阅读1分钟

写在最前面

最近在复习原生JS,结合 setInterval贝塞尔曲线 实现元素的平滑过渡性效果。 先做了一个基础demo,后期再完善。

贝塞尔曲线 oy.easing.js

'use strict';

(function() {
    var OY = window.OY = {};
    var cubicBezier = function(p1x, p1y, p2x, p2y) {
        var cX = 3 * p1x,
            bX = 3 * (p2x - p1x) - cX,
            aX = 1 - cX - bX,
            cY = 3 * p1y,
            bY = 3 * (p2y - p1y) - cY,
            aY = 1 - cY - bY;

        var bezierX = function(t) {
            return t * (cX + t * (bX + t * aX));
        };
        var bezierXDerivative = function(t) {
            return cX + t * (2 * bX + 3 * aX * t);
        };

        var newtonRaphson = function(x) {
            var prev,
                // Initial estimation is linear
                t = x;
            do {
                prev = t;
                t = t - (bezierX(t) - x) / bezierXDerivative(t);
            } while (Math.abs(t - prev) > 1e-4);

            return t;
        };

        return function(x) {
            var t = newtonRaphson(x);
            return t * (cY + t * (bY + t * aY));
        };
    };
    
    // 可以根据自己的需求再添加
    OY.Easing = {
        ease: cubicBezier(0.25, 0.1, 0.25, 1),
        easeIn: cubicBezier(0.42, 0, 1, 1),
        easeOut: cubicBezier(0, 0, 0.58, 1),
        easeInOut: cubicBezier(0.42, 0, 0.58, 1)
    };
})();

基本使用

<img class="easing" src="./images/pikachu.jpeg">

<script src="js/oy.easing.js"></script>
<script>
'use strict';

let x = 0;
let easing = document.querySelector('.easing');
let id = setInterval(function() {
    if (x >= 1) {
        clearInterval(id);
        return;
    }
    easing.style.top = 200 * OY.Easing.easeInOut(x).toFixed(2) + 'px';
    x += 0.01;
}, 10);
</script>

生产使用

<img class="easing" src="./images/pikachu.jpeg">

<script src="js/oy.easing.js"></script>
<script>
'use strict';

function animate(el, render, duration, easing, callback) {
    let start = Date.now();
    (function loop() {
        let p = (Date.now() - start) / duration;
        if (p > 1) {
            render(1, el);
            callback && callback();
        } else {
            requestAnimationFrame(loop);
            render(easing(p), el);
        }
    })();
};

let easing = document.querySelector('.easing');
animate(
    easing,
    moveGo,
    1000,
    OY.Easing.easeInOut
);
function moveGo(p, el) {
    el.style.top = 200 * p + 'px';
}
</script>

参考资料

  1. www.moshplant.com/direct-or/b…
  2. overturejs.com/docs/animat…
  3. easings.net/