简化代码学习jquery动画源码

发布于 2022-09-12 20:49:53 字数 14923 浏览 16 评论 0

效果见这里: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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文