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.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>
本文来自高热度网,作者:高热度网,转载请注明原文链接:https://www.cnblogs.com/52tech/p/9325102.html