effects.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. define( [
  2. "./core",
  3. "./var/document",
  4. "./var/rcssNum",
  5. "./var/rnotwhite",
  6. "./css/var/cssExpand",
  7. "./css/var/isHiddenWithinTree",
  8. "./css/var/swap",
  9. "./css/adjustCSS",
  10. "./data/var/dataPriv",
  11. "./css/showHide",
  12. "./core/init",
  13. "./queue",
  14. "./deferred",
  15. "./traversing",
  16. "./manipulation",
  17. "./css",
  18. "./effects/Tween"
  19. ], function( jQuery, document, rcssNum, rnotwhite, cssExpand, isHiddenWithinTree, swap,
  20. adjustCSS, dataPriv, showHide ) {
  21. "use strict";
  22. var
  23. fxNow, timerId,
  24. rfxtypes = /^(?:toggle|show|hide)$/,
  25. rrun = /queueHooks$/;
  26. function raf() {
  27. if ( timerId ) {
  28. window.requestAnimationFrame( raf );
  29. jQuery.fx.tick();
  30. }
  31. }
  32. // Animations created synchronously will run synchronously
  33. function createFxNow() {
  34. window.setTimeout( function() {
  35. fxNow = undefined;
  36. } );
  37. return ( fxNow = jQuery.now() );
  38. }
  39. // Generate parameters to create a standard animation
  40. function genFx( type, includeWidth ) {
  41. var which,
  42. i = 0,
  43. attrs = { height: type };
  44. // If we include width, step value is 1 to do all cssExpand values,
  45. // otherwise step value is 2 to skip over Left and Right
  46. includeWidth = includeWidth ? 1 : 0;
  47. for ( ; i < 4; i += 2 - includeWidth ) {
  48. which = cssExpand[ i ];
  49. attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
  50. }
  51. if ( includeWidth ) {
  52. attrs.opacity = attrs.width = type;
  53. }
  54. return attrs;
  55. }
  56. function createTween( value, prop, animation ) {
  57. var tween,
  58. collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
  59. index = 0,
  60. length = collection.length;
  61. for ( ; index < length; index++ ) {
  62. if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {
  63. // We're done with this property
  64. return tween;
  65. }
  66. }
  67. }
  68. function defaultPrefilter( elem, props, opts ) {
  69. var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
  70. isBox = "width" in props || "height" in props,
  71. anim = this,
  72. orig = {},
  73. style = elem.style,
  74. hidden = elem.nodeType && isHiddenWithinTree( elem ),
  75. dataShow = dataPriv.get( elem, "fxshow" );
  76. // Queue-skipping animations hijack the fx hooks
  77. if ( !opts.queue ) {
  78. hooks = jQuery._queueHooks( elem, "fx" );
  79. if ( hooks.unqueued == null ) {
  80. hooks.unqueued = 0;
  81. oldfire = hooks.empty.fire;
  82. hooks.empty.fire = function() {
  83. if ( !hooks.unqueued ) {
  84. oldfire();
  85. }
  86. };
  87. }
  88. hooks.unqueued++;
  89. anim.always( function() {
  90. // Ensure the complete handler is called before this completes
  91. anim.always( function() {
  92. hooks.unqueued--;
  93. if ( !jQuery.queue( elem, "fx" ).length ) {
  94. hooks.empty.fire();
  95. }
  96. } );
  97. } );
  98. }
  99. // Detect show/hide animations
  100. for ( prop in props ) {
  101. value = props[ prop ];
  102. if ( rfxtypes.test( value ) ) {
  103. delete props[ prop ];
  104. toggle = toggle || value === "toggle";
  105. if ( value === ( hidden ? "hide" : "show" ) ) {
  106. // Pretend to be hidden if this is a "show" and
  107. // there is still data from a stopped show/hide
  108. if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
  109. hidden = true;
  110. // Ignore all other no-op show/hide data
  111. } else {
  112. continue;
  113. }
  114. }
  115. orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
  116. }
  117. }
  118. // Bail out if this is a no-op like .hide().hide()
  119. propTween = !jQuery.isEmptyObject( props );
  120. if ( !propTween && jQuery.isEmptyObject( orig ) ) {
  121. return;
  122. }
  123. // Restrict "overflow" and "display" styles during box animations
  124. if ( isBox && elem.nodeType === 1 ) {
  125. // Support: IE <=9 - 11, Edge 12 - 13
  126. // Record all 3 overflow attributes because IE does not infer the shorthand
  127. // from identically-valued overflowX and overflowY
  128. opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
  129. // Identify a display type, preferring old show/hide data over the CSS cascade
  130. restoreDisplay = dataShow && dataShow.display;
  131. if ( restoreDisplay == null ) {
  132. restoreDisplay = dataPriv.get( elem, "display" );
  133. }
  134. display = jQuery.css( elem, "display" );
  135. if ( display === "none" ) {
  136. if ( restoreDisplay ) {
  137. display = restoreDisplay;
  138. } else {
  139. // Get nonempty value(s) by temporarily forcing visibility
  140. showHide( [ elem ], true );
  141. restoreDisplay = elem.style.display || restoreDisplay;
  142. display = jQuery.css( elem, "display" );
  143. showHide( [ elem ] );
  144. }
  145. }
  146. // Animate inline elements as inline-block
  147. if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) {
  148. if ( jQuery.css( elem, "float" ) === "none" ) {
  149. // Restore the original display value at the end of pure show/hide animations
  150. if ( !propTween ) {
  151. anim.done( function() {
  152. style.display = restoreDisplay;
  153. } );
  154. if ( restoreDisplay == null ) {
  155. display = style.display;
  156. restoreDisplay = display === "none" ? "" : display;
  157. }
  158. }
  159. style.display = "inline-block";
  160. }
  161. }
  162. }
  163. if ( opts.overflow ) {
  164. style.overflow = "hidden";
  165. anim.always( function() {
  166. style.overflow = opts.overflow[ 0 ];
  167. style.overflowX = opts.overflow[ 1 ];
  168. style.overflowY = opts.overflow[ 2 ];
  169. } );
  170. }
  171. // Implement show/hide animations
  172. propTween = false;
  173. for ( prop in orig ) {
  174. // General show/hide setup for this element animation
  175. if ( !propTween ) {
  176. if ( dataShow ) {
  177. if ( "hidden" in dataShow ) {
  178. hidden = dataShow.hidden;
  179. }
  180. } else {
  181. dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } );
  182. }
  183. // Store hidden/visible for toggle so `.stop().toggle()` "reverses"
  184. if ( toggle ) {
  185. dataShow.hidden = !hidden;
  186. }
  187. // Show elements before animating them
  188. if ( hidden ) {
  189. showHide( [ elem ], true );
  190. }
  191. /* eslint-disable no-loop-func */
  192. anim.done( function() {
  193. /* eslint-enable no-loop-func */
  194. // The final step of a "hide" animation is actually hiding the element
  195. if ( !hidden ) {
  196. showHide( [ elem ] );
  197. }
  198. dataPriv.remove( elem, "fxshow" );
  199. for ( prop in orig ) {
  200. jQuery.style( elem, prop, orig[ prop ] );
  201. }
  202. } );
  203. }
  204. // Per-property setup
  205. propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
  206. if ( !( prop in dataShow ) ) {
  207. dataShow[ prop ] = propTween.start;
  208. if ( hidden ) {
  209. propTween.end = propTween.start;
  210. propTween.start = 0;
  211. }
  212. }
  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 = Animation.prefilters.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 only
  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, {
  283. specialEasing: {},
  284. easing: jQuery.easing._default
  285. }, options ),
  286. originalProperties: properties,
  287. originalOptions: options,
  288. startTime: fxNow || createFxNow(),
  289. duration: options.duration,
  290. tweens: [],
  291. createTween: function( prop, end ) {
  292. var tween = jQuery.Tween( elem, animation.opts, prop, end,
  293. animation.opts.specialEasing[ prop ] || animation.opts.easing );
  294. animation.tweens.push( tween );
  295. return tween;
  296. },
  297. stop: function( gotoEnd ) {
  298. var index = 0,
  299. // If we are going to the end, we want to run all the tweens
  300. // otherwise we skip this part
  301. length = gotoEnd ? animation.tweens.length : 0;
  302. if ( stopped ) {
  303. return this;
  304. }
  305. stopped = true;
  306. for ( ; index < length; index++ ) {
  307. animation.tweens[ index ].run( 1 );
  308. }
  309. // Resolve when we played the last frame; otherwise, reject
  310. if ( gotoEnd ) {
  311. deferred.notifyWith( elem, [ animation, 1, 0 ] );
  312. deferred.resolveWith( elem, [ animation, gotoEnd ] );
  313. } else {
  314. deferred.rejectWith( elem, [ animation, gotoEnd ] );
  315. }
  316. return this;
  317. }
  318. } ),
  319. props = animation.props;
  320. propFilter( props, animation.opts.specialEasing );
  321. for ( ; index < length; index++ ) {
  322. result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
  323. if ( result ) {
  324. if ( jQuery.isFunction( result.stop ) ) {
  325. jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
  326. jQuery.proxy( result.stop, result );
  327. }
  328. return result;
  329. }
  330. }
  331. jQuery.map( props, createTween, animation );
  332. if ( jQuery.isFunction( animation.opts.start ) ) {
  333. animation.opts.start.call( elem, animation );
  334. }
  335. jQuery.fx.timer(
  336. jQuery.extend( tick, {
  337. elem: elem,
  338. anim: animation,
  339. queue: animation.opts.queue
  340. } )
  341. );
  342. // attach callbacks from options
  343. return animation.progress( animation.opts.progress )
  344. .done( animation.opts.done, animation.opts.complete )
  345. .fail( animation.opts.fail )
  346. .always( animation.opts.always );
  347. }
  348. jQuery.Animation = jQuery.extend( Animation, {
  349. tweeners: {
  350. "*": [ function( prop, value ) {
  351. var tween = this.createTween( prop, value );
  352. adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
  353. return tween;
  354. } ]
  355. },
  356. tweener: function( props, callback ) {
  357. if ( jQuery.isFunction( props ) ) {
  358. callback = props;
  359. props = [ "*" ];
  360. } else {
  361. props = props.match( rnotwhite );
  362. }
  363. var prop,
  364. index = 0,
  365. length = props.length;
  366. for ( ; index < length; index++ ) {
  367. prop = props[ index ];
  368. Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
  369. Animation.tweeners[ prop ].unshift( callback );
  370. }
  371. },
  372. prefilters: [ defaultPrefilter ],
  373. prefilter: function( callback, prepend ) {
  374. if ( prepend ) {
  375. Animation.prefilters.unshift( callback );
  376. } else {
  377. Animation.prefilters.push( callback );
  378. }
  379. }
  380. } );
  381. jQuery.speed = function( speed, easing, fn ) {
  382. var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
  383. complete: fn || !fn && easing ||
  384. jQuery.isFunction( speed ) && speed,
  385. duration: speed,
  386. easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
  387. };
  388. // Go to the end state if fx are off or if document is hidden
  389. if ( jQuery.fx.off || document.hidden ) {
  390. opt.duration = 0;
  391. } else {
  392. opt.duration = typeof opt.duration === "number" ?
  393. opt.duration : opt.duration in jQuery.fx.speeds ?
  394. jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
  395. }
  396. // Normalize opt.queue - true/undefined/null -> "fx"
  397. if ( opt.queue == null || opt.queue === true ) {
  398. opt.queue = "fx";
  399. }
  400. // Queueing
  401. opt.old = opt.complete;
  402. opt.complete = function() {
  403. if ( jQuery.isFunction( opt.old ) ) {
  404. opt.old.call( this );
  405. }
  406. if ( opt.queue ) {
  407. jQuery.dequeue( this, opt.queue );
  408. }
  409. };
  410. return opt;
  411. };
  412. jQuery.fn.extend( {
  413. fadeTo: function( speed, to, easing, callback ) {
  414. // Show any hidden elements after setting opacity to 0
  415. return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show()
  416. // Animate to the value specified
  417. .end().animate( { opacity: to }, speed, easing, callback );
  418. },
  419. animate: function( prop, speed, easing, callback ) {
  420. var empty = jQuery.isEmptyObject( prop ),
  421. optall = jQuery.speed( speed, easing, callback ),
  422. doAnimation = function() {
  423. // Operate on a copy of prop so per-property easing won't be lost
  424. var anim = Animation( this, jQuery.extend( {}, prop ), optall );
  425. // Empty animations, or finishing resolves immediately
  426. if ( empty || dataPriv.get( this, "finish" ) ) {
  427. anim.stop( true );
  428. }
  429. };
  430. doAnimation.finish = doAnimation;
  431. return empty || optall.queue === false ?
  432. this.each( doAnimation ) :
  433. this.queue( optall.queue, doAnimation );
  434. },
  435. stop: function( type, clearQueue, gotoEnd ) {
  436. var stopQueue = function( hooks ) {
  437. var stop = hooks.stop;
  438. delete hooks.stop;
  439. stop( gotoEnd );
  440. };
  441. if ( typeof type !== "string" ) {
  442. gotoEnd = clearQueue;
  443. clearQueue = type;
  444. type = undefined;
  445. }
  446. if ( clearQueue && type !== false ) {
  447. this.queue( type || "fx", [] );
  448. }
  449. return this.each( function() {
  450. var dequeue = true,
  451. index = type != null && type + "queueHooks",
  452. timers = jQuery.timers,
  453. data = dataPriv.get( this );
  454. if ( index ) {
  455. if ( data[ index ] && data[ index ].stop ) {
  456. stopQueue( data[ index ] );
  457. }
  458. } else {
  459. for ( index in data ) {
  460. if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
  461. stopQueue( data[ index ] );
  462. }
  463. }
  464. }
  465. for ( index = timers.length; index--; ) {
  466. if ( timers[ index ].elem === this &&
  467. ( type == null || timers[ index ].queue === type ) ) {
  468. timers[ index ].anim.stop( gotoEnd );
  469. dequeue = false;
  470. timers.splice( index, 1 );
  471. }
  472. }
  473. // Start the next in the queue if the last step wasn't forced.
  474. // Timers currently will call their complete callbacks, which
  475. // will dequeue but only if they were gotoEnd.
  476. if ( dequeue || !gotoEnd ) {
  477. jQuery.dequeue( this, type );
  478. }
  479. } );
  480. },
  481. finish: function( type ) {
  482. if ( type !== false ) {
  483. type = type || "fx";
  484. }
  485. return this.each( function() {
  486. var index,
  487. data = dataPriv.get( this ),
  488. queue = data[ type + "queue" ],
  489. hooks = data[ type + "queueHooks" ],
  490. timers = jQuery.timers,
  491. length = queue ? queue.length : 0;
  492. // Enable finishing flag on private data
  493. data.finish = true;
  494. // Empty the queue first
  495. jQuery.queue( this, type, [] );
  496. if ( hooks && hooks.stop ) {
  497. hooks.stop.call( this, true );
  498. }
  499. // Look for any active animations, and finish them
  500. for ( index = timers.length; index--; ) {
  501. if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
  502. timers[ index ].anim.stop( true );
  503. timers.splice( index, 1 );
  504. }
  505. }
  506. // Look for any animations in the old queue and finish them
  507. for ( index = 0; index < length; index++ ) {
  508. if ( queue[ index ] && queue[ index ].finish ) {
  509. queue[ index ].finish.call( this );
  510. }
  511. }
  512. // Turn off finishing flag
  513. delete data.finish;
  514. } );
  515. }
  516. } );
  517. jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) {
  518. var cssFn = jQuery.fn[ name ];
  519. jQuery.fn[ name ] = function( speed, easing, callback ) {
  520. return speed == null || typeof speed === "boolean" ?
  521. cssFn.apply( this, arguments ) :
  522. this.animate( genFx( name, true ), speed, easing, callback );
  523. };
  524. } );
  525. // Generate shortcuts for custom animations
  526. jQuery.each( {
  527. slideDown: genFx( "show" ),
  528. slideUp: genFx( "hide" ),
  529. slideToggle: genFx( "toggle" ),
  530. fadeIn: { opacity: "show" },
  531. fadeOut: { opacity: "hide" },
  532. fadeToggle: { opacity: "toggle" }
  533. }, function( name, props ) {
  534. jQuery.fn[ name ] = function( speed, easing, callback ) {
  535. return this.animate( props, speed, easing, callback );
  536. };
  537. } );
  538. jQuery.timers = [];
  539. jQuery.fx.tick = function() {
  540. var timer,
  541. i = 0,
  542. timers = jQuery.timers;
  543. fxNow = jQuery.now();
  544. for ( ; i < timers.length; i++ ) {
  545. timer = timers[ i ];
  546. // Checks the timer has not already been removed
  547. if ( !timer() && timers[ i ] === timer ) {
  548. timers.splice( i--, 1 );
  549. }
  550. }
  551. if ( !timers.length ) {
  552. jQuery.fx.stop();
  553. }
  554. fxNow = undefined;
  555. };
  556. jQuery.fx.timer = function( timer ) {
  557. jQuery.timers.push( timer );
  558. if ( timer() ) {
  559. jQuery.fx.start();
  560. } else {
  561. jQuery.timers.pop();
  562. }
  563. };
  564. jQuery.fx.interval = 13;
  565. jQuery.fx.start = function() {
  566. if ( !timerId ) {
  567. timerId = window.requestAnimationFrame ?
  568. window.requestAnimationFrame( raf ) :
  569. window.setInterval( jQuery.fx.tick, jQuery.fx.interval );
  570. }
  571. };
  572. jQuery.fx.stop = function() {
  573. if ( window.cancelAnimationFrame ) {
  574. window.cancelAnimationFrame( timerId );
  575. } else {
  576. window.clearInterval( timerId );
  577. }
  578. timerId = null;
  579. };
  580. jQuery.fx.speeds = {
  581. slow: 600,
  582. fast: 200,
  583. // Default speed
  584. _default: 400
  585. };
  586. return jQuery;
  587. } );