动画优化
匀速运动
将box匀速移动到屏幕右边界, box的右边框
边界处理问题
每次移动之前都判定,这次移动是否碰撞到边界, 如果碰撞到就将它的位置设置为边界.client - 盒子.offsetWidth; 否则移动一个步长
代码
<!DOCTYPE?html>
<html?lang="en">
??<head>
????<meta?charset="UTF-8"?/>
????<meta?name="viewport"?content="width=device-width,?initial-scale=1.0"?/>
????<title>Document</title>
????<style?type="text/css">
??????*?{
????????margin:?0;
????????padding:?0;
??????}
??????#box?{
????????position:?absolute;
????????left:?0px;
????????width:?100px;
????????height:?100px;
????????background-color:?lightblue;
????????padding:?10px;
??????}
??????#box2?{
????????position:?absolute;
????????left:?0px;
????????width:?100px;
????????height:?100px;
????????background-color:?lightblue;
????????padding:?10px;
??????}
??????div?{
????????height:?200px;
??????}
????</style>
??</head>
??<body>
????<div><div?id="box"></div></div>
????<div><div?id="box2"></div></div>
??</body>
??<script?type="text/javascript">
????var?oBox?=?document.getElementById("box");
????var?maxLeft?=
??????(document.documentElement.clientWidth?||?document.body.clientWidth)?-
??????oBox.clientWidth;
????var?duration?=?2000;
????var?setpt?=?(maxLeft?/?duration)?*?10;
????var?timer?=?window.setInterval(function?()?{
??????var?curLeft?=?window.getComputedStyle(oBox,?null).left;
??????var?left?=?parseInt(curLeft);
??????console.log(curLeft);
??????if?(left?+?setpt?>=?maxLeft)?{
????????window.clearInterval(timer);
????????oBox.style.left?=
??????????document.documentElement.clientWidth?-?oBox.offsetWidth?+?"px";
????????return;
??????}
??????left?+=?setpt;
??????console.log(left);
??????oBox.style.left?=?left?+?"px";
????},?10);
????function?Linear(t,?b,?c,?d)?{
??????return?(c?*?t)?/?d?+?b;
????}
????var?oBox2?=?document.getElementById("box2");
????var?begin?=?parseInt(window.getComputedStyle(oBox2,?null).left);
????var?target?=
??????(document.documentElement.clientWidth?||?document.body.clientWidth)?-
??????oBox.clientWidth;
????var?change?=?parseFloat(target)?-?parseFloat(begin);
????var?duration?=?2000;
????var?time?=?null;
????var?timer2?=?setInterval(function?()?{
??????time?+=?10;
??????if?(time?>=?duration)?{
????????clearInterval(timer2);
????????return;
??????}
??????var?curPos?=?Linear(time,?begin,?change,?duration);
??????console.log(time,?begin,?change,?duration);
??????console.log("curPos",?curPos);
??????box2.style.left?=?curPos?+?"px";
????},?10);
??</script>
</html>
setTimeout实现轮循动画
使用递归思想完成setTimeout的轮循动画: 每一次在执行动画之前首先把上一次设置的那个没有用的定时器清除掉, 节约我们的内存空间
代码
<script?type="text/javascript">
????var?oBox?=?document.getElementById("box");
????var?maxLeft?=
??????(document.documentElement.clientWidth?||?document.body.clientWidth)?-
??????oBox.clientWidth;
????var?duration?=?2000;
????var?setpt?=?(maxLeft?/?duration)?*?10;
????var?timer?=?window.setInterval(function?()?{},?10);
????var?timer?=?null;
????function?move()?{
??????clearTimeout(timer);
??????var?curLeft?=?window.getComputedStyle(oBox,?null).left;
??????var?left?=?parseInt(curLeft);
??????console.log(curLeft);
??????if?(left?+?setpt?>=?maxLeft)?{
????????window.clearTimeout(timer);
????????oBox.style.left?=
??????????document.documentElement.clientWidth?-?oBox.offsetWidth?+?"px";
????????return;
??????}
??????left?+=?setpt;
??????console.log(left);
??????oBox.style.left?=?left?+?"px";
??????timer?=?setTimeout(move,?10);
????}
????move();
??</script>
实现反弹动画(作用域积累优化问题)
<!DOCTYPE?html>
<html?lang="en">
??<head>
????<meta?charset="UTF-8"?/>
????<meta?name="viewport"?content="width=device-width,?initial-scale=1.0"?/>
????<title>Document</title>
????<style?type="text/css">
??????*?{
????????margin:?0;
????????padding:?0;
??????}
??????#box?{
????????position:?absolute;
????????left:?0px;
????????width:?100px;
????????height:?100px;
????????background-color:?lightblue;
????????padding:?10px;
??????}
??????#box2?{
????????position:?absolute;
????????left:?0px;
????????width:?100px;
????????height:?100px;
????????background-color:?lightblue;
????????padding:?10px;
??????}
??????div?{
????????height:?200px;
??????}
??????.btn?{
????????position:?absolute;
????????top:?200px;
????????left:?100px;
????????height:?50px;
??????}
??????.btn?input?{
????????outline:?none;
????????display:?inline-block;
????????margin-left:?50px;
????????width:?100px;
????????height:?50px;
????????border:?1px?solid?green;
????????cursor:?pointer;
??????}
????</style>
??</head>
??<body>
????<div><div?id="box"></div></div>
????<div?class="btn">
??????<input?type="button"?value="向左"?id="btnLeft"?/>
??????<input?type="button"?value="向右"?id="btnRight"?/>
????</div>
??</body>
??<script?type="text/javascript">
????var?oBox?=?document.getElementById("box");
????var?maxLeft?=
??????(document.documentElement.clientWidth?||?document.body.clientWidth)?-
??????oBox.clientWidth;
????var?minLeft?=?0;
????var?timer?=?null;
????function?move(target)?{
??????window.clearTimeout(timer);
??????var?curLeft?=?window.getComputedStyle(oBox,?null).left;
??????var?left?=?parseFloat(curLeft);
??????var?step?=?20;
??????if?(left?<?target)?{
????????//?向右走
????????if?(left?+?step?>=?target)?return;
????????left?+=?step;
????????oBox.style.left?=?left?+?"px";
??????}?else?if?(left?>?target)?{
????????//?向左走
????????if?(left?+?step?<=?target)?return;
????????left?-=?step;
????????oBox.style.left?=?left?+?"px";
??????}?else?{
????????//?不需要运动
??????}
??????timer?=?window.setTimeout(function?()?{
????????move(target);
??????},?10);
????}
????document.getElementById("btnLeft").onclick?=?function?()?{
??????move(minLeft);
????};
????document.getElementById("btnRight").onclick?=?function?()?{
??????move(maxLeft);
????};
??</script>
</html>
性能问题
这样写性能不好, 因为每一次到达时间的时候, 都需要先执行一次匿名函数(形成一个私有的作用域), 在匿名函数中在执行move, 但是move中需要用到的数据在第一次执行的move方法中, 需要把匿名函数形成的这个私有的作用域作为跳板找到之前的,这样就导致了匿名函数形成的这个私有的作用域不能被销毁。。。
解决
_move下的私有变量没有被其他地方引用,所以_move执行完栈销毁。
当所有_move执行完都被销毁时,move中的target没有被其他地方引用,move形成的执行栈也销毁。
function?move(target)?{
??????function?_move()?{
????????window.clearTimeout(timer);
????????var?curLeft?=?window.getComputedStyle(oBox,?null).left;
????????var?left?=?parseFloat(curLeft);
????????var?step?=?20;
????????if?(left?<?target)?{
??????????//?向右走
??????????if?(left?+?step?>=?target)?return;
??????????left?+=?step;
??????????oBox.style.left?=?left?+?"px";
????????}?else?if?(left?>?target)?{
??????????//?向左走
??????????if?(left?+?step?<=?target)?return;
??????????left?-=?step;
??????????oBox.style.left?=?left?+?"px";
????????}?else?{
??????????//?不需要运动
????????}
????????timer?=?window.setTimeout(function?()?{
??????????_move();
????????},?10);
??????}
??????_move();
????}
保证当前元素同一时间只能执行一个动画
为了让当前的元素在同一个时间只运行一个动画(下一个动画开始的时候首先把上一个动画的定时器清除掉):保证当前元素所有的动画接受定时器返回值的那个变量需要共享(把这个值放在当前元素的自定义属性上)
代码
function?move(target)?{
??????function?_move()?{
????????window.clearTimeout(oBox._timer);
????????var?curLeft?=?window.getComputedStyle(oBox,?null).left;
????????var?left?=?parseFloat(curLeft);
????????var?step?=?20;
????????if?(left?<?target)?{
??????????//?向右走
??????????if?(left?+?step?>=?target)?return;
??????????left?+=?step;
??????????oBox.style.left?=?left?+?"px";
????????}?else?if?(left?>?target)?{
??????????//?向左走
??????????if?(left?+?step?<=?target)?return;
??????????left?-=?step;
??????????oBox.style.left?=?left?+?"px";
????????}?else?{
??????????//?不需要运动
????????}
????????oBox._timer?=?window.setTimeout(function?()?{
??????????_move();
????????},?10);
??????}
??????_move();
}
JS实现高性能动画法则
- 边界判断+步长
- 清除没有用的定时器
- 大move(target),里面写一个_move(),避免作用域的累积
- 把定时器的返回值放到自定义属性上,避免同一个元素在同一时间执行多个动画
实现多方向匀速运动动画
简单版代码
<!--
?*?@Author:?lemon
?*?@Date:?2020-09-16?11:40:20
?*?@LastEditTime:?2020-09-16?12:15:51
?*?@LastEditors:?Please?set?LastEditors
?*?@Description:?In?User?Settings?Edit
?*?@FilePath:?\React前端准备\CSS基础\动画\简单匀速运动动画库.html
-->
<!DOCTYPE?html>
<html?lang="en">
??<head>
????<meta?charset="UTF-8"?/>
????<meta?name="viewport"?content="width=device-width,?initial-scale=1.0"?/>
????<title>Document</title>
????<style?type="text/css">
??????*?{
????????margin:?0;
????????padding:?0;
??????}
??????#box?{
????????position:?absolute;
????????left:?0px;
????????width:?100px;
????????height:?100px;
????????background-color:?lightblue;
????????padding:?10px;
????????opacity:?1;
????????filter:?alpha(opacity=100);
??????}
??????div?{
????????height:?200px;
??????}
??????.btn?{
????????position:?absolute;
????????top:?200px;
????????left:?100px;
????????height:?50px;
??????}
??????.btn?input?{
????????outline:?none;
????????display:?inline-block;
????????margin-left:?50px;
????????width:?100px;
????????height:?50px;
????????border:?1px?solid?green;
????????cursor:?pointer;
??????}
????</style>
??</head>
??<body>
????<div><div?id="box"></div></div>
????<div?class="btn">
??????<input?type="button"?value="向左"?id="btnLeft"?/>
??????<input?type="button"?value="向右"?id="btnRight"?/>
????</div>
??</body>
??<script?type="text/javascript">
????function?setCSS(curEle,?style)?{
??????for?(var?key?in?style)?{
????????if?(curEle.hasOwnProperty(key))?{
??????????curEle.style[key]?=?style[key];
????????}
??????}
????}
????(function?()?{
??????var?effect?=?{
????????Linear:?function?(t,?b,?c,?d)?{
??????????return?(c?*?t)?/?d?+?b;
????????},
??????};
??????//?move:实现多方向的运动动画??curEle:当前要操作运动的元素?target:当前动画的目标位置,?存储每一个方向的目标位置{left:xxx,top:xxx,xxx}
??????//?duration:当前动画的总时间
??????function?move(curEle,?target,?duration)?{
????????//?在每一次执行方法之前首先把元素之前正在运行的动画结束掉
????????window.clearInterval(curEle._timer);
????????//?根据target获取每一个方向的起始值和总距离change
????????var?begin?=?{},
??????????change?=?{};
????????for?(var?key?in?target)?{
??????????if?(target.hasOwnProperty(key))?{
????????????begin[key]?=?parseFloat(window.getComputedStyle(curEle,?null).key);
????????????if?(isNaN(begin[key]))?{
??????????????begin[key]?=?0;
????????????}
????????????change[key]?=?target[key]?-?begin[key];
??????????}
????????}
????????console.log(begin);
????????var?time?=?0;
????????curEle._timer?=?window.setInterval(function?()?{
??????????time?+=?10;
??????????//?到达目标:?结束动画,?让当前元素的样式等于目标央视值
??????????if?(time?>=?duration)?{
????????????setCSS(curEle,?target);
????????????window.clearInterval(curEle._timer);
????????????return;
??????????}
??????????//?没达到目标:?分别的获取每一个方向的当前位置,?给当前元素设置样式即可
??????????for?(var?key?in?target)?{
????????????if?(target.hasOwnProperty(key))?{
??????????????var?curPos?=?effect.Linear(
????????????????time,
????????????????begin[key],
????????????????change[key],
????????????????duration
??????????????);
??????????????console.log(begin[key],?change[key]);
??????????????curEle.style[key]?=?curPos?+?"px";
????????????}
??????????}
??????????console.log(curPos);
????????},?10);
??????}
??????window.Animate?=?move;
????})();
????var?oBox?=?document.getElementById("box");
????Animate(
??????oBox,
??????{
????????left:?1000,
????????top:?500,
????????opacity:?0,
????????width:?0,
????????height:?0,
??????},
??????1000
????);
??</script>
</html>
支持回调函数
.....
??if?(time?>=?duration)?{
????????????setCSS(curEle,?target);
????????????window.clearInterval(curEle._timer);
????????????if?(typeof?callBack?===?"function")?{
??????????????callBack.call(curEle);
????????????}
????????????return;
??????????}
......
?Animate(
??????oBox,
??????{
????????left:?1000,
????????top:?500,
????????opacity:?0,
????????width:?0,
????????height:?0,
??????},
??????1000,
??????function?()?{
????????console.log(this);
????????this.style.background?=?"red";
??????}
);