jQuery代码分析之一 Animation
本文的代码分析基于jQuery 1.7函数jQuery.animate(params, speed, easing, callback)
params: 需要被渐变的属性及其目标值
这儿需要指出的是jQuery.fx提供了3种特殊的目标属性值:show hide toggle。例如我们可以传入{height: "show"},这样会在height属性上做值渐变来达到显示出来的效果。注意,元素必须处于hidden状态,show值才会有animation,否则直接执行complete回调函数然后结束。下面是对show/hide不同情况处理的分析
动作1 | 动作2 | 结果 |
$("#div1").hide(); |
| 这个时候div1元素会的width属性会从0变到初始值 |
| 这个时候div1元素会首先被显示出来,然后它的width属性会从1秒时候的值变到初始值 | |
| 这个时候div1元素会的width属性会从0变到初始值 |
speed: 数字或者字符串,这个参数本来是指完成整个渐变需要花的时间(以毫秒为单位),因为jQuery本身支持三种速度:slow, normal, fast,所以可以传入字符串,但是只能是刚提到的三种之一。
easing: 是在某一时刻,计算渐变属性当前值的公式,jQuery自带两个基本的easing函数:linear和swing,linear就是属性值随时间作线性变化,swing就是属性值作震荡变化,然后振幅越来越小,最后不变了,就像鸟儿的翅膀上下扇动。如果想要很cool的效果,可以使用easing插件。关于easing插件的使用,网上的例子很多,在这儿就不赘述了。但是有个东西要提一下,在jQuery UI中提供了效果(effect)函数,例如Bind,Bounce等,可以在这儿查看demo效果,原因是easing函数本身就是些原始的数学公司,让很多人觉得头大,计算二次曲线,圆弧上坐标等函数,很难从这些公式知道这个easing函数运行后是什么效果,所以要用好easing,真的不太容易。那effect函数就很直观,使用起来也方便。所以在制作动画效果时先看一下有木已经有effect函数可以满足需求,如果没有的话,那就要好好研究下easing插件了。;-) 另外,这个参数是对在params参数中提到的所有CSS属性值都用同样的easing函数,这儿就有个小问号:如果我希望对各个属性用不同的easing函数的话该怎么办呢?答案是可以在params参数中为单个属性赋easing函数,例如
1 $("#div1").animate({left: ["125px", "linear"],top: ["321px", "easeInCubic"]});
callback:animation结束时的回调函数
说了半天,本来是代码分析的,现在成演讲稿了。上代码:
1 jQuery.extend({ 2 // 注意这儿是fx,而不是fn。fx是animation的内部实现类, 3 // 包括animation用到的成员变量以及成员函数 4 // fn是表示html元素的jQuery包装,就相当于设计模式中的decorator, 5 // 因为访问html元素的属性值存在浏览器不兼容, 6 // 所以fn就提供了一个统一的跨浏览器接口。 7 // 这个就相当于fx的带参构造函数 8 fx:function( elem, options, prop ) { 9 this.options = options; 10 this.elem = elem; 11 this.prop = prop; 12 13 options.orig = options.orig || {}; 14 } 15 });
1 // 这儿定义的是fx的公共成员函数 2 jQuery.fx.prototype = { 3 update: function() { 4 // 调用step函数来更新元素的渐变属性值 5 }, 6 cur: function() { 7 // 获取元素的当前属性值,不容易理解是吧? 8 // 参考上面fx的定义,初始化fx时需要传入一个属性名prop 9 }, 10 custom: function(from, to, unit) { 11 // 设置元素的属性值从from渐变到to,属性值的单位为unit 12 // 关于unit,可以为“px” “em” "%",也可以为"",为什么呢? 13 // 如下CSS属性是没有单位的: 14 // fillOpacity fontWeight lineHeight 15 // opacity orphans widows zIndex zoom 16 // 这些没有单位的CSS属性值放在集合jQuery.cssNumber中 17 this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" ); 18 19 // 里面定义了一个闭包函数,它可以看作是step函数的包装类, 20 // step函数在下面定义的 21 function t( gotoEnd ) { 22 // 注意到self了吧?这个地方有个小技巧, 23 // 调用t时上下文(context)可能会改变, 24 // 所以之前会把指向fx的this保存到self中, 25 // 那这样才能保证调用到fx的step函数了。 26 // 否则就有可能报"找不到step函数"的错误 27 return self.step( gotoEnd ); 28 } 29 // 设置step包装类的成员变量 30 t.queue = this.options.queue; 31 // step包装类的成员函数saveState很有趣, 32 // 它只有当hide值为true并且元素没有名为 33 // "fxshow"+self.prop的附加属性值时 34 // 才会做一件事:保存start值。这里面有几层含义, 35 // 一是只有在调用了fx.hide(),saveState才起作用, 36 // 下面在介绍hide()定义时,将会看到这一点; 37 // 二是只有头一次调用saveState会起作用, 38 // 因为后面调用时附加属性值已经不为undefined了。 39 // 这个函数是为了处理这样一种情况:当hide的过程还没完成时 40 // 调用stop来立即停止animation,这个时候我们需要保存元素的状态, 41 // 可以参考上面提到的show/hide的不同情况的处理2 42 t.saveState = function() { 43 if ( self.options.hide && jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) { 44 jQuery._data( self.elem, "fxshow" + self.prop, self.start ); 45 } 46 }; 47 48 // 从这儿我们可以看出animation的基本实现机制还是依赖于强大的setInternal。 49 // 集合timers里的对象就是刚才提到的step函数的包装类, 50 // 每隔一段时间调用一下这些包装类来更新元素的属性值。 51 if ( t() && jQuery.timers.push(t) && !timerId ) { 52 timerId = setInterval( fx.tick, fx.interval ); 53 } 54 }, 55 show: function() { 56 // 当需要被animate的CSS属性值为show时,处理这种情况, 57 // 最后仍然是调用custom函数,只不过有些参数是特殊值 58 if ( dataShow !== undefined ) { 59 // This show is picking up where a previous hide or show left off 60 this.custom( this.cur(), dataShow ); 61 } else { 62 this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() ); 63 } 64 }, 65 hide: function() { 66 // 当需要被animate的CSS属性值为hide时,处理这种情况, 67 // 最后仍然是调用custom函数,只不过有些参数是特殊值 68 this.custom( this.cur(), 0 ); 69 }, 70 step: function( gotoEnd ) { 71 // 这个函数就是每次internal后的回调函数,更新元素的属性值, 72 // 如果animation已经结束,需要做一些清理工作 73 } 74 };
1 jQuery.extend(jQuery.fx, { 2 tick: function() { 3 var timers = jQuery.timers; 4 // 这个地方会调用所有的fx.step函数来更新元素的被animated的属性值 5 // 至于是怎么调到fx.step的,上面的代码分析里有提到 6 // 属性的值已经到了目标值,那就将其对应的step函数从timers中删除 7 // 这个地方用到了JavaScript中一个很cool的语法: 8 // 可以在轮询集合时,把元素从集合中删掉 9 // 如果对C#熟悉的同学应该清楚,这种方式在C#中是要报异常的 10 for (var i = 0; i < timers.length; i++) 11 if (!timers[i]()) timers.splice(i--, 1); 12 //如果timers中已经没有未完成的属性值未达到目标值, 13 // 那就把这个用setInterval启动的timer取消掉 14 if (!timers.length) jQuery.fx.stop(); 15 }, 16 interval: 13,// 定义setInterval的时间间隔 17 stop: function() { 18 // 这个应该很熟悉吧?;-)就不介绍了 19 // 不熟悉的同学可以搜索一下 20 clearInternal(timerId); 21 timerId = null; 22 }, 23 // 定义默认的animation所需要的时间 24 speeds: { 25 slow: 600, 26 fast: 200, 27 _default: 400 28 }, 29 // 注意这儿也有个step成员变量,但是是个匿名对象 30 // 他主要负责改变元素的属性值,我们知道一旦元素的属性值被修改, 31 // 浏览器的render engine都会重绘页面,这样的修改甚至会 32 // 影响到页面布局,所以最好不要把大量计算和属性值操作夹杂在一起, 33 // 很容易引起animation显示不流畅,也就是会“看起来很卡” 34 step: { 35 opacity: function(fx) { 36 jQuery.style(fx.elem, "opacity", fx.now); 37 }, 38 _default: function(fx) { 39 if (fx.elem.style && fx.elem.style[fx.prop] != null) 40 fx.elem.style[fx.prop] = fx.now + fx.unit; 41 else fx.elem[fx.prop] = fx.now; 42 } 43 } 44 });
问题
animate支持的CSS属性:
- 拥有数值,例如width, height, zoom; 下表列出的是目前FixFox支持的所有可以通过CSS的keyframe新特性能animate的属性,但jQuery默认并不支持所有这些属性
Property Type Property Name Transitionable Values Text properties color color font-size length, percentage font-weight number, keywords(excluding bolder, lighter)) letter-spacing length line-height number, length, percentage text-indent length, percentage text-shadow shadow vertical-align keywords, length, percentage word-spacing length, percentage Box properties background-color color background-image images, gradients background-position percentage, length border-left-color color border-spacing length border-left-width length clip rectangle crop rectangle margin-left length opacity number outline-width length outline-offset integer outline-color color padding-left length width, min-width, max-width length, percentage Positioning properties bottom length, percentage top length, percentage grid-* various left length, percentage right length, percentage visibility visibility z-index integer zoom number SVG properties fill paint server fill-opacity float flood-color color, keywords lighting-color color, keywords marker-offset length stop-color color stop-opacity float stroke paint server stroke-dasharray list of numbers stroke-dashoffset number stroke-miterlimit number stroke-opacity float stroke-width float viewport-fill color viewport-fill-opacity color
animate不支持的CSS属性:
-
拥有非数值,例如background-color
解决方案:使用jQuery插件color
-
shortcut CSS属性,例如font, border
解决方案:使用非shotcut写法,例如我们想对font-size做animate,可以直接设置属性font-size而不是font
-
CSS3的新增属性,例如box-shadow, transform
解决方案:使用transit插件