一、JS动画本质
JavaScript 的动画,本质来说,就是让标签动起来。而想要让标签动起来,其本质就是改变标签属性,比如高宽,左边距,上边距,透明度等。
JavaScript 动画的本质就是间隔极短的时间(毫秒),持续改变标签的某个属性。
一般用用定时器,就能得到动画效果。
定时器,可以选择 setInterval, setTimeout,以及 requestAnimationFrame。
以控制 div.box 的移动为例子。
setInterval
let aniFun= function(){
// 动画代码
};
let myset = setInterval(aniFun,30);
let box = document.getElementById("box");
let dis = 0;
let speed = 10 ;
let aniFun= function(){
// 动画代码
dis += speed;
box.style.transform=`translateX(${dis}px)`;
};
let myset2 = setInterval(aniFun,30);
setTimeout
let aniFun= function(){
// 动画代码
myset2 = setTimeout(aniFun,30);
};
let myset2 = setTimeout(aniFun,30);
let box = document.getElementById("box");
let dis = 0;
let speed = 10 ;
let aniFun= function(){
// 动画代码
dis += speed;
box.style.transform=`translateX(${dis}px)`;
myset2 = setTimeout(aniFun,30);
};
let myset2 = setTimeout(aniFun,30);
requestAnimationFrame
使用方法跟 setTimeout 很类似。需要在动画函数的最后,再次回调它自己。
let aniFun= function(){
// 动画代码
req = requestAnimationFrame(aniFun);
};
let req = requestAnimationFrame(aniFun);
let box = document.getElementById("box");
let dis = 0;
let speed = 10 ;
let aniFun= function(){
// 动画代码
dis += speed;
box.style.transform=`translateX(${dis}px)`;
req = requestAnimationFrame(aniFun);
};
let req = requestAnimationFrame(aniFun);
更推荐使用 requestAnimationFrame
。
requestAnimationFrame
是由系统来决定回调函数的执行时机。如果屏幕刷新率是 60Hz,那么回调函数就每16.7ms (1000/60)被执行一次。如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,换句话说就是,requestAnimationFrame的步伐跟着系统的刷新步伐走。- 当页面失去焦点(切换到另外一个页面),
requestAnimationFrame
的动画会暂停,以解决系统资源。
二、利用事件触发动画
点击按钮让 div.box 动起来。
<div id="box" class="box"></div>
<button id="btn">点击开始运动</button>
1. 基本操作
let btn = document.getElementById("btn");
let box = document.getElementById("box");
let aniFun ;
let dis = 0;
let speed = 30;
let req ;
let BoxGo = function(){
dis += speed ;
box.style.transform=`translateX(${dis}px)`;
req = requestAnimationFrame(BoxGo);
};
btn.addEventListener("click",function () {
BoxGo();
});
上述代码有两个问题:
-
如果连续点击按钮,box 会不停持续加速运动。
解决方法:点击按钮后,先停止之前的动画,再开始新的动画。
-
box 会不停的向右运动,不会停止下来。
解决方法如下。
2. 设定动画距离
超过了距离就停止动画。
let btn = document.getElementById("btn");
let box = document.getElementById("box");
let aniFun ;
let dis = 0;
let speed = 30 ;
let target = 500 ;
let BoxGo = function(){
dis += speed ;
// 限制运动距离
if( dis >= target){
dis = target ;
cancelAnimationFrame(aniFun);
}else{
console.info(dis);
box.style.transform=`translateX(${dis}px)`;
aniFun = requestAnimationFrame(BoxGo);
}
};
btn.addEventListener("click",function () {
// 防止重复叠加动画:开始新的动画前,取消之前的动画。
cancelAnimationFrame(aniFun);
BoxGo();
});
3. 设定动画时间
设定动画的时间,超过了时间就停止动画。
let btn = document.getElementById("btn");
let box = document.getElementById("box");
let aniFun ;
let dis = 0;
let speed = 30;
let startTime = 0;
let aniTime = 500 ; // 动画时间为500毫秒。
let BoxGo = function(){
let time = new Date();
let p = time - startTime ;
dis += speed ;
// 限制运动时间 。到了 0.5s 就停止
console.info( p );
if( p >= aniTime ){
cancelAnimationFrame(aniFun);
}else{
box.style.transform=`translateX(${dis}px)`;
aniFun = requestAnimationFrame(BoxGo);
}
};
btn.addEventListener("click",function () {
// 防止重复叠加动画:开始新的动画前,取消之前的动画。
cancelAnimationFrame(aniFun);
startTime = new Date();
BoxGo();
});
4. 设定动画时间和动画距离
let btn = document.getElementById("btn");
let box = document.getElementById("box");
let aniFun ;
let speed = 30;
let aniTime = 500 ; // 运动时间 500 毫秒
let aniDis = 1500 ; // 运动距离 500px
let startTime = 0;
let BoxGo = function(){
let time = new Date();
let pastTime = time - startTime ; // 已经运动的时间点
console.info(pastTime);
let past = Math.min(pastTime/aniTime,1) ; // 0-1
// 限制运动时间 。到了 0.5s 就停止
if( pastTime >= aniTime ){
pastTime = 500 ;
cancelAnimationFrame(aniFun);
}else{
box.style.transform=`translateX(${past*aniDis}px)`;
aniFun = requestAnimationFrame(BoxGo);
}
};
btn.addEventListener("click",function () {
// 防止重复叠加动画:开始新的动画前,取消之前的动画。
cancelAnimationFrame(aniFun);
startTime = new Date();
BoxGo();
});
三、应用
图片的淡入淡出。
<img src="images/pic1.jpg" alt="" id="pic">
let pic = document.getElementById("pic");
let aniTime = 500; // 动画时间
let startTime = new Date();
let aniFun = function(){
let pastTime = new Date() - startTime ;
let past = Math.min( pastTime / aniTime ,1 ) ;
pic.style.opacity = past ;
if( past >= 1){
cancelAnimationFrame(req);
}else{
req = requestAnimationFrame(aniFun);
}
};
let req = requestAnimationFrame(aniFun);
封装成函数:
let fadeAni = function({duration=500,
targetV=1,
startV=0,
element
}={}){
let aniTime = duration; // 动画时间
let startTime = new Date();
let aniFun = function(){
let pastTime = new Date() - startTime ;
let past = Math.min( pastTime / aniTime ,1 ) ;
element.style.opacity = (targetV- startV)* past + startV ;
if( past >= 1){
cancelAnimationFrame(req);
}else{
req = requestAnimationFrame(aniFun);
}
};
let req = requestAnimationFrame(aniFun);
};
let pic = document.getElementById("pic");
// 淡入
fadeAni({
duration:1000,
startV:0,
targetV:1,
element:pic
});
let pic = document.getElementById("pic");
// 淡出
fadeAni({
duration:1000,
startV:1,
targetV:0,
element:pic
});