effects.js 17 KB

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