JavaScript轻量级缓动动画插件设计与实现
本文介绍了一款轻量级JavaScript缓动动画插件,旨在解决现有库体积大或功能单一的问题。该插件支持动画的开始、暂停、继续和停止,兼容线性与非线性缓动,并允许并行处理多个样式属性。通过引入mixin机制,它可灵活适配不同框架或原生JS环境,且代码压缩后不足1KB,适合对性能有严格要求的项目。
需求分析与设计目标
开发该插件需满足以下核心需求:
具备完整的生命周期控制,包括启动、暂停(需同时支持线性与非线性缓动模式)、继续执行及强制结束。
支持对多个CSS样式属性进行并行动画处理,而非串行执行。
保持独立性,不强制依赖jQuery、Tangram等特定第三方框架,确保在原生JavaScript环境中即可运行。
极致压缩代码体积,确保文件尺寸尽可能小,以优化加载性能。
经市场调研,现有插件要么功能不全,要么体积庞大,难以在轻量与功能间取得平衡。基于上述需求,本文实现了一个精简的动画组件,基本覆盖了上述场景。
在线演示地址:http://demo.jb51.net/js/2012/animate/
源码打包下载:animate_jquery.rar
HTML结构部分如下:
代码实现如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>animate</title>
<script type="text/javascript" src="tangram-1.5.0.core.js"></script>
<script type="text/javascript" src="jquery.min.js"></script>
<style>
html,body,ul,li{padding:0;margin:0;}
#anim{width:50px;height:50px;background:red;position:absolute; top:30px;left:0;}
</style>
</head>
<body>
<div>
<input type="button" value="start" onclick="an.start()" />
<input type="button" value="pause" onclick="an.pause()" />
<input type="button" value="resume" onclick="an.resume()" />
<input type="button" value="stop" onclick="an.stop()" />
<a target="_self" id="demolink" href="animate.html?demo=1" />auto start,pasue,then resume</a>
</div>
<div id="anim"></div>
</body></html> 核心动画引擎实现
核心动画逻辑部分:
代码实现如下:
function animate(options){
this.from = options.from;//如果没有from,就计算出from
this.to = options.to || {};
this.onStart = options.onStart || empty;//以下是一些回调函数,就不采用事件机制了
this.onStop = options.onStop || empty;
this.onAnimate = options.onAnimate || empty;
this.onContinue = options.onContinue || empty;
this.onPause = options.onPause || empty;
var element = options.element;
var duration = options.duration || 300;//变化的总时长,单位是ms
var pending = false;//是不是已经暂停了,如果还木有开始的话,该值也是true
var timer = null;
var mixin = options.mix;
var defaultState = this.from || getState(element, this.to);//原始的数据
var thiz = this;
//获取最初始的样式
function getState(ele, targetStyles){
var obj = {};
var i = 0;
for (var p in targetStyles)
{
i++;
obj[p] = parseFloat(mixin.getStyle(ele, p));
}
if(i == 0){
return null;
}
return obj;
}
function empty(){}
function animate(from, to, elapse){
var startTime = (new Date).getTime() + (elapse ? - elapse : 0);//如果有第三个参数,证明是从一个暂停开始的,那么就设置startTime为当前时间减去暂定时已经逝去的时间,如果只有两个参数,那么逝去时间就是0
timer = setInterval(function(){
var endTime = (new Date).getTime();
if(endTime - startTime < duration){
thiz.onAnimate();
currentElapse = endTime - startTime;
for(var p in from){
if(from.hasOwnProperty(p)){
var currentPropertyStyle = mixin.compute(currentElapse, from[p], to[p]-from[p], duration);
mixin.setStyle(element, p, currentPropertyStyle);
}
}
}
else{
thiz.stop(thiz.to);
}
}, 16);
}
this.start = function(){
if(pending){
this.resume();
}
else{
this.onStart();
animate(defaultState, this.to);
}
}
this.stop = function(){
clearInterval(timer);
var styles = this.to;
for(var p in styles){
if(styles.hasOwnProperty(p)){
mixin.setStyle(element, p, styles[p]);
}
}
this.onStop();
}
this.pause = function(){
clearInterval(timer);
pending = true;
this.onPause();
}
this.resume = function(){
pending = false;
this.onContinue();
animate(defaultState, this.to, currentElapse);
}
} 框架适配与使用示例
使用部分:
代码实现如下:
var mixinT = {
getStyle:baidu.dom.getStyle,
setStyle:baidu.dom.setStyle,
compute:function(t, b, c, d){
//return t*c/d + b;
if (t==0) return b;
if (t==d) return b+c;
if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
}
};
var mixinJQ = {
getStyle:function(ele, styleName){
return $(ele).css(styleName);
},
setStyle:function(ele, styleName, styleValue){
$(ele).css(styleName, styleValue);
},
compute:function(t, b, c, d){
return b+ t*c/d;
}
};
var an = new animate({
element:document.getElementById('anim'),
//from:{'width':100, 'height':100, left:0, top:30},
to:{left:500},
mix:mixinT,
duration:2000
});
if(/demo=1/.test(location.search)){
var demolink = baidu.g('demolink');
demolink.href= 'animate.html';
demolink.innerHTML = 'back';
an.start();
setTimeout(function(){
an.pause();
resume();
}, 1200);
function resume(){
setTimeout(function(){an.resume()}, 1000);
}
} 技术细节与优缺点评估
上述代码构成了一个完整的Demo示例。以下是对该实现的关键说明:
代码极其精简,总行数仅100行左右,经过Gzip压缩后体积不足1KB。
完整实现了start、pause、resume、stop的操作需求,并额外提供了多个回调函数供扩展。
支持对多个CSS样式属性同时进行动画变化。
该实现引入一个mixin变量,传入三个在动画执行期间需调用的函数:getStyle、setStyle与compute。前两者通常与框架相关,后者取决于用户选定的变化计算方式。传入四个参数的设计旨在兼容主流tween类库,可参考http://www.robertpenner.com/easing/及http://www.actionscript.org/resources/articles/170/1/Flash-MX-2004-Undocumented-TweenEasing-Classes-Documented/Page1.html。示例结合tangram与jquery库,分别对应两个mixin对象,经简单适配即可使用。
部分“私有”变量和函数被封装在闭包中。虽然这保证了每次初始化animate实例时对象状态的纯净,但缺点是每个实例都会维护自己的方法副本,从而增加内存占用。
使用时可以不指定options.from,系统会根据options.to自动计算初始样式值。这一功能高度依赖mixin提供的能力,目前仍存在一定缺陷,但已能满足80%的常见需求。可谓麻雀虽小,五脏俱全。
JS监听滚动事件:回到顶部按钮的显示与隐藏逻辑
2026-11-05 16:19:37
JavaScript怎么读写LocalStorage与SessionStorage
2026-11-05 15:40:11
anime.js 与 GSAP 对比:轻量级项目该怎么选择动画库
2026-11-05 11:24:55
flv.js 与 hls.js 在直播场景下的选型对比
2026-11-05 08:18:52
JavaScript能做什么?前端开发中的作用与应用场景解析
2026-11-04 23:17:54
