UNPKG

@egjs/jquery-pauseresume

Version:

Pauses and resumes animation executed by the jQuery animate() method

226 lines (188 loc) 6.41 kB
import jQuery from "jquery"; import AniPropertyManager from "./AniPropertyManager"; import MathUtil from "./MathUtil"; /** * @namespace jQuery */ export default (function($) { const animateFn = $.fn.animate; const stopFn = $.fn.stop; const delayFn = $.fn.delay; /** * Generate a new easing function. * * function to avoid JS Hint error "Don't make functions within a loop" */ function generateNewEasingFunc(resumePercent, remainPercent, scale, originalEasing) { return function easingFunc(percent) { const newPercent = resumePercent + remainPercent * percent; return scale(originalEasing(newPercent)); }; } $.fn.animate = function(prop, speed, easing, callback) { return this.each(function() { // optall should be made for each elements. const optall = $.speed(speed, easing, callback); // prepare next animation when current animation completed. optall.complete = function() { AniPropertyManager.prepareNextAniProp(this); }; // Queue animation property to recover the current animation. AniPropertyManager.addAniProperty("animate", this, prop, optall); animateFn.call($(this), prop, optall); }); // TODO: Below code is more reasonable? // return animateFn.call(this, prop, optall); // and declare optall at outside this.each loop. }; /** * Set a timer to delay execution of subsequent items in the queue. * And it internally manages "fx"queue to support pause/resume if "fx" type. * * @param {Number} An integer indicating the number of milliseconds to delay execution of the next item in the queue. * @param {String} A string containing the name of the queue. Defaults to fx, the standard effects queue. */ $.fn.delay = function(time, type, ...args) { let t; const isCallByResume = args[0]; // internal used value. if (type && type !== "fx") { return delayFn.call(this, time, type); } t = parseInt(time, 10); t = isNaN(t) ? 0 : t; return this.each(function() { if (!isCallByResume) { // Queue delay property to recover the current animation. // Don't add property when delay is called by resume. AniPropertyManager.addAniProperty("delay", this, null, {duration: t}); } delayFn.call($(this), time).queue(next => { next(); // Remove delay property when delay has been expired. AniPropertyManager.removeAniProperty(this); }); }); }; /** * Pauses the animation executed through a call to the jQuery <a href=http://api.jquery.com/animate/>.animate()</a> method. * @ko jQuery의<a href=http://api.jquery.com/animate/>animate() 메서드</a>가 실행한 애니메이션을 일시 정지한다 * * @name jQuery#pause * @method * @support {"ie": "10+", "ch" : "latest", "sf" : "latest", "edge" : "latest", "ios" : "7+", "an" : "2.3+ (except 3.x)"} * @example * $("#box").pause(); //paused the current animation */ $.fn.pause = function() { return this.each(function() { let p; if (AniPropertyManager.getStatus(this) !== "inprogress") { return; } // Clear fx-queue except 1 dummy function // for promise not to be expired when calling stop() $.queue(this, "fx", [$.noop]); stopFn.call($(this)); // Remember current animation property p = this.__aniProps[0]; if (p) { p.elapsed += $.now() - p.start; // Complement native timer's inaccuracy (complete timer can be different from your request.) // (eg. your request:400ms -> real :396 ~ 415 ms )) if (p.elapsed >= p.opt.duration) { p = AniPropertyManager.prepareNextAniProp(this); } p && (p.paused = true); } }); }; /** * Resumes the animation paused through a call to the pause() method. * @ko pause() 메서드가 일시 정지한 애니메이션을 다시 실행한다 * * @name jQuery#resume * @alias eg.Pause * @method * @support {"ie": "10+", "ch" : "latest", "sf" : "latest", "edge" : "latest", "ios" : "7+", "an" : "2.3+ (except 3.x)"} * @example * $("#box").resume(); //resume the paused animation */ $.fn.resume = function() { return this.each(function() { const type = "fx"; let p; let i; if (AniPropertyManager.getStatus(this) !== "paused") { return; } // Clear fx-queue, // And this queue will be initialized by animate call. $.queue(this, type || "fx", []); // Restore __aniProps i = 0; p = this.__aniProps[i]; while (p) { // Restore easing status if (p.elapsed > 0 && p.opt.easing) { const resumePercent = p.elapsed / p.opt.duration; const remainPercent = 1 - resumePercent; const originalEasing = $.easing[p.opt.easing]; const startEasingValue = originalEasing(resumePercent); const scale = MathUtil.scaler([startEasingValue, 1], [0, 1]); const newEasingName = `${p.opt.easing}_${p.uuid}`; // Make new easing function that continues from pause point. $.easing[newEasingName] = generateNewEasingFunc( resumePercent, remainPercent, scale, originalEasing); p.opt.easing = newEasingName; // Store new easing function to clear it later. p.addEasingFn(newEasingName); } p.paused = false; p.opt.duration -= p.elapsed; // If duration remains, request 'animate' with storing aniProps if (p.opt.duration > 0 || p.elapsed === 0) { i === 0 && p.init(); if (p.type === "delay") { // pass last parameter 'true' not to add an aniProperty. $(this).delay(p.opt.duration, "fx", true); } else { animateFn.call($(this), p.prop, p.opt); } } i++; p = this.__aniProps[i]; } }); }; $.fn.stop = function(...args) { const type = args[0]; let clearQ = args[1]; stopFn.apply(this, args); if (typeof type !== "string") { clearQ = type; } return this.each(function() { let p; // When this element was not animated properly, do nothing. if (AniPropertyManager.getStatus(this) === "empty") { return; } if (!clearQ) { p = this.__aniProps.shift(); p && p.clearEasingFn(); } else { // If clearQueue is requested, // then all properties must be initialized // for element not to be resumed. p = this.__aniProps.shift(); while (p) { p.clearEasingFn(); p = this.__aniProps.shift(); } this.__aniProps = []; } }); }; $.expr.filters.paused = function(elem) { return AniPropertyManager.getStatus(elem) === "paused"; }; })(jQuery);