我们知道,借助jQuery提供的animate方法,我们可以很容易实现一些常见的js动画效果。而对于颜色等无法用数值直接表示的样式,我们可以通过引入一些jQuery插件来实现。但是随着前端组件化开发的流行,jQuery大量的DOM操作已显得十分多余,正在逐渐从前端技术栈中被淘汰。那么如何使用原生js来实现类似于jQuery中的动画效果呢?下面我们将使用原生js实现一个简单的让div匀速加宽的动画效果。
点击按钮使div从100像素加宽为200像素。初始HTML代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>简单js动画的实现</title>
<style>
#nav{
margin-top: 20px;
width: 100px;
height: 100px;
background-color: #409eff;
}
</style>
</head>
<body>
<button onclick="run()">加宽</button>
<div id="nav"></div>
</body>
<script>
//需要应用动画效果的目标元素
var elem = document.getElementById('nav');
//动画配置,为了方便,这里只针对width属性,duration表示动画持续时间
var options = {
width: '200px',
duration: 1000
};
//启动动画
function run(){
animate.call(elem, options);
}
//动画逻辑
function animate(options){
//动画的具体实现,后面的所有代码均位于该函数内
...
}
</script>
<html>
使用js实现动画效果的基本原理是,用window.setInterval(logic, time)控制动画的整体运行过程。logic用于计算和设置当前帧的样式,time给定动画刷新速率(即每隔该间隔就重新计算样式,暂不考虑任务阻塞)。
那么logic如何计算当前帧中div的宽度值呢?首先我们需要获取div的原始宽度和结束宽度,然后根据当前动画执行的百分比,计算当前宽度值,这需要借助三个时间参数来确定。一个是动画的开始时间,我们通过一个简单的函数,来获取当前时间的毫秒数形式,代码如下:
//定时器
var timer = null;
//div的初始宽度和结束宽度
var startWidth = parseInt(window.getComputedStyle(elem)['width']);
var endWidth = parseInt(options.width);
function createTime(){
//返回当前日期距1970年1月1日午夜(GMT 时间)之间的毫秒数,一种简化写法
return (+new Date);
}
var startTime = createTime();
另一个是动画持续时间duration,我们从配置的参数中得到。第三个就是当前时间,我们在logic函数中动态获取。根据这三个参数,我们可以在logic中计算出当前动画的剩余时间:
function logic(){
//开始时间 + 持续时间 - 当前时间,结果即为动画的剩余时间,当剩余时间小于0则置0,表示动画结束
var remaining = Math.max(0, startTime + options.duration - createTime());
根据动画的剩余时间和动画的持续时间,我们可以很容易计算出当前动画执行的百分比,即:
//remaining/duration即为动画剩余的百分比,用1去减,得到已执行的百分比
var percent = 1 - (remaining / options.duration);
根据动画已执行的百分比,我们就可以计算当前帧元素的样式:
//(结束宽度 - 开始宽度)* 百分比 + 开始宽度,即为当前帧div的实际宽度
var nowWidth = ( endWidth - startWidth ) * percent + startWidth;
然后我们定义一个函数setStyle来为div设置宽度:
function setStyle(nowWidth){
elem.style['width'] = nowWidth + 'px';
}
我们通过percent来判断当前动画是否结束,如果结束,就设置样式并且清除定时器,否则只设置样式即可:
if(percent === 1){
setStyle(nowWidth);
clearInterval(timer);
timer = null;
} else {
setStyle(nowWidth);
}
}
最后,我们在logic函数外启动定时器,设置帧刷新速率为13毫秒(jQuery的默认刷新速率):
timer = setInterval(logic, 13);
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>简单js动画的实现</title>
<style>
#nav{
margin-top: 20px;
width: 100px;
height: 100px;
background-color: #409eff;
}
</style>
</head>
<body>
<button onclick="run()">加宽</button>
<div id="nav"></div>
</body>
<script>
//需要应用动画效果的目标元素
var elem = document.getElementById('nav');
//动画配置,为了方便,这里只针对width属性,duration表示动画持续时间
var options = {
width: '200px',
duration: 1000
};
//点击按钮启动动画
function run(){
animate.call(elem, options);
}
//动画逻辑
function animate(options){
//定时器
var timer = null;
//div的初始宽度和结束宽度
var startWidth = parseInt(window.getComputedStyle(elem)['width']);
var endWidth = parseInt(options.width);
function createTime(){
//返回当前日期距1970年1月1日午夜(GMT 时间)之间的毫秒数,一种简化写法
return (+new Date);
}
var startTime = createTime();
function logic(){
//开始时间 + 持续时间 - 当前时间,结果即为动画的剩余时间,当剩余时间小于0则置0,表示动画结束
var remaining = Math.max(0, startTime + options.duration - createTime());
//remaining/duration即为动画剩余的百分比,用1去减,得到已执行的百分比
var percent = 1 - (remaining / options.duration);
//(结束宽度 - 开始宽度)* 百分比 + 开始宽度,即为当前帧div的实际宽度
var nowWidth = ( endWidth - startWidth ) * percent + startWidth;
function setStyle(nowWidth){
elem.style['width'] = nowWidth + 'px';
}
if(percent === 1){
setStyle(nowWidth);
clearInterval(timer);
timer = null;
} else {
setStyle(nowWidth);
}
}
timer = setInterval(logic, 13);
}
</script>
<html>
本示例只简单地实现元素宽度的变化,但是可以从中了解到js动画执行的基本原理。即通过setInterval(logic, time)控制动画执行,logic用于计算每帧的样式,time则为帧刷新速率,这样连续计算和设置元素样式,即形成了动画效果。