먼저 큐에 대한 기본 지식이 필요합니다. 이전 장을 참조하세요.
관련 튜토리얼: jQuery에서의 애니메이션 처리 요약: http://www.jb51.net/article/42000.htm
jQuery 1.9.1 소스 코드 분석 시리즈(15) 애니메이션 처리 및 완화 애니메이션 코어 Tween: http://www.jb51.net/article/75821.htm
a. 애니메이션 입력 jQuery.fn.animate 함수 실행 과정에 대한 자세한 설명
---------------------------------- --- ---------------------
먼저 애니메이션 관련 매개변수를 얻기 위해 매개변수에 따라 jQuery.speed를 호출하고, 다음과 유사한 객체를 얻어 애니메이션 실행 함수 doAnimation을 생성합니다.
optall = {
complete: fnction(){...},//动画执行完成的回调
duration: 400,//动画执行时长
easing: "swing",//动画效果
queue: "fx",//动画队列
old: false/fnction(){...},
}
var empty = jQuery.isEmptyObject( prop ),
optall = jQuery.speed( speed, easing, callback ),
doAnimation = function() {
//在特征的副本上操作,保证每个特征效果不会被丢失
var anim = Animation( this, jQuery.extend( {}, prop ), optall );
doAnimation.finish = function() {
anim.stop( true );
};
//空动画或完成需要立马解决
if ( empty || jQuery._data( this, "finish" ) ) {
anim.stop( true );
}
};
doAnimation.finish = doAnimation;
//没有动画在执行则马上执行动画,否则将动画压入动画队列等待执行 return empty || optall.queue === false ? this.each( doAnimation ) : this.queue( optall.queue, doAnimation );
b. jQuery 내부 함수 Animation에 대한 자세한 설명
애니메이션(요소, 속성, 옵션) 속성은 애니메이션화할 CSS 기능이고, 옵션은 애니메이션 관련 옵션입니다. "fx"}.
먼저 애니메이션 대기열을 처리하는 데 사용되는 지연 개체를 초기화합니다.
deferred = jQuery.Deferred().always( function() {
// don't match elem in the :animated selector
delete tick.elem;
}), 그런 다음 매 시점마다 실행될 함수 틱을 생성합니다(인접한 두 시점 사이의 이벤트 간격은 기본적으로 13밀리초입니다). 이 틱 함수는 jQuery.timers에 저장되며, 이후 매 시점마다 time jQuery.fx.tick이 실행될 때 꺼내서 실행됩니다.
tick = function() {
if ( stopped ) {
return false;
}
var currentTime = fxNow || createFxNow(),
remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
temp = remaining / animation.duration || 0,
percent = 1 - temp,
index = 0,
length = animation.tweens.length;
//执行动画效果
for ( ; index < length ; index++ ) {
animation.tweens[ index ].run( percent );
}
//生成进度报告
deferred.notifyWith( elem, [ animation, percent, remaining ]);
if ( percent < 1 && length ) {
return remaining;
} else {
//动画执行完毕,执行所有延时队列中的函数(包括清除动画相关的数据)
deferred.resolveWith( elem, [ animation ] );
return false;
}
}
remaining = Math.max( 0, animation.startTime + animation.duration - currentTime )temp = remaining / animation.duration || 0,percent = 1 - temp,
셋째, 애니메이션에 사용되는 모든 기능으로 구성된 객체 애니메이션을 생성합니다(이 객체의 구조는 소스 코드와 같습니다). animation.props에 저장되는 것은 사용자가 전달한 기능입니다. (애니메이션의 최종 목표).
animation = deferred.promise({
elem: elem,
props: jQuery.extend( {}, properties ),
opts: jQuery.extend( true, { specialEasing: {} }, options ),
originalProperties: properties,
originalOptions: options,
startTime: fxNow || createFxNow(),
duration: options.duration,
tweens: [],
createTween: function( prop, end ) {
var tween = jQuery.Tween( elem, animation.opts, prop, end,
animation.opts.specialEasing[ prop ] || animation.opts.easing );
animation.tweens.push( tween );
return tween;
},
stop: function( gotoEnd ) {
var index = 0,
// if we are going to the end, we want to run all the tweens
// otherwise we skip this part
length = gotoEnd ? animation.tweens.length : 0;
if ( stopped ) {
return this;
}
stopped = true;
for ( ; index < length ; index++ ) {
animation.tweens[ index ].run( 1 );
}
// resolve when we played the last frame
// otherwise, reject
if ( gotoEnd ) {
deferred.resolveWith( elem, [ animation, gotoEnd ] );
} else {
deferred.rejectWith( elem, [ animation, gotoEnd ] );
}
return this;
}
}) 넷째, CSS 기능 이름을 브라우저에서 인식할 수 있도록 propFilter를 호출합니다. borderWidth/padding/margin은 하나의 CSS 기능을 참조하는 것이 아니라 4개(상단, 하단)를 참조합니다. , 왼쪽, 오른쪽)
//经过propFilter,animation.opts.specialEasing添加了相应的特征 propFilter( props, animation.opts.specialEasing );
props = { height: 200 }
animation.opts.specialEasing = {height: undefined}
props = { marginBottom: 200,marginLeft: 200,marginRight: 200,marginTop: 200 }
animation.opts.specialEasing = { marginBottom: undefined,marginLeft: undefined,marginRight: undefined,marginTop: undefined } 다섯째, 적응 처리를 위해 defaultPrefilter를 호출합니다. 예를 들어 높이/너비 애니메이션이 효과를 가지려면 표시 및 오버플로가 특정 값이어야 합니다. 예를 들어 표시/숨기기 애니메이션에는 많은 CSS가 필요합니다. 기능 값을 지정하고 함수에서 createTweens를 호출하여 이징 애니메이션을 생성합니다.
// animationPrefilters[0] = defaultPrefilter
for ( ; index < length ; index++ ) {
result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
if ( result ) {
return result;
}
}
// height/width overflow pass
if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
//确保没有什么偷偷出来
//记录3个overflow相关特征,因为IE不能改变overflow特征值,
//当overflowX和overflowY设置了相同的值
opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
// 内联元素中height/width相关动画需要设置display特征值为inline-block
if ( jQuery.css( elem, "display" ) === "inline" &&
jQuery.css( elem, "float" ) === "none" ) {
// 内联元素接受inline-block;
// 块级元素必须内嵌在布局上
if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
style.display = "inline-block";
} else {
style.zoom = 1;
}
}
}
//对于height/width动画overflow都要设置为"hidden",动画完成后恢复
if ( opts.overflow ) {
style.overflow = "hidden";
//收缩包装块
if ( !jQuery.support.shrinkWrapBlocks ) {
anim.always(function() {
style.overflow = opts.overflow[ 0 ];
style.overflowX = opts.overflow[ 1 ];
style.overflowY = opts.overflow[ 2 ];
});
}
}
props = {
height: "hide"
marginBottom: "hide"
marginLeft: "hide"
marginRight: "hide"
marginTop: "hide"
opacity: "hide"
paddingBottom: "hide"
paddingLeft: "hide"
paddingRight: "hide"
paddingTop: "hide"
width: "hide"
}
for ( index in props ) {
value = props[ index ];
//rfxtypes = /^(?:toggle|show|hide)$/。可以看到最终只有和show/hide的动画才会被饶茹handled中
if ( rfxtypes.exec( value ) ) {
delete props[ index ];
toggle = toggle || value === "toggle";
//如果当前节点的状态和指定的状态相同则不需要处理直接进行下一个状态判断
if ( value === ( hidden ? "hide" : "show" ) ) {
continue;
}
handled.push( index );
}
}
//有需要执行的动画处理则进入分支,里面会对各个特征动画生成缓动动画
length = handled.length;
if ( length ) {
dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
if ( "hidden" in dataShow ) {
hidden = dataShow.hidden;
}
// toggle需要保存状态 - enables .stop().toggle() to "reverse"
if ( toggle ) {
dataShow.hidden = !hidden;
}
if ( hidden ) {
jQuery( elem ).show();
} else {
anim.done(function() {
jQuery( elem ).hide();
});
}
anim.done(function() {
var prop;
jQuery._removeData( elem, "fxshow" );
for ( prop in orig ) {
jQuery.style( elem, prop, orig[ prop ] );
}
});
for ( index = 0 ; index < length ; index++ ) {
prop = handled[ index ];
//生成缓动动画
tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
if ( !( prop in dataShow ) ) {
dataShow[ prop ] = tween.start;
if ( hidden ) {
tween.end = tween.start;
tween.start = prop === "width" || prop === "height" ? 1 : 0;
}
}
}
} 여섯 번째, 이징 애니메이션 생성, 표시/숨기기는 defaultPrefilter 함수(위 소스 코드)에서 처리되었습니다.
createTweens( animation, props );
我们来看一看createTweens中具体做了什么,先看一下createTweens之前的animation对象

然后看一下经过createTweens之后的animation对象的tweens数组变成了

将margin分解成了四个属性(marginTop/Right/Bottom/Left)并且每个属性都有自己的动画特征。
第七,启动动画计时,定时执行tick
//启动动画计时
jQuery.fx.timer(
jQuery.extend( tick, {
elem: elem,
anim: animation,
queue: animation.opts.queue
})
); 最后,将传入的动画结束回调加入延时队列
//从options中获取回调函数添加到延时队列中 return animation.progress( animation.opts.progress ) .done( animation.opts.done, animation.opts.complete ) .fail( animation.opts.fail ) .always( animation.opts.always );
Animation函数流程到此为止
拓展:
前面提到的genFx函数是专门用在toggle、hide、show时获取相关的需要动画的特征的
最终生成的attrs = {
height: "show",
marginTop: "show",
marginRight: "show",//当includeWidth为false时没有
marginBottom: "show",
marginLeft: "show",//当includeWidth为false时没有
opacity: "show",
width: "show"
}
function genFx( type, includeWidth ) {
var which,
attrs = { height: type },
i = 0;
//如果包括宽度,步长值为1来完成所有cssExpand值,
//如果不包括宽度,步长值是2跳过左/右值
//cssExpand = [ "Top", "Right", "Bottom", "Left" ]
includeWidth = includeWidth? 1 : 0;
for( ; i < 4 ; i += 2 - includeWidth ) {
which = cssExpand[ i ];
attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
}
if ( includeWidth ) {
attrs.opacity = attrs.width = type;
}
return attrs;
} Animation函数比较复杂,童鞋们可以随便使用例子去跟踪代码。这个是理解jQuery源码的一种比较好的方式。推荐两个例子:
第一个,有hide/show的例子:$("#id").hide(1000);
第二个,其他例子:$("#id").animate({"marginLeft":500},1000);
jQuery 1.9.1源码分析系列(十五)之动画处理 的全部内容就给大家介绍到这里,有问题随时给我留言,谢谢。!