
可以看到上面的缓动动画组有四个原子动画组成。每一个原子动画的信息都包含在里面了。
仔细查看createTweens函数,实际上就是遍历调用了tweeners ["*"]的数组中的函数(实际上就只有一个元素)。
function createTweens( animation, props ) {jQuery.each( props, function( prop, value ) {var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),index = 0,length = collection.length;for ( ; index < length; index++ ) {if ( collection[ index ].call( animation, prop, value ) ) {// we"re done with this propertyreturn;}}});} 再次查看这个tweeners ["*"][0]函数,主要代码如下function( prop, value ) {var end, unit,//根据css特征值获取缓动动画结构tween = this.createTween( prop, value ),parts = rfxnum.exec( value ),target = tween.cur(),start = +target || 0,scale = 1,maxIterations = 20;if ( parts ) {end = +parts[2];unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );//非像素单位的属性if ( unit !== "px" && start ) {// 从一个非零起点开始迭代,//对于当前属性,如果它使用相同的单位这一过程将是微不足道// 后备为end,或一个简单的常量start = jQuery.css( tween.elem, prop, true ) || end || 1;do {//如果前一次迭代为零,加倍,直到我们得到*东西* //使用字符串倍增因子,所以我们不会偶然看到scale不改变scale = scale || ".5";// 调整和运行start = start / scale;jQuery.style( tween.elem, prop, start + unit );// 更新scale, 默认0或NaN从tween.cur()获取// 跳出循环,如果scale不变或完成时, 或者我们已经觉得已经足够了} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );}tween.unit = unit;tween.start = start;//如果提供了+=/-=记号,表示我们正在做一个相对的动画tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;}return tween;}]}; 可以看出除了hide/show两种动画外的其他动画都经过tweeners ["*"][0]这个函数封装了动画组。其中有几个关键的数组start/end/unit。特别是对非像素单位的动画start值获取费了一番功夫。function Tween( elem, options, prop, end, easing ) {return new Tween.prototype.init( elem, options, prop, end, easing );}jQuery.Tween = Tween;Tween.prototype = {constructor: Tween,init: function( elem, options, prop, end, easing, unit ) {this.elem = elem;this.prop = prop;this.easing = easing || "swing";this.options = options;this.start = this.now = this.cur();this.end = end;this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );},cur: function() {...},run: function( percent ) {...}};Tween.prototype.init.prototype = Tween.prototype; 是不是有一种很熟悉的赶脚。cur: function() {var hooks = Tween.propHooks[ this.prop ];return hooks && hooks.get ?hooks.get( this ) :Tween.propHooks._default.get( this );}, 而run函数则会在每个动画时间点上对正在进行的动画的每个特征值进行处理。//如果有动画时长则使用jQuery.easing计算出缓动动画进度eased,否则进度eased为percent//并根据进度得到当前动画位置nowif ( this.options.duration ) {this.pos = eased = jQuery.easing[ this.easing ](percent, this.options.duration * percent, 0, 1, this.options.duration);} else {this.pos = eased = percent;}this.now = ( this.end - this.start ) * eased + this.start; 2.根据当前进度情况设置css特征值//设置css特征值if ( hooks && hooks.set ) {hooks.set( this );} else {Tween.propHooks._default.set( this );}return this; 可见生成缓动动画这一步处理才是整个动画的核心:
不同的是hide/show直接在defaultPrefilter中创建了这个缓动动画组(所有的属性都默认是px单位),其他的动画在调用createTweens时创建缓动动画组。
还记不记得在创建动画的时候有个tick函数,这个tick函数会在每隔一个步长的时间调用一次
tick = function() {...length = animation.tweens.length;for ( ; index < length ; index++ ) {animation.tweens[ index ].run( percent );} ...} 看到没,每一个原子动画有自己的run函数来执行自己的动画,这在创建缓动动画组的时候就建好了的。opt = {complete: fnction(){...},//动画执行完成的回调duration: 400,//动画执行时长easing: "swing",//动画效果queue: "fx",//动画队列old: false/fnction(){...},} 2.doAnimation中调用创建一个延时对象,使用延时对象的promise方法构造一个动画对象animation(延时对象+动画特征列表),最后给animation添加动画执行完成后的回调函数。remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),temp = remaining / animation.duration || 0,percent = 1 - temp得到的percent是符合时间规律的。代入这个percent设置准确的css特征值,以刷新动画显示。