首先需要有队列(queue)的基本知识。见上一章。
相关教程:jQuery下的动画处理总结: https://www.jb51.net/article/42000.htm
jQuery 1.9.1源码分析系列(十五)动画处理之缓动动画核心Tween :https://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 "color: #0000ff">b. jQuery内部函数Animation详解
--------------------------------------------------------------------------------
Animation ( elem, properties, options ). properties是要进行动画的css特征,options是动画相关选项{complete: function () {…},duration: 400,easing: undefined,old: false,queue: "fx"}。
首先,初始化一个延时对象,这个延时对象用来处理动画队列。
deferred = jQuery.Deferred().always( function() { // don't match elem in the :animated selector delete tick.elem; }),然后,生成一个每一个时间点(相邻两个时间点的事件间隔默认为13毫秒)上都会执行的函数tick,这个tick函数会保存在jQuery.timers中,然后每次执行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; } }我们看到jQuery对动画进度的处理:
remaining = Math.max( 0, animation.startTime + animation.duration - currentTime )temp = remaining / animation.duration || 0,percent = 1 - temp,进度百分比 = 1 - 剩余时间百分比。
平常我们是这么处理:假设时间13毫秒执行一次动画,当前是第n此执行,总的动画时长为T。那么
进度百分比 = (n*13)/T
实际上这种算法得到的时间n*13是不准确的,因为cpu不只是你一个程序在执行,时间片分给你的时候往往都比n*13大。而且是一个很不准确的值,导致动画感觉时快时慢,不连贯。而jQuery这种方式保证当前的事件点上动画执行结果的准确性,毕竟事件是最新计算结果。
第三,生成动画用的所有特征组成的对象animation(这个对象结构如源码所示),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 "htmlcode">//经过propFilter,animation.opts.specialEasing添加了相应的特征 propFilter( props, animation.opts.specialEasing );举例说明propFilter修正成果。
例1,css特征{ height: 200 }的修正后结果为:
props = { height: 200 } animation.opts.specialEasing = {height: undefined}例2:,css特征{margin:200}的修正结果为:
props = { marginBottom: 200,marginLeft: 200,marginRight: 200,marginTop: 200 } animation.opts.specialEasing = { marginBottom: undefined,marginLeft: undefined,marginRight: undefined,marginTop: undefined }第五,调用defaultPrefilter做适配处理:比如对height/width的动画要求display和overflow为特定的值才能有效果;比如对show/hide动画需要对一大堆css特征值进行动画,并且在函数里就调用createTweens生成缓动动画。
// animationPrefilters[0] = defaultPrefilter for ( ; index < length ; index++ ) { result = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); if ( result ) { return result; } }其中animationPrefilters[ index ]值得函数就是defaultPrefilter,defaultPrefilter函数处理有几个比较重要的地方
defaultPrefilter重点1:内联元素中height/width相关动画需要设置display特征值为inline-block
// 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; } } }defaultPrefilter重点2:对于height/width动画overflow都要设置为"hidden",动画完成后恢复。这个有利于提高渲染速度。
//对于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 ]; }); } }defaultPrefilter重点3:show/hide动画的特殊处理:show/hide动画调用genFx得到形如
props = { height: "hide" marginBottom: "hide" marginLeft: "hide" marginRight: "hide" marginTop: "hide" opacity: "hide" paddingBottom: "hide" paddingLeft: "hide" paddingRight: "hide" paddingTop: "hide" width: "hide" }需要进行动画处理的特征压入handled列表,并将相应的特征删除,后面会生成相应的缓动动画。
for ( index in props ) { value = props[ index ]; //rfxtypes = /^("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 "width" || prop === "height" "htmlcode">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"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源码分析系列(十五)之动画处理 的全部内容就给大家介绍到这里,有问题随时给我留言,谢谢。!
相思资源网 Design By www.200059.com
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。