使用原生js实现简单动画效果

使用原生js实现简单动画效果

我们知道,借助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则为帧刷新速率,这样连续计算和设置元素样式,即形成了动画效果。

发布了37 篇原创文章 · 获赞 90 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41694291/article/details/93340607