effects.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  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 string for doubling 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. // break 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. // otherwise 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, checkDisplay,
  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. // Ensure the complete handler is called before this completes
  121. anim.always(function() {
  122. hooks.unqueued--;
  123. if ( !jQuery.queue( elem, "fx" ).length ) {
  124. hooks.empty.fire();
  125. }
  126. });
  127. });
  128. }
  129. // Height/width overflow pass
  130. if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
  131. // Make sure that nothing sneaks out
  132. // Record all 3 overflow attributes because IE9-10 do not
  133. // change the overflow attribute when overflowX and
  134. // overflowY are set to the same value
  135. opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
  136. // Set display property to inline-block for height/width
  137. // animations on inline elements that are having width/height animated
  138. display = jQuery.css( elem, "display" );
  139. // Test default display if display is currently "none"
  140. checkDisplay = display === "none" ?
  141. data_priv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
  142. if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
  143. style.display = "inline-block";
  144. }
  145. }
  146. if ( opts.overflow ) {
  147. style.overflow = "hidden";
  148. anim.always(function() {
  149. style.overflow = opts.overflow[ 0 ];
  150. style.overflowX = opts.overflow[ 1 ];
  151. style.overflowY = opts.overflow[ 2 ];
  152. });
  153. }
  154. // show/hide pass
  155. for ( prop in props ) {
  156. value = props[ prop ];
  157. if ( rfxtypes.exec( value ) ) {
  158. delete props[ prop ];
  159. toggle = toggle || value === "toggle";
  160. if ( value === ( hidden ? "hide" : "show" ) ) {
  161. // 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
  162. if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
  163. hidden = true;
  164. } else {
  165. continue;
  166. }
  167. }
  168. orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
  169. // Any non-fx value stops us from restoring the original display value
  170. } else {
  171. display = undefined;
  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. // If this is a noop like .hide().hide(), restore an overwritten display value
  211. } else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
  212. style.display = display;
  213. }
  214. }
  215. function propFilter( props, specialEasing ) {
  216. var index, name, easing, value, hooks;
  217. // camelCase, specialEasing and expand cssHook pass
  218. for ( index in props ) {
  219. name = jQuery.camelCase( index );
  220. easing = specialEasing[ name ];
  221. value = props[ index ];
  222. if ( jQuery.isArray( value ) ) {
  223. easing = value[ 1 ];
  224. value = props[ index ] = value[ 0 ];
  225. }
  226. if ( index !== name ) {
  227. props[ name ] = value;
  228. delete props[ index ];
  229. }
  230. hooks = jQuery.cssHooks[ name ];
  231. if ( hooks && "expand" in hooks ) {
  232. value = hooks.expand( value );
  233. delete props[ name ];
  234. // Not quite $.extend, this won't overwrite existing keys.
  235. // Reusing 'index' because we have the correct "name"
  236. for ( index in value ) {
  237. if ( !( index in props ) ) {
  238. props[ index ] = value[ index ];
  239. specialEasing[ index ] = easing;
  240. }
  241. }
  242. } else {
  243. specialEasing[ name ] = easing;
  244. }
  245. }
  246. }
  247. function Animation( elem, properties, options ) {
  248. var result,
  249. stopped,
  250. index = 0,
  251. length = animationPrefilters.length,
  252. deferred = jQuery.Deferred().always( function() {
  253. // Don't match elem in the :animated selector
  254. delete tick.elem;
  255. }),
  256. tick = function() {
  257. if ( stopped ) {
  258. return false;
  259. }
  260. var currentTime = fxNow || createFxNow(),
  261. remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
  262. // Support: Android 2.3
  263. // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
  264. temp = remaining / animation.duration || 0,
  265. percent = 1 - temp,
  266. index = 0,
  267. length = animation.tweens.length;
  268. for ( ; index < length ; index++ ) {
  269. animation.tweens[ index ].run( percent );
  270. }
  271. deferred.notifyWith( elem, [ animation, percent, remaining ]);
  272. if ( percent < 1 && length ) {
  273. return remaining;
  274. } else {
  275. deferred.resolveWith( elem, [ animation ] );
  276. return false;
  277. }
  278. },
  279. animation = deferred.promise({
  280. elem: elem,
  281. props: jQuery.extend( {}, properties ),
  282. opts: jQuery.extend( true, { specialEasing: {} }, options ),
  283. originalProperties: properties,
  284. originalOptions: options,
  285. startTime: fxNow || createFxNow(),
  286. duration: options.duration,
  287. tweens: [],
  288. createTween: function( prop, end ) {
  289. var tween = jQuery.Tween( elem, animation.opts, prop, end,
  290. animation.opts.specialEasing[ prop ] || animation.opts.easing );
  291. animation.tweens.push( tween );
  292. return tween;
  293. },
  294. stop: function( gotoEnd ) {
  295. var index = 0,
  296. // If we are going to the end, we want to run all the tweens
  297. // otherwise we skip this part
  298. length = gotoEnd ? animation.tweens.length : 0;
  299. if ( stopped ) {
  300. return this;
  301. }
  302. stopped = true;
  303. for ( ; index < length ; index++ ) {
  304. animation.tweens[ index ].run( 1 );
  305. }
  306. // Resolve when we played the last frame; otherwise, reject
  307. if ( gotoEnd ) {
  308. deferred.resolveWith( elem, [ animation, gotoEnd ] );
  309. } else {
  310. deferred.rejectWith( elem, [ animation, gotoEnd ] );
  311. }
  312. return this;
  313. }
  314. }),
  315. props = animation.props;
  316. propFilter( props, animation.opts.specialEasing );
  317. for ( ; index < length ; index++ ) {
  318. result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
  319. if ( result ) {
  320. return result;
  321. }
  322. }
  323. jQuery.map( props, createTween, animation );
  324. if ( jQuery.isFunction( animation.opts.start ) ) {
  325. animation.opts.start.call( elem, animation );
  326. }
  327. jQuery.fx.timer(
  328. jQuery.extend( tick, {
  329. elem: elem,
  330. anim: animation,
  331. queue: animation.opts.queue
  332. })
  333. );
  334. // attach callbacks from options
  335. return animation.progress( animation.opts.progress )
  336. .done( animation.opts.done, animation.opts.complete )
  337. .fail( animation.opts.fail )
  338. .always( animation.opts.always );
  339. }
  340. jQuery.Animation = jQuery.extend( Animation, {
  341. tweener: function( props, callback ) {
  342. if ( jQuery.isFunction( props ) ) {
  343. callback = props;
  344. props = [ "*" ];
  345. } else {
  346. props = props.split(" ");
  347. }
  348. var prop,
  349. index = 0,
  350. length = props.length;
  351. for ( ; index < length ; index++ ) {
  352. prop = props[ index ];
  353. tweeners[ prop ] = tweeners[ prop ] || [];
  354. tweeners[ prop ].unshift( callback );
  355. }
  356. },
  357. prefilter: function( callback, prepend ) {
  358. if ( prepend ) {
  359. animationPrefilters.unshift( callback );
  360. } else {
  361. animationPrefilters.push( callback );
  362. }
  363. }
  364. });
  365. jQuery.speed = function( speed, easing, fn ) {
  366. var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
  367. complete: fn || !fn && easing ||
  368. jQuery.isFunction( speed ) && speed,
  369. duration: speed,
  370. easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
  371. };
  372. opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
  373. opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
  374. // Normalize opt.queue - true/undefined/null -> "fx"
  375. if ( opt.queue == null || opt.queue === true ) {
  376. opt.queue = "fx";
  377. }
  378. // Queueing
  379. opt.old = opt.complete;
  380. opt.complete = function() {
  381. if ( jQuery.isFunction( opt.old ) ) {
  382. opt.old.call( this );
  383. }
  384. if ( opt.queue ) {
  385. jQuery.dequeue( this, opt.queue );
  386. }
  387. };
  388. return opt;
  389. };
  390. jQuery.fn.extend({
  391. fadeTo: function( speed, to, easing, callback ) {
  392. // Show any hidden elements after setting opacity to 0
  393. return this.filter( isHidden ).css( "opacity", 0 ).show()
  394. // Animate to the value specified
  395. .end().animate({ opacity: to }, speed, easing, callback );
  396. },
  397. animate: function( prop, speed, easing, callback ) {
  398. var empty = jQuery.isEmptyObject( prop ),
  399. optall = jQuery.speed( speed, easing, callback ),
  400. doAnimation = function() {
  401. // Operate on a copy of prop so per-property easing won't be lost
  402. var anim = Animation( this, jQuery.extend( {}, prop ), optall );
  403. // Empty animations, or finishing resolves immediately
  404. if ( empty || data_priv.get( this, "finish" ) ) {
  405. anim.stop( true );
  406. }
  407. };
  408. doAnimation.finish = doAnimation;
  409. return empty || optall.queue === false ?
  410. this.each( doAnimation ) :
  411. this.queue( optall.queue, doAnimation );
  412. },
  413. stop: function( type, clearQueue, gotoEnd ) {
  414. var stopQueue = function( hooks ) {
  415. var stop = hooks.stop;
  416. delete hooks.stop;
  417. stop( gotoEnd );
  418. };
  419. if ( typeof type !== "string" ) {
  420. gotoEnd = clearQueue;
  421. clearQueue = type;
  422. type = undefined;
  423. }
  424. if ( clearQueue && type !== false ) {
  425. this.queue( type || "fx", [] );
  426. }
  427. return this.each(function() {
  428. var dequeue = true,
  429. index = type != null && type + "queueHooks",
  430. timers = jQuery.timers,
  431. data = data_priv.get( this );
  432. if ( index ) {
  433. if ( data[ index ] && data[ index ].stop ) {
  434. stopQueue( data[ index ] );
  435. }
  436. } else {
  437. for ( index in data ) {
  438. if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
  439. stopQueue( data[ index ] );
  440. }
  441. }
  442. }
  443. for ( index = timers.length; index--; ) {
  444. if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
  445. timers[ index ].anim.stop( gotoEnd );
  446. dequeue = false;
  447. timers.splice( index, 1 );
  448. }
  449. }
  450. // Start the next in the queue if the last step wasn't forced.
  451. // Timers currently will call their complete callbacks, which
  452. // will dequeue but only if they were gotoEnd.
  453. if ( dequeue || !gotoEnd ) {
  454. jQuery.dequeue( this, type );
  455. }
  456. });
  457. },
  458. finish: function( type ) {
  459. if ( type !== false ) {
  460. type = type || "fx";
  461. }
  462. return this.each(function() {
  463. var index,
  464. data = data_priv.get( this ),
  465. queue = data[ type + "queue" ],
  466. hooks = data[ type + "queueHooks" ],
  467. timers = jQuery.timers,
  468. length = queue ? queue.length : 0;
  469. // Enable finishing flag on private data
  470. data.finish = true;
  471. // Empty the queue first
  472. jQuery.queue( this, type, [] );
  473. if ( hooks && hooks.stop ) {
  474. hooks.stop.call( this, true );
  475. }
  476. // Look for any active animations, and finish them
  477. for ( index = timers.length; index--; ) {
  478. if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
  479. timers[ index ].anim.stop( true );
  480. timers.splice( index, 1 );
  481. }
  482. }
  483. // Look for any animations in the old queue and finish them
  484. for ( index = 0; index < length; index++ ) {
  485. if ( queue[ index ] && queue[ index ].finish ) {
  486. queue[ index ].finish.call( this );
  487. }
  488. }
  489. // Turn off finishing flag
  490. delete data.finish;
  491. });
  492. }
  493. });
  494. jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
  495. var cssFn = jQuery.fn[ name ];
  496. jQuery.fn[ name ] = function( speed, easing, callback ) {
  497. return speed == null || typeof speed === "boolean" ?
  498. cssFn.apply( this, arguments ) :
  499. this.animate( genFx( name, true ), speed, easing, callback );
  500. };
  501. });
  502. // Generate shortcuts for custom animations
  503. jQuery.each({
  504. slideDown: genFx("show"),
  505. slideUp: genFx("hide"),
  506. slideToggle: genFx("toggle"),
  507. fadeIn: { opacity: "show" },
  508. fadeOut: { opacity: "hide" },
  509. fadeToggle: { opacity: "toggle" }
  510. }, function( name, props ) {
  511. jQuery.fn[ name ] = function( speed, easing, callback ) {
  512. return this.animate( props, speed, easing, callback );
  513. };
  514. });
  515. jQuery.timers = [];
  516. jQuery.fx.tick = function() {
  517. var timer,
  518. i = 0,
  519. timers = jQuery.timers;
  520. fxNow = jQuery.now();
  521. for ( ; i < timers.length; i++ ) {
  522. timer = timers[ i ];
  523. // Checks the timer has not already been removed
  524. if ( !timer() && timers[ i ] === timer ) {
  525. timers.splice( i--, 1 );
  526. }
  527. }
  528. if ( !timers.length ) {
  529. jQuery.fx.stop();
  530. }
  531. fxNow = undefined;
  532. };
  533. jQuery.fx.timer = function( timer ) {
  534. jQuery.timers.push( timer );
  535. if ( timer() ) {
  536. jQuery.fx.start();
  537. } else {
  538. jQuery.timers.pop();
  539. }
  540. };
  541. jQuery.fx.interval = 13;
  542. jQuery.fx.start = function() {
  543. if ( !timerId ) {
  544. timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
  545. }
  546. };
  547. jQuery.fx.stop = function() {
  548. clearInterval( timerId );
  549. timerId = null;
  550. };
  551. jQuery.fx.speeds = {
  552. slow: 600,
  553. fast: 200,
  554. // Default speed
  555. _default: 400
  556. };
  557. return jQuery;
  558. });