匀速动画的实现
使用offsetLeft获取left,用style.left设置left
使用计时器实现平滑的匀速动画
为了避免计时器重复,应保证一个对象对应一个计时器
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<style> *{ margin:0; padding:0; font-family:"微软雅黑"; } .box{ width:50px; height:50px; color:#fff; text-align:center; font-size:14px; line-height:50px; margin-top:20px; position:relative; } .box:nth-child(1){ background:#08f; left:50px; } .box:nth-child(2){ background:#f80; left:400px; } #ruler{ margin-top:40px; } #a div{ width:48.6px; height:7.6px; border:1.4px solid #333; border-width:0 1.4px 1.4px 0; float:left; } #a div:last-child{ border-width:0 0 1.4px 0; } #b div{ font-size:12px; font-weight:lighter; position:relative; top:-10px; float:left; } #b div:nth-child(1){left:90px;} #b div:nth-child(2){left:150px;} #b div:nth-child(3){left:220px;} #b div:nth-child(4){left:290px;} #b div:nth-child(5){left:360px;} #form{ background:#eee; font-size:14px; margin:20px 0; width:540px; padding:30px; } #form input:nth-Child(1){ width:50px; padding:3px; font-size:12px; font-weight:lighter; border:1.4px solid #666; } #form input:nth-Child(2){ border:1.4px solid #444; padding:3px 8px; font-size:12px; background:#666; color:#fff; } </style>
</head>
<body>
<div class="box">box1</div>
<div class="box">box2</div>
<div id="ruler">
<div id="a">
<div></div><div></div>
<div></div><div></div>
<div></div><div></div>
<div></div><div></div>
<div></div><div></div>
<div></div><div></div>
</div><br/>
<div id="b">
<div>100px</div>
<div>200px</div>
<div>300px</div>
<div>400px</div>
<div>500px</div>
</div>
<div id="form">
要移动到的坐标:
<input type="text" value="100" />
<input type="submit" value="移动" />
</div>
</div>
<script> var btn=document.getElementsByTagName("input")[1]; var target=document.getElementsByTagName("input")[0]; var box=document.getElementsByClassName("box")[0]; var box2=document.getElementsByClassName("box")[1]; btn.onclick=function(){ uniformSpeed(box,target.value); uniformSpeed(box2,target.value); }; function uniformSpeed(ele,target){ var speed=6; if(Math.abs(ele.offsetLeft-target) > speed){ clearInterval(ele.timer); //保证元素此动画计时器不重复 var dir=ele.offsetLeft<target ? 1 : -1; //确定运动方向 ele.timer=setInterval(function(){ ele.style.left=ele.offsetLeft+dir*speed+"px"; if(Math.abs(ele.offsetLeft-target)<=speed){ //快到达时微调到具体位置 ele.style.left=target+"px"; clearInterval(ele.timer); } },24); } } </script>
</body>
</html>
缓动动画的实现
要实现缓动效果,思路是元素每次移动1/10,移动幅度越来越小,达到缓动效果
注意:offset只能取整数位精度,要注意精度问题
将uniformSpeed函数进行以下改进:
function uniformSpeed(ele,target){
clearInterval(ele.timer); //保证元素此动画计时器不重复
var dir=ele.offsetLeft<target ? 1 : -1; //确定运动方向
ele.timer=setInterval(function(){
ele.style.left=ele.offsetLeft+dir*Math.ceil(Math.abs(ele.offsetLeft-target)/10)+"px"; //offset精度为整数位,移动幅度<1时会丢失小数,所以当<1时用Math.ceil将其改为1,直至达到目标
if(ele.offsetLeft==target){ //如果到了就清除计时器
clearInterval(ele.timer);
}
},24);
}
例子:导航固定和回到页首
导航固定:使用offsetHeight获取header的高,然后用scrollTop判断nav是否到了该固定的位置
回到页首:利用scrollBy(x,y)相对滚动,结合offset小节的缓动动画实现回到页首的缓动效果
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<style> *{ margin:0; padding:0; font-family:"微软雅黑"; color:#fff; font-size:30px; text-align:center; } div{ width:100%; } #header{ background:#aaa; line-height:100px; } #nav{ line-height:50px; background:#888; position:static; top:0; } #main{ line-height:200px; color:#888; } #toTop{ width:50px; height:50px; position:fixed; display:none; top:80%; left:5%; } </style>
</head>
<body>
<div id="header">header</div>
<div id="nav">nav</div>
<div id="main">main<br/>main<br/>main<br/>main<br/>main</div>
<!-- 图标非原创,来源:https://www.iconfont.cn/user/detail?spm=a313x.7781069.0.d214f71f6&uid=246734 -->
<svg id="toTop" xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 1024 1024" width="200" height="200" t="1565094469045" p-id="1475" version="1.1"><path fill="#0088ff" d="M 752.736 431.063 C 757.159 140.575 520.41 8.97 504.518 0.41 V 0 l -0.45 0.205 l -0.41 -0.205 v 0.41 c -15.934 8.56 -252.723 140.165 -248.259 430.653 c -48.21 31.457 -98.713 87.368 -90.685 184.074 c 8.028 96.666 101.007 160.768 136.601 157.287 c 35.595 -3.482 25.232 -30.31 25.232 -30.31 l 12.206 -50.095 s 52.47 80.569 69.304 80.528 c 15.114 -1.23 87 -0.123 95.6 0 h 0.82 c 8.602 -0.123 80.486 -1.23 95.6 0 c 16.794 0 69.305 -80.528 69.305 -80.528 l 12.165 50.094 s -10.322 26.83 25.272 30.31 c 35.595 3.482 128.574 -60.62 136.602 -157.286 c 8.028 -96.665 -42.475 -152.617 -90.685 -184.074 Z m -248.669 -4.26 c -6.758 -0.123 -94.781 -3.359 -102.891 -107.192 c 2.95 -98.714 95.97 -107.438 102.891 -107.93 c 6.964 0.492 99.943 9.216 102.892 107.93 c -8.11 103.833 -96.174 107.07 -102.892 107.192 Z m -52.019 500.531 c 0 11.838 -9.42 21.382 -21.012 21.382 a 21.217 21.217 0 0 1 -21.054 -21.34 V 821.74 c 0 -11.797 9.421 -21.382 21.054 -21.382 c 11.591 0 21.012 9.585 21.012 21.382 v 105.635 Z m 77.333 57.222 a 21.504 21.504 0 0 1 -21.34 21.626 a 21.504 21.504 0 0 1 -21.34 -21.626 V 827.474 c 0 -11.96 9.543 -21.668 21.299 -21.668 c 11.796 0 21.38 9.708 21.38 21.668 v 157.082 Z m 71.147 -82.043 c 0 11.796 -9.42 21.34 -21.053 21.34 a 21.217 21.217 0 0 1 -21.013 -21.34 v -75.367 c 0 -11.755 9.421 -21.299 21.013 -21.299 c 11.632 0 21.053 9.544 21.053 21.3 v 75.366 Z" p-id="1476" /></svg>
<script> var headerHeight=document.getElementById("header").offsetHeight; //头部高度 var nav=document.getElementById("nav"); var main=document.getElementById("main"); var toTop=document.getElementById("toTop"); //回到页首代码1: var toTopTimer=null; //计时器声明 toTop.onclick=function(){ clearInterval(toTopTimer); //清除计时器 toTopTimer=setInterval(function(){ st=window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop; //scrollTop window.scrollBy(0,-Math.ceil(st/10)); //相对滚动;缓动效果,Math.ceil解决精度问题 if(st==0){ clearInterval(toTopTimer); } },10); }; window.onscroll=function(){ //滚动页面时触发 st=window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop; //固定导航代码: if(st>headerHeight){ //到达导航时 nav.style.position="fixed"; main.style.paddingTop=nav.offsetHeight+"px"; //腾出main被占用的空间 }else{ nav.style.position="static"; main.style.paddingTop="0px"; } //回到页首代码2: if(st>150){ //当超出部分多于150时 toTop.style.display="block"; //显示箭头 }else{ toTop.style.display="none"; } }; </script>
</body>
</html>
缓动动画封装
实现效果
在offset小节,实现了匀速动画和缓动动画,但每次只能设置一个属性,且动画不能先后有序执行,所以要解决:
- 给函数传递一个包含样式信息的数组,执行动画时所有属性同步进行
- 若有多个动画需依次执行,后面的语句传给前面的语句,依次执行
- 若需要延时,后者传给前者的语句可以加上计时器
代码
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<style> *{ margin:0; padding:0; font-family:"微软雅黑"; } #box{ width:50px; height:50px; color:#fff; text-align:center; font-size:14px; line-height:50px; position:relative; background:#08f; top:50px; left:10px; border-radius:100%; } </style>
</head>
<body>
<div id="box">box</div>
<script> var box=document.getElementById("box"); box.onclick=function(){ var t=this; //this赋给t方便操作 var json={ //动画1 "top":25, "left":100, "width":100, "height":100, "line-height":100, "font-size":30 }; var json2={ //动画2 "top":50, "left":190, "width":50, "height":50, "line-height":50, "font-size":14 }; animate(t,json,function(){ setTimeout(function(){ //延时1s animate(t,json2); },1000); }); }; function animate(ele,json,fn){ clearInterval(ele.timer); ele.timer=setInterval(function(){ var flag=true; for(var key in json){ var current=parseInt(getStyle(ele,key)) || 0; //获取源值,若无则赋0 var dir=current<json[key] ? 1 : -1; //确定方向 ele.style[key]=current+dir*Math.ceil(Math.abs(current-json[key])/10)+"px"; if(json[key]!=current){ //所有参数都到位再停止 flag=false; } } if(flag){ clearInterval(ele.timer); if(fn){ fn(); } } },24); } function getStyle(ele, attr) { if (window.getComputedStyle) { return window.getComputedStyle(ele, null)[attr]; } return ele.currentStyle[attr]; } </script>
</body>
</html>
以上代码只能实现left,width,font-size等整数数值类样式的动画,opacity等小数位样式不能实现,color,background等复杂样式不能实现,但可以针对某个样式进行专门的解析