匀速动画的实现
使用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等复杂样式不能实现,但可以针对某个样式进行专门的解析