简化代码学习jquery动画源码
效果见这里:http://bangswork.googlecode.com/svn/trunk/lab/effect/index.html
jquery的动画总体思路是:
有一个fx类专门处理动画,fx的各个实例共享一个timers数组和一个setInterval。对每个传进来的dom的每个属性值都新建一个fx实例去处理,一个fx实例对应一个dom的一个属性的变化。fx里有个step函数,可以计算出当前这个时刻这个属性要达到什么值。
这个step会通过共用的一个setInterval每13毫秒执行一次,这就可以使得它行成动画。另外如果浏览器速度太慢无法达到13毫秒执行一次step,动画也会按时完成,因为是根据当前系统时间计算属性要达到的值的。
每一个fx实例的step都会放进timers数组,实际上setInterval是持续执行timers里的每一个函数,这样只用一个setInterval就让众多属性“一起动”了。step里判断到超过了动画运行的时间,就会返回false让它从timers里移除,timers为空时clearInterval。
下面简单实现这整个过程:
001.(function (window) {
002. var timerId,
003. timers = [],
004. rfxnum = /^([+-]=)?([d+-.]+)(.*)$/,
005. effect = {
006. animate : function (elems, prop, duration) {
007. for ( var i = 0, l = elems.length; i < l; i ++ ) {
008. for ( var p in prop ) {
009. var e = new effect.fx ( elems, p, prop[p], duration ),
010. parts = rfxnum.exec(prop[p]),
011. start = e.cur(), //获得属性初始状态
012. end = parseFloat( parts[2] ),
013. unit = p == "opacity" ? "" : parts[3] || "px";
014. e.custom( start, end, unit );
015. }
016. }
017. },
018. now : function () {
019. return new Date().getTime();
020. },
021. stop : function (elems) {
022. //stop清除timers里于传进的elems有关的元素
023. for ( var i = 0, l = elems.length; i < l; i ++ ) {
024. for ( var j = timers.length - 1; j >= 0; j-- ) {
025. if ( timers[j].elem === elems ) {
026. timers.splice(j, 1);
027. }
028. }
029. };
030. },
031. easing: {
032. //线性变化公式
033. linear: function( p, n, firstNum, diff ) {
034. return firstNum + diff * p;
035. },
036. //缓动公式
037. swing: function( p, n, firstNum, diff ) {
038. return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
039. }
040. },
041. fx : function (elem, name, val, duration) {
042. this.val = val;
043. this.duration = duration;
044. this.elem = elem;
045. this.name = name;
046. }
047. }
048.
049. effect.fx.prototype = {
050. cur : function () {
051. return parseFloat( this.elem.style[this.name] );
052. },
053. custom : function (from, to, unit) {
054. this.startTime = effect.now();
055. this.start = from;
056. this.end = to;
057. this.unit = unit;
058. this.now = this.start;
059. this.state = this.pos= 0; //如果是线性变化 state会一直等于pos 如果是缓动 会通过state计算出pos 即此刻应该达到几分之几的状态
060.
061. //为了让step里的this指向正确,创建一个闭包
062. var self = this;
063. function t() {
064. return self.step();
065. }
066. //指明这个函数对应的dom元素,在stop里可以根据这个元素指定特定dom停止动画
067. t.elem = this.elem
068.
069. timers.push(t)
070. if ( !timerId ) {
071. timerId = setInterval( effect.fx.tick, 13 );
072. }
073. },
074. step : function () {
075. var t = effect.now(),
076. done = true;
077.
078. if ( t >= this.duration + this.startTime ) {
079. this.now = this.end;
080. this.state = this.pos = 1;
081. this.update();
082. return false;
083. } else {
084. var n = t - this.startTime;
085. this.state = n / this.duration;
086.
087. //如果只是线性渐变,这里可以直接this.pos = this.state
088. this.pos = effect.easing.swing( this.state, n, 0, 1, this.duration);
089.
090. this.now = this.start + ( (this.end - this.start) * this.pos );
091. this.update();
092. return true;
093. }
094. },
095. update : function () {
096. this.elem.style[this.name] = this.now + this.unit;
097. }
098. }
099.
100. effect.fx.tick = function () {
101. for ( var i = 0; i < timers.length; i ++ ) {
102. !timers() && timers.splice( i--, 1 );
103. }
104. !timers.length && effect.fx.stop();
105. }
106. effect.fx.stop = function () {
107. clearInterval( timerId );
108. timerId = null;
109. }
110.
111. window.effect = effect;
112.})(window);
这个是把整个过程尽量简化了,jquery做的包括很多细节处理,例如,不同形式的传参方式统一成包装一个对象,处理各种show hide,处理元素单位确保不出错,其中获取元素的width和height的值比较复杂,放在css模块里。还有支持队列的特性,默认是各种动画同步执行,如果需要按队列排序执行就用到queue方法。还有很多,这些全部加起来,源码就变得挺复杂了~
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论