• JavaScript进阶【五】利用JavaScript实现动画的基本思路


    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            #div {
                width: 100px;
                height: 100px;
                background-color: #00a8c6;
                position: absolute;
            }
    
            #block {
                width: 80px;
                height: 80px;
                background-color: #0a001f;
                position: absolute;
                margin-left: 120px;
            }
    
            #blockTwo {
                width: 80px;
                height: 80px;
                background-color: #00FA9A;
                position: absolute;
                margin-left: 190px;
            }
    
            #ball {
                width: 100px;
                height: 100px;
                border-radius: 50px;
                border: solid rgb(100, 100, 100) 1px;
                position: absolute;
                margin-top: 100px;
                background-color: #1b1b1b;
            }
    
            #car {
                width: 80px;
                height: 80px;
                background-color: #4B0082;
                position: absolute;
                margin-left: 220px;
            }
    
            #stone {
                width: 50px;
                height: 50px;
                border-radius: 25px;
                border: solid rgb(100, 100, 100) 1px;
                position: absolute;
                margin-top: 150px;
                margin-left: 150px;
                background-color: #8B0000;
            }
    
            #SinMove {
                width: 50px;
                height: 50px;
                border-radius: 25px;
                border: solid rgb(100, 100, 100) 1px;
                position: absolute;
                margin-top: 800px;
                margin-left: 220px;
                background-color: #00FA9A;
            }
    
            #CircleMove {
                width: 50px;
                height: 50px;
                background-color: #4B0082;
                margin-top: 500px;
                margin-left: 500px;
            }
    
            #moveAny {
                width: 100px;
                height: 100px;
                border-radius: 50px;
                border: solid rgb(100, 100, 100) 1px;
                position: absolute;
                margin-top: 800px;
                margin-left: 220px;
                background-color: #7FFF00;
                text-align: center;
                padding-top: 20px;
            }
    
            #controlPan {
                margin-top: 400px;
                border: 2px solid blue;
                height: 400px;
                width: auto;
            }
        </style>
    
        <!--1.实现一个简单的div块元素-->
        <script>
            var id;
    
            var direct = -1;
            var times = 0;
    
            function step() {
                times++;
    
                // 获取这个块元素
                var div = document.getElementById("div");
    
                console.log(div.offsetLeft);
                if (div.offsetLeft == 800) {
                    direct = 0;
                } else if (div.offsetLeft == 8) {
                    direct = 1;
                }
    
                if (direct == 1) {
                    // 设置这个div元素向左的偏移量
                    var temp = div.offsetLeft + 2;
                    // 设置坐标的距离
                    div.style.left = temp + "px";
                } else if (direct == 0) {
                    // 设置这个div元素向左的偏移量
                    var temp = div.offsetLeft - 2;
                    // 设置坐标的距离
                    div.style.left = temp + "px";
                }
    
                //setTimeout一样,要手动调用才能实现连续动画。
                id = window.requestAnimationFrame(step);  //返回值是一个id,可以通过这个id来取消
    
                // 复杂的计算
                for (var i = 0; i < 50; i++) {
                    console.log("再牛逼的定时器也得等到我执行完才能执行");
                }
    
    
                //取消回调函数
                if (times == 0) {
                    window.cancelAnimationFrame(id);
                }
    
            }
    
            // 第一次调用
            //id = window.requestAnimationFrame(step);
    
    
        </script>
    
        <!--2.setTimeoutsetInterval深入理解-->
        <script>
            /*console.log("1");
            setTimeout(function () {
                console.log("3");
            }, 0);
            console.log("2");  // 输出: 1 2 3*/
            // 2. 使用动画的正确姿势,动画其实是位移关于时间的函数:s=f(t)
            // 解决动画变慢的问题:把动画与时间关联起来
            function startAnimation() {
                var startTime = Date.now();
                requestAnimationFrame(function change(time) {
                    var current = Date.now() - startTime;
                    console.log("动画已经执行的时间为:" + current);
                    requestAnimationFrame(startAnimation);
                });
            }
    
            //startAnimation();
    
            //  动画通常情况下有终止时间,如果是循环动画,我们也可以看做特殊的——当动画达到终止时间之后,重新开始动画。因此,我们可以将动画时间归一(Normalize)表示:
            //duration 是动画执行时间   isLoop是否为循环执行。
            function startAnimation(duration, isLoop) {
                var startTime = Date.now();
    
                requestAnimationFrame(function change() {
                    // 动画已经用去的时间占总时间的比值
                    var p = (Date.now() - startTime) / duration;
                    //console.log(p);
    
                    if (p >= 1.0) {
                        if (isLoop) { // 如果是循环执行,则开启下一个循环周期。并且把开始时间改成上个周期的结束时间
                            startTime += duration;
                            p -= 1.0; //动画进度初始化
                        } else {
                            p = 1.0;    //如果不是循环,则把时间进度至为 1.0 表示动画执行结束
                        }
                    }
                    console.log("动画已执行进度", p);
                    if (p < 1.0) { //如果小于1.0表示动画还诶有值完毕,继续执行动画。
                        requestAnimationFrame(change);
                    }
                });
            }
    
            //startAnimation(100, true);
    
    
            // 示例1:用时间控制动画周期精确到2s        function blockClick(obj) {
                var block = document.getElementById("block");
    
    
                var self = obj,
                    startTime = Date.now(),
                    duration = 2000;
                // 添加一个动画
                setInterval(function () {
                    var p = (Date.now() - startTime) / duration;
                    // 时间已经完成了2000的比例,则360度也是进行了这么个比例。
                    self.style.transform = "rotate(" + (360 * p) + "deg)";
                }, 100);
    
            }
    
            // 示例2:让滑块在2秒内向右匀速移动600px
            function blockTwoClick(obj) {
                var self = obj;
                var startTime = Date.now(),
                    distance = 600,
                    duration = 2000;
    
                requestAnimationFrame(function step(time) {
                    var p = Math.min(1.0, (Date.now() - startTime) / duration);
                    //沿着X方向运动
                    self.style.transform = "translateX(" + (distance * p) + "px)";
                    // 如果动画没有执行完毕, 就继续执行
                    if (p < 1.0) {
                        requestAnimationFrame(step);
                    }
                })
            };
    
            // 实例3: 实现小球的自由落体运动
            function ballClick(obj) {
                var self = obj,
                    startTime = Date.now(),
                    distance = 1000,
                    duration = 1500;
                requestAnimationFrame(function step() {
                    var p = Math.min(1.0, (Date.now() - startTime) / duration);
                    self.style.transform = "translateY(" + (distance * p * p) + "px)";
                    if (p < 1.0) requestAnimationFrame(step);
                });
    
            }
    
    
            // 实例4 : 实现汽车的匀减速运动
            function carClick(obj) {
                var self = obj, startTime = Date.now(),
                    distance = 1000, duration = 2000;
                requestAnimationFrame(function step() {
                    var p = Math.min(1.0, (Date.now() - startTime) / duration);
                    self.style.transform = "translateX("
                        + (distance * p * (2 - p)) + "px)";
                    if (p < 1.0) requestAnimationFrame(step);
                });
    
            }
    
            // 实例5 : 水平抛物运动
            function stoneClick(obj) {
                var self = obj, startTime = Date.now(),
                    disX = 1000, disY = 1000,
                    duration = Math.sqrt(2 * disY / 10 / 9.8) * 1000;   // 落到地面需要的时间  单位ms
                //假设10px1米,disY = 100
                requestAnimationFrame(function step() {
                    var p = Math.min(1.0, (Date.now() - startTime) / duration);
                    var tx = disX * p;  //水平方向是匀速运动
                    var ty = disY * p * p;  //垂直方向是匀加速运动
    
                    self.style.transform = "translate("
                        + tx + "px" + "," + ty + "px)";
                    if (p < 1.0) requestAnimationFrame(step);
                });
    
            }
    
    
            // 实例6 : 正弦曲线运动
            function SinMoveClick(obj) {
                var self = obj, startTime = Date.now(),
                    distance = 800,
                    duration = 5000;
    
                requestAnimationFrame(function step() {
                    var p = Math.min(1.0, (Date.now() - startTime) / duration);
                    var ty = distance * Math.sin(2 * Math.PI * p);
                    var tx = 2 * distance * p;
    
                    self.style.transform = "translate("
                        + tx + "px," + ty + "px)";
                    if (p < 1.0) requestAnimationFrame(step);
                });
    
            }
    
            // 实例7 : 圆周运动
            function CircleMoveClick(obj) {
                var self = obj,
                    startTime = Date.now(),
                    r = 100,
                    duration = 2000;
    
                requestAnimationFrame(function step() {
                    var p = Math.min(1.0, (Date.now() - startTime) / duration);
                    var tx = r * Math.sin(2 * Math.PI * p),
                        ty = -r * Math.cos(2 * Math.PI * p);
    
                    self.style.transform = "translate(" +
                        tx + "px," + ty + "px)";
                    requestAnimationFrame(step);
                });
            }
    
    
            // 实现动画算子的封装
            // pow() 方法可返回 x  y 次幂的值
            var pow = Math.pow,
                BACK_CONST = 1.70158;
            // t指的的是动画进度  前面的p
            Easing = {
                // 匀速运动
                linear: function (t) {
                    return t;
                },
                // 加速运动
                easeIn: function (t) {
                    return t * t;
                },
                // 减速运动
                easeOut: function (t) {
                    return (2 - t) * t;
                },
                //先加速后减速
                easeBoth: function (t) {
                    return (t *= 2) < 1 ? .5 * t * t : .5 * (1 - (--t) * (t - 2));
                },
                // 4次方加速
                easeInStrong: function (t) {
                    return t * t * t * t;
                },
                // 4次方法的减速
                easeOutStrong: function (t) {
                    return 1 - (--t) * t * t * t;
                },
                // 先加速后减速,加速和减速的都比较剧烈
                easeBothStrong: function (t) {
                    return (t *= 2) < 1 ? .5 * t * t * t * t : .5 * (2 - (t -= 2) * t * t * t);
                },
                //
                easeOutQuart: function (t) {
                    return -(Math.pow((t - 1), 4) - 1)
                },
                // 指数变化 加减速
                easeInOutExpo: function (t) {
                    if (t === 0) return 0;
                    if (t === 1) return 1;
                    if ((t /= 0.5) < 1) return 0.5 * Math.pow(2, 10 * (t - 1));
                    return 0.5 * (-Math.pow(2, -10 * --t) + 2);
                },
                //指数式减速
                easeOutExpo: function (t) {
                    return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
                },
                // 先回弹,再加速
                swingFrom: function (t) {
                    return t * t * ((BACK_CONST + 1) * t - BACK_CONST);
                },
    
                // 多走一段,再慢慢的回弹
                swingTo: function (t) {
                    return (t -= 1) * t * ((BACK_CONST + 1) * t + BACK_CONST) + 1;
                },
    
                //弹跳
                bounce: function (t) {
                    var s = 7.5625,
                        r;
    
                    if (t < (1 / 2.75)) {
                        r = s * t * t;
                    } else if (t < (2 / 2.75)) {
                        r = s * (t -= (1.5 / 2.75)) * t + .75;
                    } else if (t < (2.5 / 2.75)) {
                        r = s * (t -= (2.25 / 2.75)) * t + .9375;
                    } else {
                        r = s * (t -= (2.625 / 2.75)) * t + .984375;
                    }
    
                    return r;
                }
            };
    
            /*
          参数1:动画的执行时间
          参数2:动画执行的时候的回调函数(动画执行的要干的事情)
          参数3:动画算子. 如果没有传入动画算子,则默认使用匀速算子
         */
            function Animator(duration, progress, easing) {
                this.duration = duration;
                this.progress = progress;
                this.easing = easing || function (p) {
                    return p
                };
            }
    
            Animator.prototype = {
                /*开始动画的方法,
                 参数:一个布尔值
                 true表示动画不循环执行。
                */
                start: function (finished) {
                    /*动画开始时间*/
                    var startTime = Date.now();
                    /*动画执行时间*/
                    var duration = this.duration,
                        self = this;
                    /*定义动画执行函数*/
                    requestAnimationFrame(function step() {
                        /*得到动画执行进度*/
                        var p = (Date.now() - startTime) / duration;
                        /*是否执行下一帧动画*/
                        var next = true;
                        /*判断动画进度是否完成*/
                        if (p < 1.0) {
                            self.progress(self.easing(p), p);   //执行动画回调函数,并传入动画算子的结果和动画进度。
                        } else {
                            if (finished) {  //判断是否停止动画。如果是true代表停止动画。
                                next = false;
                            } else {
                                startTime = Date.now();
                            }
                        }
                        // 如果nexttrue执行下一帧动画
                        if (next) requestAnimationFrame(step);
                    });
                }
            };
    
            function ChangeState() {
                var self = document.getElementById("moveAny");
                // 新建一个动画
                new Animator(2000, function (p) {
                    self.style.top = 500 * p + "px";
                }, Easing.easeBoth).start(false);
    
            }
    
        </script>
    </head>
    <body>
    <div style="border: 2px solid red">
        <!--动画实现方式:通过操作JavaScript间接操作CSS样式,每隔一段时间更新一次;-->
        <div id="div"></div>
    
        <div id="block" onclick="blockClick(this)"></div>
        <div id="blockTwo" onclick="blockTwoClick(this)"></div>
    
        <div id="ball" onclick="ballClick(this)"></div>
        <div id="car" onclick="carClick(this)"></div>
        <div id="stone" onclick="stoneClick(this)"></div>
        <div id="SinMove" onclick="SinMoveClick(this)"></div>
        <div id="CircleMove" onclick="CircleMoveClick(this)"></div>
    </div>
    
    
    <div id="moveAny">我是一个可以做任意运动的物体</div>
    <div id="controlPan">控制面板
        <button onclick="ChangeState(this)">切换运动状态</button>
    </div>
    </body>
    </html>
  • 相关阅读:
    【小程序】---- 使用 Echarts 的方式
    【小程序】---- 使用 Vant 的方式
    WebSocket协议 与 IO多路复用
    python 实现发送邮件功能
    记一次Hadoop安装部署过程
    docker容器中布置静态网站
    [DL]面向稀有事件的 Logistic Regression 模型校准
    [Statistic] 置信度
    Leetcode1137. 第 N 个泰波那契数
    Leetcode1394. 找出数组中的幸运数
  • 原文地址:https://www.cnblogs.com/52tech/p/9325102.html
Copyright © 2020-2023  润新知