动画组件(wagang版)之基础篇:时长、进度、帧
页面动画是一种很常见的效果,很多同学因它而对js产生兴趣,甚至jquery把它放在核心库里。
QWrap的核心库,并没有提供动画组件。wagang版动画组件是依赖QWrap开发出的一个动画组件。它的前身是youa网站使用的animation组件,原作者是Jerry Qu,改装成wagang组件时作了一些修改。
文档与示例参见:http://dev.qwrap.com/resource/js/wagang/anim/_examples/index.html
anim.js是一个复合js,它由四个js组成:
document.write('<script type="text/javascript" src="' + srcPath + 'wagang/anim/anim_base.js"></script>');
document.write('<script type="text/javascript" src="' + srcPath + 'wagang/anim/elanim.js"></script>');
document.write('<script type="text/javascript" src="' + srcPath + 'wagang/anim/easing.js"></script>');
document.write('<script type="text/javascript" src="' + srcPath + 'wagang/anim/anim_retouch.js"></script>');
动画提供了以下四个类:
anim_base.js产出基础动画类QW.Anim。主要是时间、帧、播放进度等的一个管理器,相当于一个万能动画。
elanim.js产出三个针对元素的动画类:QW.ElAnim、QW.ColorAnim、QW.ScrollAnim 。
easing.js产出算子集合:QW.Easing。
anim_retouch.js是添加一些易于调用的动画相关方法,例如:W('#id').toggleSlide();
先看一下基础动画类。源码大略为:

/*
Copyright QWrap
version: $version$ $release$ released
author: JK
*/
(function() {
var CustEvent = QW.CustEvent,
mix = QW.ObjectH.mix;
/**
* @class Anim 动画
* @namespace QW
* @constructor
* @param {function} animFun - 管理动画效果的闭包
* @param {int} dur - 动画效果持续的时间
* @param {json} opts - 其它参数,
---目前只支持以下参数:
{boolean} byStep (Optional) 是否按帧动画(即"不跳帧")。如果为true,表示每一帧都走到,帧数为dur/frameTime
{boolean} frameTime (Optional) 帧间隔时间。默认为28
{boolean} per (Optional) 初始播放进度
{function} onbeforeplay (Optional) onbeforeplay事件
{function} onplay (Optional) onplay事件
{function} onstep (Optional) onstep事件
{function} onpause (Optional) onpause事件
{function} onresume (Optional) onresume事件
{function} onstop (Optional) onstop事件
{function} onsuspend (Optional) onsuspend事件
{function} onreset (Optional) onreset事件
* @returns {Anim} anim - 动画对象
*/
var Anim = function(animFun, dur, opts) {
mix(this, opts);
mix(this, {
animFun: animFun, //animFun,动画函数,
dur: dur, //动画时长
byStep: false, //是否按帧动画
per: 0, //播放进度
frameTime: 28, //帧间隔时间
_status: 0 //0-未播放,1-播放中,2-播放结束,4-被暂停,8-被终止
});
changePer(this, this.per);
CustEvent.createEvents(this, Anim.EVENTS);
};
Anim.EVENTS = 'beforeplay,play,step,pause,resume,stop,suspend,reset'.split(',');
/*
* turnOn 打开动画定时器
* @param {Anim} anim Anim实例
* @returns {void}
*/
function turnOn(anim) {
anim.step();
if (anim.isPlaying()) {
anim._interval = window.setInterval(function() {
anim.step();
}, anim.frameTime);
}
}
/*
* turnOff 关闭动画定时器
* @param {Anim} anim Anim实例
* @returns {void}
*/
function turnOff(anim) {
window.clearInterval(anim._interval);
}
/*
* changePer 调整播放进度,进度值
* @param {Anim} anim Anim实例
* @param {number} per 进度值,为[0,1]区间内的数值
* @returns {void}
*/
function changePer(anim, per) {
anim.per = per;
anim._startDate = new Date() * 1 - per * anim.dur;
if (anim.byStep) {
anim._totalStep = anim.dur / anim.frameTime;
anim._currentStep = per * anim._totalStep;
}
}
mix(Anim.prototype, {
/**
* 判断是否正在播放
* @method isPlaying
* @returns {boolean}
*/
isPlaying: function() {
return this._status == 1;
},
/**
* 从0开始播放
* @method play
* @returns {boolean} 是否开始顺利开始。(因为onbeforeplay有可能阻止了play)
*/
play: function() {
var me = this;
if (me.isPlaying()) me.stop();
changePer(me, 0);
if (!me.fire('beforeplay')) return false;
me._status = 1;
me.fire('play');
turnOn(me);
return true;
},
/**
* 播放一帧
* @method step
* @param {number} per (Optional) 进度值,为[0,1]区间内的数值
* @returns {void}
*/
step: function(per) {
var me = this;
if (per != null) {
changePer(me, per);
} else {
if (me.byStep) {
per = me._currentStep++ / me._totalStep;
} else {
per = (new Date() - me._startDate) / me.dur;
}
this.per = per;
}
if (this.per > 1) {
this.per = 1;
}
me.animFun(this.per);
me.fire('step');
if (this.per >= 1) {
this.suspend();
return;
}
},
/**
* 停止播放,并预归位到0。
* @method stop
* @returns {void}
*/
stop: function() {
this._status = 8;
changePer(this, 0);
turnOff(this);
this.fire('stop');
},
/**
* 播放到最后
* @method suspend
* @returns {void}
*/
suspend: function() {
changePer(this, 1);
this.animFun(1);
this._status = 2;
turnOff(this);
this.fire('suspend');
},
/**
* 暂停播放
* @method pause
* @returns {void}
*/
pause: function() {
this._status = 4;
turnOff(this);
this.fire('pause');
},
/**
* 继续播放
* @method resume
* @returns {void}
*/
resume: function() {
changePer(this, this.per);
this._status = 1;
this.fire('resume');
turnOn(this);
},
/**
* 播放到最开始
* @method reset
* @returns {void}
*/
reset: function() {
changePer(this, 0);
this.animFun(0);
this.fire('reset');
}
});
QW.provide('Anim', Anim);
}());
我们把动画抽象一下,它由以下几个组成部分:
时长(dur),即动画播放的时间总长。
进度(per),播放的进度,在区间[0,1]之内。
帧间隔时间(frameTime),即多长时间播放一帧。
动画函数(animFun),它是每一帧的渲染函数。定时器每隔frameTime来调用一下animFun(per)。也就是说按进度播放一帧动画。
这四个基本参数将动画抽象成为一个js类,系统的理解就是:
“在dur时间内,每隔frameTime时间,播放一次animFun(per)。”
这个类实例可以有以下方法:
isPlaying() 判断是否正在播放 -----returns {boolean}
play() 从0开始播放 ----returns {boolean} 是否开始顺利开始。(因为onbeforeplay有可能阻止了play)
step(per) 播放一帧,参数per为[0,1]区间内的数值,为可选,表示播放到某进度
stop() 停止播放,并预归位到0。
suspend() 播放到最后
pause() 暂停播放
resume() 继续播放
reset() 播放到最开始
“在dur时间内,每隔frameTime时间,播放一次animFun(per)。”
例如:一个“在3秒时间内,每0.1秒,播放function(per){document.getElementById('div1').style.height = (per * 100) +'px';}”
对应的效果就是:在3秒时间内,将div1元素的高度从0变成100。
代码如下:

<HTML>
<HEAD>
<META content="text/html; charset=gb2312" http-equiv=Content-Type>
<TITLE>JK Test</TITLE>
<script src="http://dev.qwrap.com/resource/js/apps/core_dom_youa.combo.js"></script>
<script src="http://dev.qwrap.com/resource/js/wagang/anim/anim_base.js"></script>
<style>
#div1{background-color:#ccc;overflow:hidden;height:100px;}
</style>
</HEAD>
<body>
<div id="div1" onclick="test();">点我</div>
</body>
<script type="text/javascript">
function test(){
var anim=new QW.Anim(
function(per){
document.getElementById('div1').style.height=(100*per)+'px';
},
3000,
{frameTime:100}
);
anim.play();
}
</script>
</HTML>
待续。。。。
附:QWrap网址:http://www.qwrap.com
推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架