• window.requestAnimationFrame与Tween.js配合使用实现动画缓动效果


    window.requestAnimationFrame

    概述

    window.requestAnimationFrame()这个方法是用来在页面重绘之前,通知浏览器调用一个指定的函数,以满足开发者操作动画的需求。这个方法接受一个函数为参,该函数会在重绘前调用。

    注意: 如果想得到连贯的逐帧动画,函数中必须重新调用 requestAnimationFrame()。

    如果你想做逐帧动画的时候,你应该用这个方法。这就要求你的动画函数执行会先于浏览器重绘动作。通常来说,被调用的频率是每秒60次,但是一般会遵循W3C标准规定的频率。如果是后台标签页面,重绘频率则会大大降低。

    回调函数只会被传入一个DOMHighResTimeStamp参数,这个参数指示当前被 requestAnimationFrame 序列化的函数队列被触发的时间。因为很多个函数在这一帧被执行,所以每个函数都将被传入一个相同的时间戳,尽管经过了之前很多的计算工作。这个数值是一个小数,单位毫秒,精确度在 10 µs。

    语法

    requestID = window.requestAnimationFrame(callback);               // Firefox 23 / IE10 / Chrome / Safari 7 (incl. iOS)
    requestID = window.mozRequestAnimationFrame(callback);                // Firefox < 23
    requestID = window.webkitRequestAnimationFrame(callback); // Older versions Chrome/Webkit

    参数

    callback在每次需要重新绘制动画时,会调用这个参数所指定的函数。这个回调函数会收到一个参数,这个 DOMHighResTimeStamp 类型的参数指示当前时间距离开始触发 requestAnimationFrame 的回调的时间。

    返回值

    requestID 是一个长整型非零值,作为一个唯一的标识符.你可以将该值作为参数传给 window.cancelAnimationFrame() 来取消这个回调函数。

    例子

    window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
    
    var start = null;
    var d = document.getElementById('SomeElementYouWantToAnimate');
    function step(timestamp) { 
      if (start === null) start = timestamp;
      var progress = timestamp - start;
      d.style.left = Math.min(progress/10, 200) + "px";
      if (progress < 2000) {
        requestAnimationFrame(step);
      }
    }
    requestAnimationFrame(step);

    Tween.js

    tween.js源码如下:

    /*
     * Tween.js
     * t: current time(当前时间);
     * b: beginning value(初始值);
     * c: change in value(变化量);
     * d: duration(持续时间)。
     * you can visit 'http://easings.net/zh-cn' to get effect
    */
    var Tween = {
        Linear: function(t, b, c, d) { return c*t/d + b; },
        Quad: {
            easeIn: function(t, b, c, d) {
                return c * (t /= d) * t + b;
            },
            easeOut: function(t, b, c, d) {
                return -c *(t /= d)*(t-2) + b;
            },
            easeInOut: function(t, b, c, d) {
                if ((t /= d / 2) < 1) return c / 2 * t * t + b;
                return -c / 2 * ((--t) * (t-2) - 1) + b;
            }
        },
        Cubic: {
            easeIn: function(t, b, c, d) {
                return c * (t /= d) * t * t + b;
            },
            easeOut: function(t, b, c, d) {
                return c * ((t = t/d - 1) * t * t + 1) + b;
            },
            easeInOut: function(t, b, c, d) {
                if ((t /= d / 2) < 1) return c / 2 * t * t*t + b;
                return c / 2*((t -= 2) * t * t + 2) + b;
            }
        },
        Quart: {
            easeIn: function(t, b, c, d) {
                return c * (t /= d) * t * t*t + b;
            },
            easeOut: function(t, b, c, d) {
                return -c * ((t = t/d - 1) * t * t*t - 1) + b;
            },
            easeInOut: function(t, b, c, d) {
                if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
                return -c / 2 * ((t -= 2) * t * t*t - 2) + b;
            }
        },
        Quint: {
            easeIn: function(t, b, c, d) {
                return c * (t /= d) * t * t * t * t + b;
            },
            easeOut: function(t, b, c, d) {
                return c * ((t = t/d - 1) * t * t * t * t + 1) + b;
            },
            easeInOut: function(t, b, c, d) {
                if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b;
                return c / 2*((t -= 2) * t * t * t * t + 2) + b;
            }
        },
        Sine: {
            easeIn: function(t, b, c, d) {
                return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
            },
            easeOut: function(t, b, c, d) {
                return c * Math.sin(t/d * (Math.PI/2)) + b;
            },
            easeInOut: function(t, b, c, d) {
                return -c / 2 * (Math.cos(Math.PI * t/d) - 1) + b;
            }
        },
        Expo: {
            easeIn: function(t, b, c, d) {
                return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
            },
            easeOut: function(t, b, c, d) {
                return (t==d) ? b + c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
            },
            easeInOut: function(t, b, c, d) {
                if (t==0) return b;
                if (t==d) return b+c;
                if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
                return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
            }
        },
        Circ: {
            easeIn: function(t, b, c, d) {
                return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
            },
            easeOut: function(t, b, c, d) {
                return c * Math.sqrt(1 - (t = t/d - 1) * t) + b;
            },
            easeInOut: function(t, b, c, d) {
                if ((t /= d / 2) < 1) return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
                return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
            }
        },
        Elastic: {
            easeIn: function(t, b, c, d, a, p) {
                var s;
                if (t==0) return b;
                if ((t /= d) == 1) return b + c;
                if (typeof p == "undefined") p = d * .3;
                if (!a || a < Math.abs(c)) {
                    s = p / 4;
                    a = c;
                } else {
                    s = p / (2 * Math.PI) * Math.asin(c / a);
                }
                return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
            },
            easeOut: function(t, b, c, d, a, p) {
                var s;
                if (t==0) return b;
                if ((t /= d) == 1) return b + c;
                if (typeof p == "undefined") p = d * .3;
                if (!a || a < Math.abs(c)) {
                    a = c; 
                    s = p / 4;
                } else {
                    s = p/(2*Math.PI) * Math.asin(c/a);
                }
                return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b);
            },
            easeInOut: function(t, b, c, d, a, p) {
                var s;
                if (t==0) return b;
                if ((t /= d / 2) == 2) return b+c;
                if (typeof p == "undefined") p = d * (.3 * 1.5);
                if (!a || a < Math.abs(c)) {
                    a = c; 
                    s = p / 4;
                } else {
                    s = p / (2  *Math.PI) * Math.asin(c / a);
                }
                if (t < 1) return -.5 * (a * Math.pow(2, 10* (t -=1 )) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
                return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p ) * .5 + c + b;
            }
        },
        Back: {
            easeIn: function(t, b, c, d, s) {
                if (typeof s == "undefined") s = 1.70158;
                return c * (t /= d) * t * ((s + 1) * t - s) + b;
            },
            easeOut: function(t, b, c, d, s) {
                if (typeof s == "undefined") s = 1.70158;
                return c * ((t = t/d - 1) * t * ((s + 1) * t + s) + 1) + b;
            },
            easeInOut: function(t, b, c, d, s) {
                if (typeof s == "undefined") s = 1.70158; 
                if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
                return c / 2*((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
            }
        },
        Bounce: {
            easeIn: function(t, b, c, d) {
                return c - Tween.Bounce.easeOut(d-t, 0, c, d) + b;
            },
            easeOut: function(t, b, c, d) {
                if ((t /= d) < (1 / 2.75)) {
                    return c * (7.5625 * t * t) + b;
                } else if (t < (2 / 2.75)) {
                    return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
                } else if (t < (2.5 / 2.75)) {
                    return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
                } else {
                    return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
                }
            },
            easeInOut: function(t, b, c, d) {
                if (t < d / 2) {
                    return Tween.Bounce.easeIn(t * 2, 0, c, d) * .5 + b;
                } else {
                    return Tween.Bounce.easeOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
                }
            }
        }
    }
    Math.tween = Tween;

    简介

    动画运动算法名称如下:

    1. Linear:线性匀速运动效果;
    2. Quadratic:二次方的缓动(t^2);
    3. Cubic:三次方的缓动(t^3);
    4. Quartic:四次方的缓动(t^4);
    5. Quintic:五次方的缓动(t^5);
    6. Sinusoidal:正弦曲线的缓动(sin(t));
    7. Exponential:指数曲线的缓动(2^t);
    8. Circular:圆形曲线的缓动(sqrt(1-t^2));
    9. Elastic:指数衰减的正弦曲线缓动;
    10. Back:超过范围的三次方缓动((s+1)*t^3 – s*t^2);
    11. Bounce:指数衰减的反弹缓动。

    每个效果都分三个缓动方式,分别是:

    • easeIn:从0开始加速的缓动,也就是先慢后快;
    • easeOut:减速到0的缓动,也就是先快后慢;
    • easeInOut:前半段从0开始加速,后半段减速到0的缓动。

    所有的这些缓动算法都离不开下面4个参数,t, b, c, d,含义如下:

    /*
     * t: current time(当前时间);
     * b: beginning value(初始值);
     * c: change in value(变化量);
     * d: duration(持续时间)。
    */

    下面用最简单的线性匀速运动来解释下:

    Tween.Linear = function(t, b, c, d) { 
        return c*t/d + b; 
    }

    比方说我们要从位置0的地方运动到100,时间是10秒钟,此时,b, c, d三个参数就已经确认了,b初始值就是0,变化值c就是100-0就是100,最终的时间就是10,此时,只要给一个小于最终时间10的值,Tween.Linear就会返回当前时间应该的坐标,例如,假设此时动画进行到第5秒,也就是t为5,则得到(截图自Chrome控制台):

    如何实际使用Tween.js中的缓动算法?

    兼容写法:

    window.requestAnimFrame = (function (callback,time) {
        return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimaitonFrame ||
            function (callback) {
                window.setTimeout(callback, time);
            };
    })();

    我们要显示一个动画效果,例如,还是拿上面的线性效果举例,则代码可以变成:

    var t = 0, b = 0, c = 100, d = 10;
    var step = function () {
        // value就是当前的位置值
        // 例如我们可以设置DOM.style.left = value + 'px'实现定位
        var value = Tween.Linear(t, b, c, d);
        t++;
        if (t <= d) {
             // 继续运动
             requestAnimationFrame(step);
        } else {
            // 动画结束
        }
    };

    基本上,所有的动画使用都是这个套路。

    参考地址

    缓动函数速查表:http://easings.net/zh-cn

    [翻译]使用requestAnimationFrame实现炫目的动画

    http://www.zhangxinxu.com/wordpress/2016/12/how-use-tween-js-animation-easing/

  • 相关阅读:
    ios设备new Date('2019-07-26 11:00:00')报错
    vue图片压缩(不失真)
    ios微信端网页点击右上角复制链接或在浏览器打开,得不到当前页地址(动态路由)
    ios打开网页,输入框获取焦点键盘弹起,关闭键盘,界面下方空白不回弹
    vue-cli打包后vendor.js文件太大怎么办
    ios点击有300毫秒延迟,输入框必须重压或长按才能获取焦点唤起软键盘
    ios微信端上下滑动页面,若触摸的是input区域,页面内不滚动,整个页面被拖动了
    vue-cli打包优化之分析工具webpack-bundle-analyzer
    vue引入fastclick设置输入框type="number"报错Failed to execute 'setSelectionRange' on 'HTMLInputElement': The input element's type ('number') does not support selection.的解决办法
    js/vue图片压缩
  • 原文地址:https://www.cnblogs.com/moqiutao/p/6393007.html
Copyright © 2020-2023  润新知