javascript动画浅析

动画原理

所谓的动画,就是通过一些列的运动形成的动的画面。在网页中,我们可以通过不断的改变元素的css值,来达到动的效果。

用到的公式

总距离S = 总时间T * 速度V 即: V = S/T
当前距离s = S/T * 已耗时t 即: s = S * (t/T)
即:当前距离 = 总距离 * (已耗时/总时间)
即:动画元素开始值 + (动画元素结束值 - 动画元素开始值) * (当前时间-开始时间) / (动画需要时间) + 值的格式

有了上面这些公式,我们就能利用javascript的setInterval或者setTimeout来做一个简单的动画了。
然而想要做一个动画库,就不得不考虑另外一些因素了。 比如同一个元素的动画,必须要有顺序的执行。不同元素的动画可以同步运行。

如此一来,就必须得用另外一个对象来管理这些动画了。我开始的想法是讲每个元素都放在一个数组里,用几个setInterval来循环取出数组中的动画函数依次执行。

animate1 = [{elem,fn},{elem,fn}];
animate2 = [{elem,fn},{elem,fn}];

这样就能达到,相同的元素动画,是有顺序的执行,而不同的则可以同时运行了。然后这样却存在一个问题,那就是如果超过10个元素的动画。程序就要开十个setInterval。

为了避免这样的情况发生,就在上面的基础上做了一些改进。使得,不论多少个动画。都使用一个setInterval来完成。修改后结构如下。

代码如下:

[
[elem,[fn,fn,fn,fn]],
[elem,[fn,fn,fn,fn]],
[elem,[fn,fn,fn,fn]]
]

这样一来,就可以用一个setInterval来完成所有动画了。 所需要做就是,循环取出elem,并执行elem后面一个元素的头一个fn,fn执行完毕后删除fn。调用下一个fn,如果fn全部为空则从大的数组中删除elem,如果elem为空时,则清楚setInterval。这样一来,逻辑上便可以走得通了。

然而动画最关键的因素还有一个,那就是缓动。 如果没有缓动,那么动画效果看起来就非常的死板。千篇一律。目前做js动画用到的缓动算法是很多的,大致分为两类。
一种是flash类,一种是prototype类。

flash的需要四个参数。分别是,
1.时间初始话的时间t
2.动画的初始值b
3.动画的结束值c
4.动画持续的时间d
下面是一个flash 类的匀速运动算法
Linear: function(t,b,c,d){ return c*t/d + b; }
另一种则是prototype,这一类的参数只需要一个,那就是当前时间t与持续时间d的比值 (t/d)
我采用了第二种,因为它的参数方便。也更加适合上面的动画公式,下面是一个prototype类的匀速运动算法
linear : function(t){ return t;}.
加入缓动后上面的公式变为
动画元素开始值 + (动画元素结束值 - 动画元素开始值) * 缓动函数((当前时间-开始时间) / (动画需要时间)) + 值的格式。
至此便是整个动画类设计便结束了。其中参考了一些其它人的博客,在此表示感谢!
最后,还是贴一下详细代码吧。


代码如下:

/**
* create time 2012/08/29
* @author lynx cat.
* @version 0.77beta.
*/

(function(win,doc){
var win = win || window;
var doc = doc || win.document,
pow = Math.pow,
sin = Math.sin,
PI = Math.PI,
BACK_CONST = 1.70158;
var Easing = {
// 匀速运动
linear : function(t){
return t;
},
easeIn : function (t) {
return t * t;
},
easeOut : function (t) {
return ( 2 - t) * t;
},
easeBoth : function (t) {
return (t *= 2) < 1 ?
.5 * t * t :
.5 * (1 - (--t) * (t - 2));
},
easeInStrong : function (t) {
return t * t * t * t;
},
easeOutStrong : function (t) {
return 1 - (--t) * t * t * t;
},
easeBothStrong: function (t) {
return (t *= 2) < 1 ?
.5 * t * t * t * t :
.5 * (2 - (t -= 2) * t * t * t);
},
easeOutQuart : function(t){
return -(pow((t-1), 4) -1)
},
easeInOutExpo : function(t){
if(t===0) return 0;
if(t===1) return 1;
if((t/=0.5) < 1) return 0.5 * pow(2,10 * (t-1));
return 0.5 * (-pow(2, -10 * --t) + 2);
},
easeOutExpo : function(t){
return (t===1) ? 1 : -pow(2, -10 * t) + 1;
},
swingFrom : function(t) {
return t*t*((BACK_CONST+1)*t - BACK_CONST);
},
swingTo: function(t) {
return (t-=1)*t*((BACK_CONST+1)*t + BACK_CONST) + 1;
},
sinusoidal : function(t) {
return (-Math.cos(t*PI)/2) + 0.5;
},
flicker : function(t) {
var t = t + (Math.random()-0.5)/5;
return this.sinusoidal(t < 0 ? 0 : t > 1 ? 1 : t);
},
backIn : function (t) {
if (t === 1) t -= .001;
return t * t * ((BACK_CONST + 1) * t - BACK_CONST);
},
backOut : function (t) {
return (t -= 1) * t * ((BACK_CONST + 1) * t + BACK_CONST) + 1;
},
bounce : function (t) {
var s = 7.5625, r;
if (t < (1 / 2.75)) {
r = s * t * t;
}
else if (t < (2 / 2.75)) {
r = s * (t -= (1.5 / 2.75)) * t + .75;
}
else if (t < (2.5 / 2.75)) {
r = s * (t -= (2.25 / 2.75)) * t + .9375;
}
else {
r = s * (t -= (2.625 / 2.75)) * t + .984375;
}
return r;
}
};
/**
* 基石 用于返回一个包含对话方法的对象
* @param elem
* @return {Object}
*/
function catfx(elem){
elem = typeof elem === 'string' ? doc.getElementById(elem) : elem;
return new fx(elem);
}
/**
* 内部基石 用于返回一个包含对话方法的对象
* @param elem
* @return {Object}
*/
function fx(elem){
this.elem = elem;
return this;
}
/**
* 基础类 包含一些基础方法,和不变量
*/
var fxBase = {
speed : {
slow : 600,
fast : 200,
defaults : 400
},
fxAttrs : [],
fxMap:[],
/**
* 返回对象元素的css值
* @param elem
* @param p
* @return css value
*/
getStyle : function(){
var fn = function (){};
if('getComputedStyle' in win){
fn = function(elem, p){
var p = p.replace(/\-(\w)/g,function(i,str){
return str.toUpperCase();
});
var val = getComputedStyle(elem, null)[p];
if(~(' '+p+' ').indexOf(' left right top bottom ') && val === 'auto'){
val = '0px';
}
return val;
}
}else {
fn = function(elem, p){
var p = p.replace(/\-(\w)/g,function(i,str){
return str.toUpperCase();
});
var val = elem.currentStyle[p];
if(~(' '+p+' ').indexOf(' width height') && val === 'auto'){
var rect = elem.getBoundingClientRect();
val = ( p === 'width' ? rect.right - rect.left : rect.bottom - rect.top ) + 'px';
}
if(p === 'opacity'){
var filter = elem.currentStyle.filter;
if( /opacity/.test(filter) ){
val = filter.match( /\d+/ )[0] / 100;
val = (val === 1 || val === 0) ? val.toFixed(0) : val.toFixed(1);
}else if( val === undefined ){
val = 1;
}
}
if(~(' '+p+' ').indexOf(' left right top bottom ') && val === 'auto'){
val = '0px';
}
return val;
}
}
return fn;
}(),
/**
* 返回对象元素的css值
* @param 颜色值(暂不支持red,pink,blue等英文)
* @return rgb(x,x,x)
*/
getColor : function(val){
var r, g, b;
if(/rgb/.test(val)){
var arr = val.match(/\d+/g);
r = arr[0];
g = arr[1];
b = arr[2];
}else if(/#/.test(val)){
var len = val.length;
if( len === 7 ){
r = parseInt( val.slice(1, 3), 16);
g = parseInt( val.slice(3, 5), 16);
b = parseInt( val.slice(5), 16);
}
else if( len === 4 ){
r = parseInt(val.charAt(1) + val.charAt(1), 16);
g = parseInt(val.charAt(2) + val.charAt(2), 16);
b = parseInt(val.charAt(3) + val.charAt(3), 16);
}
}else{
return val;
}
return {
r : parseFloat(r),
g : parseFloat(g),
b : parseFloat(b)
}
},
/**
* 返回解析后的css
* @param prop
* @return {val:val,unit:unit}
*/
parseStyle : function(prop){
var val = parseFloat(prop),
unit = prop.replace(/^[\-\d\.]+/, '');
if(isNaN(val)){
val = this.getColor(unit);
unit = '';
}
return {val : val, unit : unit};
},
/**
* 设置元素的透明度
* @param elem
* @param val
*/
setOpacity : function(elem, val){
if( 'getComputedStyle' in win ){
elem.style.opacity = val === 1 ? '' : val;
}else{
elem.style.zoom = 1;
elem.style.filter = val === 1 ? '' : 'alpha(opacity=' + val * 100 + ')';
}
},
/**
* 设置元素的css值
* @param elem
* @param prop
* @param val
*/
setStyle : function(elem, prop, val){
if(prop != 'opacity'){
prop = prop.replace(/\-(\w)/g,function(i,p){
return p.toUpperCase();
});
elem.style[prop] = val;
}else{
this.setOpacity(elem, val);
}
},
/**
* 返回解析后的prop
* @param prop
* @return {prop}
*/
parseProp : function(prop){
var props = {};
for(var i in prop){
props[i] = this.parseStyle(prop[i].toString());
}
return props;
},
/**
* 修正用户的参数
* @param elem
* @param duration
* @param easing
* @param callback
* @return {options}
*/
setOption : function(elem,duration, easing, callback){
var options = {};
var _this = this;
options.duration = function(duration){
if(typeof duration == 'number'){
return duration;
}else if(typeof duration == 'string' && _this.speed[duration]){
return _this.speed[duration];
}else{
return _this.speed.defaults;
}
}(duration);
options.easing = function(easing){
if(typeof easing == 'function'){
return easing;
}else if(typeof easing == 'string' && Easing[easing]){
return Easing[easing];
}else{
return Easing.linear;
}
}(easing);
options.callback = function(callback){
var _this = this;
return function (){
if(typeof callback == 'function'){
callback.call(elem);
}
}
}(callback)
return options;
},
/**
* 维护setInterval的函数,动画的启动
*/
tick : function(){
var _this = this;
if(!_this.timer){
_this.timer = setInterval(function(){
for(var i = 0, len = _this.fxMap.length; i < len; i++){
var elem = _this.fxMap[i][0];
var core = _this.data(elem)[0];
core(elem);
}
},16);
}
},
/**
* 停止所有动画
*/
stop : function(){
if(this.timer){
clearInterval(this.timer);
this.timer = undefined;
}
},
/**
* 存储或者拿出队列
* @param elem
*/
data : function(elem){
for(var i = 0, len = this.fxMap.length; i < len; i++){
var data = this.fxMap[i];
if(elem === data[0]){
return data[1];
}
}
this.fxMap.push([elem,[]]);
return this.fxMap[this.fxMap.length - 1][1];
},
/**
* 删除队列
* @param elem
*/
removeData : function(elem){
for(var i = 0, len = this.fxMap.length; i < len; i++){
var data = this.fxMap[i];
if(elem === data[0]){
this.fxMap.splice(i, 1);
if(this.isDataEmpty()){
this.stop();
}
}
}
},
isDataEmpty : function(){
return this.fxMap.length == 0;
}
}, $ = fxBase;
/**
* 核心对象,用于生成动画对象。
* @param elem
* @param props
* @param options
* @return {Object}
*/
function fxCore(elem, props, options){
this.elem = elem;
this.props = props;
this.options = options;
this.start();
}
fxCore.prototype = {
constructor : fxCore,
/**
* 将动画函数加入到队列中,并启动动画。
*/
start : function(){
var cores = $.data(this.elem);
cores.push(this.step());
$.tick();
},
/**
* 核心方法,控制每一帧元素的状态。
* @return function
*/
step : function(){
var _this = this;
var fn = function(elem){
var t = Date.now() - this.startTime;
if(Date.now() < this.startTime + this.options.duration){
if(t <= 1){ t = 1;}
for(var i in this.target){
if(typeof this.source[i]['val'] === 'number'){
var val = parseFloat((this.source[i]['val'] + (this.target[i]['val'] - this.source[i]['val']) * this.options.easing(t / this.options.duration)).toFixed(7));
}else{
var r = parseInt(this.source[i]['val']['r'] + (this.target[i]['val']['r'] - this.source[i]['val']['r']) * this.options.easing(t / this.options.duration));
var g = parseInt(this.source[i]['val']['g'] + (this.target[i]['val']['g'] - this.source[i]['val']['g']) * this.options.easing(t / this.options.duration));
var b = parseInt(this.source[i]['val']['b'] + (this.target[i]['val']['b'] - this.source[i]['val']['b']) * this.options.easing(t / this.options.duration));
var val = 'rgb(' + r + ',' + g + ',' + b + ')';
}
$.setStyle(this.elem,i,val + this.source[i]['unit']);
}
}else{
for(var i in this.target){
if(typeof this.target[i]['val'] === 'number'){
var val = this.target[i]['val'];
}else{
var val = 'rgb(' + this.target[i]['val']['r'] + ',' + this.target[i]['val']['g'] + ',' + this.target[i]['val']['b'] + ')';
}
$.setStyle(elem,i,val + this.source[i]['unit']);
}
var cores = $.data(elem);
cores.shift();
this.options.callback();
if(cores.length == 0){
$.setStyle(elem,'overflow',this.overflow);
$.removeData(elem);
}
}
}
return function(elem){
if(!_this.startTime){
var source = {};
_this.target = _this.props;
for(var i in _this.props){
var val = $.getStyle(_this.elem, i);
source[i] = $.parseStyle(val);
}
_this.source = source;
_this.startTime = Date.now();
_this.overflow = $.getStyle(elem,'overflow');
$.setStyle(elem,'overflow','hidden');
}
fn.call(_this,elem);
}
}
}
/**
* 外部接口类。
*/
fx.prototype = {
constructor : fx,
/**
* 动画方法
* @param prop
* @param duration
* @param easing
* @param callback
* @return {Object}
*/
animate : function(prop, duration, easing, callback){
if(arguments.length == 3 && typeof easing === 'function'){ //多数时候用户第三个参数是回调
callback = easing;
easing = undefined;
}
var props = $.parseProp(prop);
var options = $.setOption(this.elem,duration,easing,callback);
var core = new fxCore(this.elem,props,options);
return this;
},
/**
* 停止动画方法
* 使用方法 catjs('your element id').stop();
*/
stop : function(){
$.removeData(this.elem);
}
}
win.catfx = catfx;
})(this,document);

使用起来也比较简单.直接catfx('ID').animate({'margin-left':200,'background-color':'#ff0000'},600,'easeOut',function(){});

跟jquery的使用方法差不多,如果不传第二个参数,则默认为400毫秒。不传第三个参数则默认匀速。第三个参数为函数,并且总共只有三个参数时。第三个参数为回调。

例:catfx('ID').animate({'margin-left':200,'background-color':'#ff0000'},600,function(){alert('洒家是回调函数~')});

(0)

相关推荐

  • javascript转换静态图片,增加粒子动画效果

    使用getImageData接口获取图片的像素点,然后基于像素点实现动画效果,封装成一个简单的lib <!DOCTYPE html> <html> <head> <title>particle image</title> <meta charset="utf-8" /> <style> #logo { margin-left:20px; margin-top:20px; width:160px; hei

  • javascript动画算法实例分析

    本文实例讲述了javascript动画算法.分享给大家供大家参考.具体如下: 动画算法 Linear:无缓动效果(匀速运动): Quadratic:二次方的缓动: Cubic:三次方的缓动 Quartic:四次方的缓动: Quintic:五次方的缓动: Sinusoidal:正弦曲线的缓动: Exponential:指数曲线的缓动: Circular:圆形曲线的缓动: Elastic:指数衰减的正弦曲线缓动: Back:超过范围的三次方缓动): Bounce:指数衰减的反弹缓动. 每个效果都分三

  • javascript动画对象支持加速、减速、缓入、缓出的实现代码

    调用接口: 复制代码 代码如下: /** * @param elem {HTMLElement} 执行动画的HTML元素 * @param params {JSON} 动画执行过过程中需要修改的HTML属性 * @param duration {Number} 可选,动画执行时间,单位毫秒 * @param easing {String} 可选,动画执行的方式,缓入easeIn.缓出easeOut * @param callback {Function} 可选,动画执行完成时的回调函数 * @r

  • 深入探究使JavaScript动画流畅的一些方法

    基于Javascript的动画暗中同CSS过渡效果一样,甚至更加快,这怎么可能呢?而Adobe和Google持续发布的富媒体移动网站的性能可媲美本地应用,这又怎么可能呢? 本文逐一遍览了基于Javascript的DOM动画库,如Velocity.js和GSAP,看其是如何比jQuery和CSS动画效果更具性能的. jQuery 让我们先从基础的开始: JavaScript 和 jQuery 被错误的混为一谈了. JavaScript 动画是很快的. jQuery 把它放慢了下来.为什么?因为 -

  • javascript动画之圆形运动,环绕鼠标运动作小球

    代码如下: 复制代码 代码如下: <script type="text/javascript"> var ball; var mouseX = 100; var mouseY = 100; var angle = 0; var radius = 50; function run(){ if(ball === undefined){ ball = document.createElement("span"); ball.style.position = &

  • javascript制作loading动画效果 loading效果

    复制代码 代码如下: /*ajax提交的延时等待效果*/ var AjaxLoding = new Object(); //wraperid : 显示loding图片的容器元素//ms:表示loding图标显示的时长,毫秒//envent:表示出发事件的事件源对象,用于获得出发事件的对象//callback:表示动画结束后执行的回掉方法//stop()方法表示在回掉方法执行成功后执行的隐藏动画的操作AjaxLoding.load = function(lodingid,ms,event,left

  • JavaScript实现动画打开半透明提示层的方法

    本文实例讲述了JavaScript实现动画打开半透明提示层的方法.分享给大家供大家参考.具体如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"&g

  • 推荐10 款 SVG 动画的 JavaScript 库

    SVG 通常可以用作跨分辨率视频.这意味着在一块高分屏幕上不会降低图片的锐度.此外,你甚至可以让SVG动起来,通过使用一些javascript类库.下面,我们分享一些javascript类库,这些类库会帮助我们将SVG动画提高一个等级. Vivus Vivus 是一个能动画js类库,它能够给SVG图像显示出被画出来的过程.Vivus是没有其他类库依赖的(比如jQuery).你仅仅需要在页面中加入这个.js文件,然后传入需要被用来动画的SVG部分就行.同时通过指定一些配置,它能够在页面加载后直接显

  • 浅析JavaScript动画

    今天,小学生以自己浅薄的见地,在前辈大能的基础上写这篇文章,希望给大家打开一扇窥探JavaScript(以下简称JS)动画的窗户. JS如何制造出动画效果? 结合浏览器提供的 setInterval 或 setTimeout API,高频改变DOM元素的一些属性,即可创造一个肉眼可见的动画效果.一个看起来非常流畅的JS动画除了需要良好的变换算法外,与其执行宿主也是非不开的.程序写得再好,如果浏览器过于老旧,电脑CPU性能低下,也会出现卡顿,甚至卡死. 执行一个动画函数对于浏览器来说是个苦差,设置

  • javascript动画浅析

    动画原理 所谓的动画,就是通过一些列的运动形成的动的画面.在网页中,我们可以通过不断的改变元素的css值,来达到动的效果. 用到的公式 总距离S = 总时间T * 速度V 即: V = S/T 当前距离s = S/T * 已耗时t 即: s = S * (t/T) 即:当前距离 = 总距离 * (已耗时/总时间) 即:动画元素开始值 + (动画元素结束值 - 动画元素开始值) * (当前时间-开始时间) / (动画需要时间) + 值的格式 有了上面这些公式,我们就能利用javascript的se

  • Javascript动画效果(1)

    前面我们介绍了Javascript的回到顶部效果,今天呢,我们对Javascript动画做进一步的研究.在这篇博文中我们只介绍简单的匀速运动.简单的缓冲运动和简单的多物体运动后面我们还会介绍任意值变化的运动.链式运动.同时运动,同时我们还会简单的封装一个运动插件并且还会将Javascript方法和jquery方法进行比较. 1.简单的匀速运动 下面我们介绍一个demo,鼠标移入,动画向右移动(即隐藏部分显示):鼠标离开,动画向左运动(继续隐藏)整个过程都是匀速的.有了前面回到顶部效果作为基础,这

  • Javascript动画效果(2)

    在前面的文章中讲了简单的Javascript动画效果,这篇文章主要介绍我在改变之前代码时发现的一些问题及解决方法. 在前面的多物体宽度变化的例子中,我们给其增加代码:border: 4px solid #000;我们发现,鼠标移出后,宽度不是200px了,那么究竟是如何产生这种情况的呢?下面我们通过一个新的例子来分析 html代码: <div id="div1">hello</div> css代码: body,div{ margin: 0px; padding:

  • AngularJS 实现JavaScript 动画效果详解

    AngularJS 应用中实现 JavaScript 动画效果 AngularJS 是一组用于创建单页Web应用的丰富框架,给构建丰富交互地应用带来了所有需要的功能.其中一项主要的特性就是Angular带来了对动画的支持. 我们能够在应用的部分内容当中使用动画来表明一些变化正在发生.在我上一篇文章当中,我讲到了在Angular应用中对CSS动画的支持.在这篇文章当中,我们会看到怎样利用JavaScript脚本在AngularJS应用当中生成动画效果. 在Angular当中,CSS和JavaScr

  • Javascript动画效果(3)

    前面我们已经介绍了速度动画.透明度动画.多物体运动和任意值变化,并且我们在Javascript动画效果(二)中介绍到我们封装了一个简单的插件雏形,接下来我们对前面的动画效果进行进一步扩充,尽量将我们的框架做到更实用.在这里我们还需要了解两个运动,一个是链式运动,一个是同时运动.它们间的区别分别是:链式运动是指运动一个接着一个(一个运动完成马上进行下一个运动):而同时运动是指所有的运动同时进行.在这里,我们该如何实现呢? 1.链式运动 前面的效果中,我们已经能对任意值进行相应的变化,我们该如何在一

  • javascript动画之磁性吸附效果篇

    前面的话 上一篇,我们介绍了javascript动画之模拟拖拽效果篇.但在实际应用中,常常需要为拖拽的元素限定范围.而通过限定范围,再增加一些辅助的措施,就可以实现磁性吸附的效果 范围限定 如果我们限定元素只可以在可视范围内移动,那么就需要对其进行范围限定 首先,先要搞清楚是可视区域限定被拖拽元素 左侧范围L0 = 0 右侧范围R0 = document.documentElement.clientWidth 上侧范围T0 = 0 下侧范围B0 = document.documentElemen

  • Javascript动画效果(4)

    前面我们自己写了一个小小的关于js动画的插件,下面我们来使用之前的框架来完成我们想要的动画效果.我们经常在淘宝网中看到,鼠标经过某一图片时,该图片有从上滚出而又从下滚入的效果,那么那种效果是如何实现的呢? 首先我们我们完成该效果的html和css代码,代码如下: html部分代码: <div id="move"> <a href="#"><i><img src="images/1.jpg"/><

随机推荐