加载中

JS-原生动画

匀速动画的实现

实现效果(设计稿)

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

posted @ 2019-08-13 17:01  jialeYang  阅读(81)  评论(0编辑  收藏  举报