| 
									
										
										
										
											2018-12-06 16:28:45 +08:00
										 |  |  | import {root} from './env' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-23 10:57:58 +08:00
										 |  |  | /* istanbul ignore file */ | 
					
						
							| 
									
										
										
										
											2018-03-26 16:04:04 +08:00
										 |  |  | const Animate = (global => { | 
					
						
							| 
									
										
										
										
											2018-07-23 10:57:58 +08:00
										 |  |  |   /* istanbul ignore next */ | 
					
						
							| 
									
										
										
										
											2018-03-26 16:04:04 +08:00
										 |  |  |   const time = | 
					
						
							|  |  |  |     Date.now || | 
					
						
							|  |  |  |     (() => { | 
					
						
							|  |  |  |       return +new Date() | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |   const desiredFrames = 60 | 
					
						
							|  |  |  |   const millisecondsPerSecond = 1000 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let running = {} | 
					
						
							|  |  |  |   let counter = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * A requestAnimationFrame wrapper / polyfill. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param callback {Function} The callback to be invoked before the next repaint. | 
					
						
							|  |  |  |      * @param root {HTMLElement} The root element for the repaint | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     requestAnimationFrame: (() => { | 
					
						
							|  |  |  |       // Check for request animation Frame support
 | 
					
						
							|  |  |  |       const requestFrame = | 
					
						
							|  |  |  |         global.requestAnimationFrame || | 
					
						
							|  |  |  |         global.webkitRequestAnimationFrame || | 
					
						
							|  |  |  |         global.mozRequestAnimationFrame || | 
					
						
							|  |  |  |         global.oRequestAnimationFrame | 
					
						
							|  |  |  |       let isNative = !!requestFrame | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (requestFrame && !/requestAnimationFrame\(\)\s*\{\s*\[native code\]\s*\}/i.test(requestFrame.toString())) { | 
					
						
							|  |  |  |         isNative = false | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (isNative) { | 
					
						
							|  |  |  |         return (callback, root) => { | 
					
						
							|  |  |  |           requestFrame(callback, root) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const TARGET_FPS = 60 | 
					
						
							|  |  |  |       let requests = {} | 
					
						
							|  |  |  |       let requestCount = 0 | 
					
						
							|  |  |  |       let rafHandle = 1 | 
					
						
							|  |  |  |       let intervalHandle = null | 
					
						
							|  |  |  |       let lastActive = +new Date() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return callback => { | 
					
						
							|  |  |  |         const callbackHandle = rafHandle++ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Store callback
 | 
					
						
							|  |  |  |         requests[callbackHandle] = callback | 
					
						
							|  |  |  |         requestCount++ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Create timeout at first request
 | 
					
						
							|  |  |  |         if (intervalHandle === null) { | 
					
						
							|  |  |  |           intervalHandle = setInterval(() => { | 
					
						
							|  |  |  |             const time = +new Date() | 
					
						
							|  |  |  |             const currentRequests = requests | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Reset data structure before executing callbacks
 | 
					
						
							|  |  |  |             requests = {} | 
					
						
							|  |  |  |             requestCount = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (const key in currentRequests) { | 
					
						
							|  |  |  |               if (currentRequests.hasOwnProperty(key)) { | 
					
						
							|  |  |  |                 currentRequests[key](time) | 
					
						
							|  |  |  |                 lastActive = time | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Disable the timeout when nothing happens for a certain
 | 
					
						
							|  |  |  |             // period of time
 | 
					
						
							|  |  |  |             if (time - lastActive > 2500) { | 
					
						
							|  |  |  |               clearInterval(intervalHandle) | 
					
						
							|  |  |  |               intervalHandle = null | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }, 1000 / TARGET_FPS) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return callbackHandle | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     })(), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Stops the given animation. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param id {Integer} Unique animation ID | 
					
						
							|  |  |  |      * @return {Boolean} Whether the animation was stopped (aka, was running before) | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     stop(id) { | 
					
						
							|  |  |  |       const cleared = running[id] != null | 
					
						
							|  |  |  |       cleared && (running[id] = null) | 
					
						
							|  |  |  |       return cleared | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Whether the given animation is still running. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param id {Integer} Unique animation ID | 
					
						
							|  |  |  |      * @return {Boolean} Whether the animation is still running | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     isRunning(id) { | 
					
						
							|  |  |  |       return running[id] != null | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Start the animation. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param stepCallback {Function} Pointer to function which is executed on every step. | 
					
						
							|  |  |  |      *   Signature of the method should be `function(percent, now, virtual) { return continueWithAnimation; }` | 
					
						
							|  |  |  |      * @param verifyCallback {Function} Executed before every animation step. | 
					
						
							|  |  |  |      *   Signature of the method should be `function() { return continueWithAnimation; }` | 
					
						
							|  |  |  |      * @param completedCallback {Function} | 
					
						
							|  |  |  |      *   Signature of the method should be `function(droppedFrames, finishedAnimation) {}` | 
					
						
							|  |  |  |      * @param duration {Integer} Milliseconds to run the animation | 
					
						
							|  |  |  |      * @param easingMethod {Function} Pointer to easing function | 
					
						
							|  |  |  |      *   Signature of the method should be `function(percent) { return modifiedValue; }` | 
					
						
							|  |  |  |      * @param root {Element ? document.body} Render root, when available. Used for internal | 
					
						
							|  |  |  |      *   usage of requestAnimationFrame. | 
					
						
							|  |  |  |      * @return {Integer} Identifier of animation. Can be used to stop it any time. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     start(stepCallback, verifyCallback, completedCallback, duration, easingMethod, root) { | 
					
						
							|  |  |  |       const start = time() | 
					
						
							|  |  |  |       let lastFrame = start | 
					
						
							|  |  |  |       let percent = 0 | 
					
						
							|  |  |  |       let dropCounter = 0 | 
					
						
							|  |  |  |       const id = counter++ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!root) { | 
					
						
							|  |  |  |         root = document.body | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Compacting running db automatically every few new animations
 | 
					
						
							|  |  |  |       if (id % 20 === 0) { | 
					
						
							|  |  |  |         const newRunning = {} | 
					
						
							|  |  |  |         for (const usedId in running) { | 
					
						
							|  |  |  |           newRunning[usedId] = true | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         running = newRunning | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // This is the internal step method which is called every few milliseconds
 | 
					
						
							|  |  |  |       const step = virtual => { | 
					
						
							|  |  |  |         // Normalize virtual value
 | 
					
						
							|  |  |  |         const render = virtual !== true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Get current time
 | 
					
						
							|  |  |  |         const now = time() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Verification is executed before next animation step
 | 
					
						
							|  |  |  |         if (!running[id] || (verifyCallback && !verifyCallback(id))) { | 
					
						
							|  |  |  |           running[id] = null | 
					
						
							|  |  |  |           completedCallback && | 
					
						
							|  |  |  |             completedCallback(desiredFrames - dropCounter / ((now - start) / millisecondsPerSecond), id, false) | 
					
						
							|  |  |  |           return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // For the current rendering to apply let's update omitted steps in memory.
 | 
					
						
							|  |  |  |         // This is important to bring internal state variables up-to-date with progress in time.
 | 
					
						
							|  |  |  |         if (render) { | 
					
						
							|  |  |  |           const droppedFrames = Math.round((now - lastFrame) / (millisecondsPerSecond / desiredFrames)) - 1 | 
					
						
							|  |  |  |           for (let j = 0; j < Math.min(droppedFrames, 4); j++) { | 
					
						
							|  |  |  |             step(true) | 
					
						
							|  |  |  |             dropCounter++ | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Compute percent value
 | 
					
						
							|  |  |  |         if (duration) { | 
					
						
							|  |  |  |           percent = (now - start) / duration | 
					
						
							|  |  |  |           if (percent > 1) { | 
					
						
							|  |  |  |             percent = 1 | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Execute step callback, then...
 | 
					
						
							|  |  |  |         let value = easingMethod ? easingMethod(percent) : percent | 
					
						
							|  |  |  |         value = isNaN(value) ? 0 : value | 
					
						
							|  |  |  |         if ((stepCallback(value, now, render) === false || percent === 1) && render) { | 
					
						
							|  |  |  |           running[id] = null | 
					
						
							|  |  |  |           completedCallback && | 
					
						
							|  |  |  |             completedCallback( | 
					
						
							|  |  |  |               desiredFrames - dropCounter / ((now - start) / millisecondsPerSecond), | 
					
						
							|  |  |  |               id, | 
					
						
							|  |  |  |               percent === 1 || duration == null, | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         } else if (render) { | 
					
						
							|  |  |  |           lastFrame = now | 
					
						
							|  |  |  |           this.requestAnimationFrame(step, root) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Mark as running
 | 
					
						
							|  |  |  |       running[id] = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Init first step
 | 
					
						
							|  |  |  |       this.requestAnimationFrame(step, root) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Return unique animation ID
 | 
					
						
							|  |  |  |       return id | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-12-06 16:28:45 +08:00
										 |  |  | })(root) | 
					
						
							| 
									
										
										
										
											2018-03-26 16:04:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-30 23:12:00 +08:00
										 |  |  | export const easeOutCubic = pos => { | 
					
						
							|  |  |  |   return Math.pow(pos - 1, 3) + 1 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const easeInOutCubic = pos => { | 
					
						
							|  |  |  |   if ((pos /= 0.5) < 1) { | 
					
						
							|  |  |  |     return 0.5 * Math.pow(pos, 3) | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return 0.5 * (Math.pow(pos - 2, 3) + 2) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-26 16:04:04 +08:00
										 |  |  | export default Animate |