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

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。更多学习资料请访问我爱科技论坛:www.52tech.tech https://blog.csdn.net/m0_37981569/article/details/79659313 ...
版权声明:本文为博主原创文章,未经博主允许不得转载。更多学习资料请访问我爱科技论坛:www.52tech.tech https://blog.csdn.net/m0_37981569/article/details/79659313
<!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.setTimeout和setInterval深入理解-->
    <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
            //假设10px是1米,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();
                        }
                    }
                    // 如果next是true执行下一帧动画
                    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>
相关文章
|
JavaScript 前端开发
如何使用时间切片来优化JavaScript动画的性能?
如何使用时间切片来优化JavaScript动画的性能?
612 158
|
JavaScript 前端开发
如何在不影响性能的前提下使用JavaScript库来实现复杂的动画效果?
如何在不影响性能的前提下使用JavaScript库来实现复杂的动画效果?
699 156
|
JavaScript 前端开发
CSS3 动画和 JavaScript 动画的性能比较
具体的性能表现还会受到许多因素的影响,如动画的复杂程度、浏览器的性能、设备的硬件条件等。在实际应用中,需要根据具体情况选择合适的动画技术。
601 154
|
前端开发 API 开发者
Next.js 实战 (五):添加路由 Transition 过渡效果和 Loading 动画
这篇文章介绍了Framer Motion,一个为React设计的动画库,提供了声明式API处理动画和页面转换,适合创建响应式用户界面。文章包括首屏加载动画、路由加载Loading、路由进场和退场动画等主题,并提供了使用Framer Motion和next.js实现这些动画的示例代码。最后,文章总结了这些效果,并邀请读者探讨更好的实现方案。
593 0
Next.js 实战 (五):添加路由 Transition 过渡效果和 Loading 动画
|
JavaScript 前端开发
JS配合CSS3实现动画和拖动小星星小Demo
本文通过代码示例展示了如何使用JavaScript和CSS3实现动画效果和拖动小星星的交互效果,包括文字掉落动画和鼠标拖动产生小星星动画的实现方法。
431 1
JS配合CSS3实现动画和拖动小星星小Demo
|
JavaScript 前端开发 UED
Vue.js动画魔法:解锁流畅过渡,让每一次交互都成为用户心中的小确幸!
【8月更文挑战第30天】在Vue.js中,动画与过渡效果不仅是视觉点缀,更是提升用户体验的关键。通过流畅的动态效果,应用的互动性和吸引力得以增强,从而提高用户满意度和参与度。`&lt;transition&gt;`和`&lt;transition-group&gt;`组件结合CSS过渡,可轻松实现元素的进入、离开及列表变化动画。合理的性能优化,如使用硬件加速,能避免页面卡顿,确保动画既美观又高效。下面是一个简单的淡入淡出效果示例,展示了如何利用Vue.js实现平滑的动画过渡。总之,恰当的动画设计能显著提升应用的用户体验。
526 1
Vue.js动画魔法:解锁流畅过渡,让每一次交互都成为用户心中的小确幸!
ractive.js联系表单动画效果源码
一款ractive.js联系表单动画效果,很有创意的发送邮件、联系内容等表单,基于ractive.js实现的动画效果,以发送信件的方式。
256 1
|
JavaScript
js动画循环播放特效源码(上班族的一天)
js动画循环播放特效是一段实现了包含形象的卡通小人吃、睡、电脑工作的网页动画,js循环动画,简单的画面设计。非常丝滑有意思,欢迎对此代码感兴趣的朋友前来下载参考。
276 2
|
前端开发 数据可视化 开发者
D3.js 内置的动画函数
D3.js 内置的动画函数
313 3
|
前端开发 JavaScript 安全
JavaScript进阶-JavaScript库与框架简介
【7月更文挑战第11天】JavaScript库和框架加速Web开发,但也带来挑战。选择适合项目、团队技能的库或框架,如React、Angular、Vue,是关键。保持依赖更新,注意性能优化,避免过度依赖。遵循最佳实践,确保安全性,如防XSS和CSRF。学习基础,结合代码示例(如React计数器组件),提升开发效率和应用质量。
283 1