effects.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. define([
  2. "./core",
  3. "./var/pnum",
  4. "./css/var/cssExpand",
  5. "./css/var/isHidden",
  6. "./css/defaultDisplay",
  7. "./data/var/data_priv",
  8. "./core/init",
  9. "./effects/Tween",
  10. "./queue",
  11. "./css",
  12. "./deferred",
  13. "./traversing"
  14. ], function( jQuery, pnum, cssExpand, isHidden, defaultDisplay, data_priv ) {
  15. var
  16. fxNow, timerId,
  17. rfxtypes = /^(?:toggle|show|hide)$/,
  18. rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
  19. rrun = /queueHooks$/,
  20. animationPrefilters = [ defaultPrefilter ],
  21. tweeners = {
  22. "*": [ function( prop, value ) {
  23. var tween = this.createTween( prop, value ),
  24. target = tween.cur(),
  25. parts = rfxnum.exec( value ),
  26. unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
  27. // Starting value computation is required for potential unit mismatches
  28. start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
  29. rfxnum.exec( jQuery.css( tween.elem, prop ) ),
  30. scale = 1,
  31. maxIterations = 20;
  32. if ( start && start[ 3 ] !== unit ) {
  33. // Trust units reported by jQuery.css
  34. unit = unit || start[ 3 ];
  35. // Make sure we update the tween properties later on
  36. parts = parts || [];
  37. // Iteratively approximate from a nonzero starting point
  38. start = +target || 1;
  39. do {
  40. // If previous iteration zeroed out, double until we get *something*
  41. // Use a string for doubling factor so we don't accidentally see scale as unchanged below
  42. scale = scale || ".5";
  43. // Adjust and apply
  44. start = start / scale;
  45. jQuery.style( tween.elem, prop, start + unit );
  46. // Update scale, tolerating zero or NaN from tween.cur()
  47. // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
  48. } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
  49. }
  50. // Update tween properties
  51. if ( parts ) {
  52. start = tween.start = +start || +target || 0;
  53. tween.unit = unit;
  54. // If a +=/-= token was provided, we're doing a relative animation
  55. tween.end = parts[ 1 ] ?
  56. start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
  57. +parts[ 2 ];
  58. }
  59. return tween;
  60. } ]
  61. };
  62. // Animations created synchronously will run synchronously
  63. function createFxNow() {
  64. setTimeout(function() {
  65. fxNow = undefined;
  66. });
  67. return ( fxNow = jQuery.now() );
  68. }
  69. // Generate parameters to create a standard animation
  70. function genFx( type, includeWidth ) {
  71. var which,
  72. i = 0,
  73. attrs = { height: type };
  74. // if we include width, step value is 1 to do all cssExpand values,
  75. // if we don't include width, step value is 2 to skip over Left and Right
  76. includeWidth = includeWidth ? 1 : 0;
  77. for ( ; i < 4 ; i += 2 - includeWidth ) {
  78. which = cssExpand[ i ];
  79. attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
  80. }
  81. if ( includeWidth ) {
  82. attrs.opacity = attrs.width = type;
  83. }
  84. return attrs;
  85. }
  86. function createTween( value, prop, animation ) {
  87. var tween,
  88. collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
  89. index = 0,
  90. length = collection.length;
  91. for ( ; index < length; index++ ) {
  92. if ( (tween = collection[ index ].call( animation, prop, value )) ) {
  93. // we're done with this property
  94. return tween;
  95. }
  96. }
  97. }
  98. function defaultPrefilter( elem, props, opts ) {
  99. /* jshint validthis: true */
  100. var prop, value, toggle, tween, hooks, oldfire, display,
  101. anim = this,
  102. orig = {},
  103. style = elem.style,
  104. hidden = elem.nodeType && isHidden( elem ),
  105. dataShow = data_priv.get( elem, "fxshow" );
  106. // handle queue: false promises
  107. if ( !opts.queue ) {
  108. hooks = jQuery._queueHooks( elem, "fx" );
  109. if ( hooks.unqueued == null ) {
  110. hooks.unqueued = 0;
  111. oldfire = hooks.empty.fire;
  112. hooks.empty.fire = function() {
  113. if ( !hooks.unqueued ) {
  114. oldfire();
  115. }
  116. };
  117. }
  118. hooks.unqueued++;
  119. anim.always(function() {
  120. // doing this makes sure that the complete handler will be called
  121. // before this completes
  122. anim.always(function() {
  123. hooks.unqueued--;
  124. if ( !jQuery.queue( elem, "fx" ).length ) {
  125. hooks.empty.fire();
  126. }
  127. });
  128. });
  129. }
  130. // height/width overflow pass
  131. if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
  132. // Make sure that nothing sneaks out
  133. // Record all 3 overflow attributes because IE9-10 do not
  134. // change the overflow attribute when overflowX and
  135. // overflowY are set to the same value
  136. opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
  137. // Set display property to inline-block for height/width
  138. // animations on inline elements that are having width/height animated
  139. display = jQuery.css( elem, "display" );
  140. // Get default display if display is currently "none"
  141. if ( display === "none" ) {
  142. display = defaultDisplay( elem.nodeName );
  143. }
  144. if ( display === "inline" &&
  145. jQuery.css( elem, "float" ) === "none" ) {
  146. style.display = "inline-block";
  147. }
  148. }
  149. if ( opts.overflow ) {
  150. style.overflow = "hidden";
  151. anim.always(function() {
  152. style.overflow = opts.overflow[ 0 ];
  153. style.overflowX = opts.overflow[ 1 ];
  154. style.overflowY = opts.overflow[ 2 ];
  155. });
  156. }
  157. // show/hide pass
  158. for ( prop in props ) {
  159. value = props[ prop ];
  160. if ( rfxtypes.exec( value ) ) {
  161. delete props[ prop ];
  162. toggle = toggle || value === "toggle";
  163. if ( value === ( hidden ? "hide" : "show" ) ) {
  164. // If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
  165. if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
  166. hidden = true;
  167. } else {
  168. continue;
  169. }
  170. }
  171. orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
  172. }
  173. }
  174. if ( !jQuery.isEmptyObject( orig ) ) {
  175. if ( dataShow ) {
  176. if ( "hidden" in dataShow ) {
  177. hidden = dataShow.hidden;
  178. }
  179. } else {
  180. dataShow = data_priv.access( elem, "fxshow", {} );
  181. }
  182. // store state if its toggle - enables .stop().toggle() to "reverse"
  183. if ( toggle ) {
  184. dataShow.hidden = !hidden;
  185. }
  186. if ( hidden ) {
  187. jQuery( elem ).show();
  188. } else {
  189. anim.done(function() {
  190. jQuery( elem ).hide();
  191. });
  192. }
  193. anim.done(function() {
  194. var prop;
  195. data_priv.remove( elem, "fxshow" );
  196. for ( prop in orig ) {
  197. jQuery.style( elem, prop, orig[ prop ] );
  198. }
  199. });
  200. for ( prop in orig ) {
  201. tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
  202. if ( !( prop in dataShow ) ) {
  203. dataShow[ prop ] = tween.start;
  204. if ( hidden ) {
  205. tween.end = tween.start;
  206. tween.start = prop === "width" || prop === "height" ? 1 : 0;
  207. }
  208. }
  209. }
  210. }
  211. }
  212. function propFilter( props, specialEasing ) {
  213. var index, name, easing, value, hooks;
  214. // camelCase, specialEasing and expand cssHook pass
  215. for ( index in props ) {
  216. name = jQuery.camelCase( index );
  217. easing = specialEasing[ name ];
  218. value = props[ index ];
  219. if ( jQuery.isArray( value ) ) {
  220. easing = value[ 1 ];
  221. value = props[ index ] = value[ 0 ];
  222. }
  223. if ( index !== name ) {
  224. props[ name ] = value;
  225. delete props[ index ];
  226. }
  227. hooks = jQuery.cssHooks[ name ];
  228. if ( hooks && "expand" in hooks ) {
  229. value = hooks.expand( value );
  230. delete props[ name ];
  231. // not quite $.extend, this wont overwrite keys already present.
  232. // also - reusing 'index' from above because we have the correct "name"
  233. for ( index in value ) {
  234. if ( !( index in props ) ) {
  235. props[ index ] = value[ index ];
  236. specialEasing[ index ] = easing;
  237. }
  238. }
  239. } else {
  240. specialEasing[ name ] = easing;
  241. }
  242. }
  243. }
  244. function Animation( elem, properties, options ) {
  245. var result,
  246. stopped,
  247. index = 0,
  248. length = animationPrefilters.length,
  249. deferred = jQuery.Deferred().always( function() {
  250. // don't match elem in the :animated selector
  251. delete tick.elem;
  252. }),
  253. tick = function() {
  254. if ( stopped ) {
  255. return false;
  256. }
  257. var currentTime = fxNow || createFxNow(),
  258. remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
  259. // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
  260. temp = remaining / animation.duration || 0,
  261. percent = 1 - temp,
  262. index = 0,
  263. length = animation.tweens.length;
  264. for ( ; index < length ; index++ ) {
  265. animation.tweens[ index ].run( percent );
  266. }
  267. deferred.notifyWith( elem, [ animation, percent, remaining ]);
  268. if ( percent < 1 && length ) {
  269. return remaining;
  270. } else {
  271. deferred.resolveWith( elem, [ animation ] );
  272. return false;
  273. }
  274. },
  275. animation = deferred.promise({
  276. elem: elem,
  277. props: jQuery.extend( {}, properties ),
  278. opts: jQuery.extend( true, { specialEasing: {} }, options ),
  279. originalProperties: properties,
  280. originalOptions: options,
  281. startTime: fxNow || createFxNow(),
  282. duration: options.duration,
  283. tweens: [],
  284. createTween: function( prop, end ) {
  285. var tween = jQuery.Tween( elem, animation.opts, prop, end,
  286. animation.opts.specialEasing[ prop ] || animation.opts.easing );
  287. animation.tweens.push( tween );
  288. return tween;
  289. },
  290. stop: function( gotoEnd ) {
  291. var index = 0,
  292. // if we are going to the end, we want to run all the tweens
  293. // otherwise we skip this part
  294. length = gotoEnd ? animation.tweens.length : 0;
  295. if ( stopped ) {
  296. return this;
  297. }
  298. stopped = true;
  299. for ( ; index < length ; index++ ) {
  300. animation.tweens[ index ].run( 1 );
  301. }
  302. // resolve when we played the last frame
  303. // otherwise, reject
  304. if ( gotoEnd ) {
  305. deferred.resolveWith( elem, [ animation, gotoEnd ] );
  306. } else {
  307. deferred.rejectWith( elem, [ animation, gotoEnd ] );
  308. }
  309. return this;
  310. }
  311. }),
  312. props = animation.props;
  313. propFilter( props, animation.opts.specialEasing );
  314. for ( ; index < length ; index++ ) {
  315. result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
  316. if ( result ) {
  317. return result;
  318. }
  319. }
  320. jQuery.map( props, createTween, animation );
  321. if ( jQuery.isFunction( animation.opts.start ) ) {
  322. animation.opts.start.call( elem, animation );
  323. }
  324. jQuery.fx.timer(
  325. jQuery.extend( tick, {
  326. elem: elem,
  327. anim: animation,
  328. queue: animation.opts.queue
  329. })
  330. );
  331. // attach callbacks from options
  332. return animation.progress( animation.opts.progress )
  333. .done( animation.opts.done, animation.opts.complete )
  334. .fail( animation.opts.fail )
  335. .always( animation.opts.always );
  336. }
  337. jQuery.Animation = jQuery.extend( Animation, {
  338. tweener: function( props, callback ) {
  339. if ( jQuery.isFunction( props ) ) {
  340. callback = props;
  341. props = [ "*" ];
  342. } else {
  343. props = props.split(" ");
  344. }
  345. var prop,
  346. index = 0,
  347. length = props.length;
  348. for ( ; index < length ; index++ ) {
  349. prop = props[ index ];
  350. tweeners[ prop ] = tweeners[ prop ] || [];
  351. tweeners[ prop ].unshift( callback );
  352. }
  353. },
  354. prefilter: function( callback, prepend ) {
  355. if ( prepend ) {
  356. animationPrefilters.unshift( callback );
  357. } else {
  358. animationPrefilters.push( callback );
  359. }
  360. }
  361. });
  362. jQuery.speed = function( speed, easing, fn ) {
  363. var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
  364. complete: fn || !fn && easing ||
  365. jQuery.isFunction( speed ) && speed,
  366. duration: speed,
  367. easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
  368. };
  369. opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
  370. opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
  371. // normalize opt.queue - true/undefined/null -> "fx"
  372. if ( opt.queue == null || opt.queue === true ) {
  373. opt.queue = "fx";
  374. }
  375. // Queueing
  376. opt.old = opt.complete;
  377. opt.complete = function() {
  378. if ( jQuery.isFunction( opt.old ) ) {
  379. opt.old.call( this );
  380. }
  381. if ( opt.queue ) {
  382. jQuery.dequeue( this, opt.queue );
  383. }
  384. };
  385. return opt;
  386. };
  387. jQuery.fn.extend({
  388. fadeTo: function( speed, to, easing, callback ) {
  389. // show any hidden elements after setting opacity to 0
  390. return this.filter( isHidden ).css( "opacity", 0 ).show()
  391. // animate to the value specified
  392. .end().animate({ opacity: to }, speed, easing, callback );
  393. },
  394. animate: function( prop, speed, easing, callback ) {
  395. var empty = jQuery.isEmptyObject( prop ),
  396. optall = jQuery.speed( speed, easing, callback ),
  397. doAnimation = function() {
  398. // Operate on a copy of prop so per-property easing won't be lost
  399. var anim = Animation( this, jQuery.extend( {}, prop ), optall );
  400. // Empty animations, or finishing resolves immediately
  401. if ( empty || data_priv.get( this, "finish" ) ) {
  402. anim.stop( true );
  403. }
  404. };
  405. doAnimation.finish = doAnimation;
  406. return empty || optall.queue === false ?
  407. this.each( doAnimation ) :
  408. this.queue( optall.queue, doAnimation );
  409. },
  410. stop: function( type, clearQueue, gotoEnd ) {
  411. var stopQueue = function( hooks ) {
  412. var stop = hooks.stop;
  413. delete hooks.stop;
  414. stop( gotoEnd );
  415. };
  416. if ( typeof type !== "string" ) {
  417. gotoEnd = clearQueue;
  418. clearQueue = type;
  419. type = undefined;
  420. }
  421. if ( clearQueue && type !== false ) {
  422. this.queue( type || "fx", [] );
  423. }
  424. return this.each(function() {
  425. var dequeue = true,
  426. index = type != null && type + "queueHooks",
  427. timers = jQuery.timers,
  428. data = data_priv.get( this );
  429. if ( index ) {
  430. if ( data[ index ] && data[ index ].stop ) {
  431. stopQueue( data[ index ] );
  432. }
  433. } else {
  434. for ( index in data ) {
  435. if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
  436. stopQueue( data[ index ] );
  437. }
  438. }
  439. }
  440. for ( index = timers.length; index--; ) {
  441. if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
  442. timers[ index ].anim.stop( gotoEnd );
  443. dequeue = false;
  444. timers.splice( index, 1 );
  445. }
  446. }
  447. // start the next in the queue if the last step wasn't forced
  448. // timers currently will call their complete callbacks, which will dequeue
  449. // but only if they were gotoEnd
  450. if ( dequeue || !gotoEnd ) {
  451. jQuery.dequeue( this, type );
  452. }
  453. });
  454. },
  455. finish: function( type ) {
  456. if ( type !== false ) {
  457. type = type || "fx";
  458. }
  459. return this.each(function() {
  460. var index,
  461. data = data_priv.get( this ),
  462. queue = data[ type + "queue" ],
  463. hooks = data[ type + "queueHooks" ],
  464. timers = jQuery.timers,
  465. length = queue ? queue.length : 0;
  466. // enable finishing flag on private data
  467. data.finish = true;
  468. // empty the queue first
  469. jQuery.queue( this, type, [] );
  470. if ( hooks && hooks.stop ) {
  471. hooks.stop.call( this, true );
  472. }
  473. // look for any active animations, and finish them
  474. for ( index = timers.length; index--; ) {
  475. if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
  476. timers[ index ].anim.stop( true );
  477. timers.splice( index, 1 );
  478. }
  479. }
  480. // look for any animations in the old queue and finish them
  481. for ( index = 0; index < length; index++ ) {
  482. if ( queue[ index ] && queue[ index ].finish ) {
  483. queue[ index ].finish.call( this );
  484. }
  485. }
  486. // turn off finishing flag
  487. delete data.finish;
  488. });
  489. }
  490. });
  491. jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
  492. var cssFn = jQuery.fn[ name ];
  493. jQuery.fn[ name ] = function( speed, easing, callback ) {
  494. return speed == null || typeof speed === "boolean" ?
  495. cssFn.apply( this, arguments ) :
  496. this.animate( genFx( name, true ), speed, easing, callback );
  497. };
  498. });
  499. // Generate shortcuts for custom animations
  500. jQuery.each({
  501. slideDown: genFx("show"),
  502. slideUp: genFx("hide"),
  503. slideToggle: genFx("toggle"),
  504. fadeIn: { opacity: "show" },
  505. fadeOut: { opacity: "hide" },
  506. fadeToggle: { opacity: "toggle" }
  507. }, function( name, props ) {
  508. jQuery.fn[ name ] = function( speed, easing, callback ) {
  509. return this.animate( props, speed, easing, callback );
  510. };
  511. });
  512. jQuery.timers = [];
  513. jQuery.fx.tick = function() {
  514. var timer,
  515. i = 0,
  516. timers = jQuery.timers;
  517. fxNow = jQuery.now();
  518. for ( ; i < timers.length; i++ ) {
  519. timer = timers[ i ];
  520. // Checks the timer has not already been removed
  521. if ( !timer() && timers[ i ] === timer ) {
  522. timers.splice( i--, 1 );
  523. }
  524. }
  525. if ( !timers.length ) {
  526. jQuery.fx.stop();
  527. }
  528. fxNow = undefined;
  529. };
  530. jQuery.fx.timer = function( timer ) {
  531. jQuery.timers.push( timer );
  532. if ( timer() ) {
  533. jQuery.fx.start();
  534. } else {
  535. jQuery.timers.pop();
  536. }
  537. };
  538. jQuery.fx.interval = 13;
  539. jQuery.fx.start = function() {
  540. if ( !timerId ) {
  541. timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
  542. }
  543. };
  544. jQuery.fx.stop = function() {
  545. clearInterval( timerId );
  546. timerId = null;
  547. };
  548. jQuery.fx.speeds = {
  549. slow: 600,
  550. fast: 200,
  551. // Default speed
  552. _default: 400
  553. };
  554. return jQuery;
  555. });