UNPKG

@vimeo/iris

Version:
170 lines (156 loc) 5.01 kB
'use strict'; var React = require('react'); /** * Browser-safe usage of process */ var defaultEnvironment = "production"; var env = typeof process === "undefined" || process.env === undefined ? defaultEnvironment : process.env.NODE_ENV || defaultEnvironment; /** * @public */ var PresenceContext = /*#__PURE__*/React.createContext(null); var isBrowser = typeof document !== "undefined"; var useIsomorphicLayoutEffect = isBrowser ? React.useLayoutEffect : React.useEffect; /** * Creates a constant value over the lifecycle of a component. * * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer * a guarantee that it won't re-run for performance reasons later on. By using `useConstant` * you can ensure that initialisers don't execute twice or more. */ function useConstant(init) { var ref = React.useRef(null); if (ref.current === null) { ref.current = init(); } return ref.current; } var LayoutGroupContext = /*#__PURE__*/React.createContext({}); function useUnmountEffect(callback) { return React.useEffect(function () { return function () { return callback(); }; }, []); } const defaultTimestep = 1 / 60 * 1000; const getCurrentTime = typeof performance !== "undefined" ? () => performance.now() : () => Date.now(); const onNextFrame = typeof window !== "undefined" ? callback => window.requestAnimationFrame(callback) : callback => setTimeout(() => callback(getCurrentTime()), defaultTimestep); function createRenderStep(runNextFrame) { let toRun = []; let toRunNextFrame = []; let numToRun = 0; let isProcessing = false; let flushNextFrame = false; const toKeepAlive = new WeakSet(); const step = { schedule: (callback, keepAlive = false, immediate = false) => { const addToCurrentFrame = immediate && isProcessing; const buffer = addToCurrentFrame ? toRun : toRunNextFrame; if (keepAlive) toKeepAlive.add(callback); if (buffer.indexOf(callback) === -1) { buffer.push(callback); if (addToCurrentFrame && isProcessing) numToRun = toRun.length; } return callback; }, cancel: callback => { const index = toRunNextFrame.indexOf(callback); if (index !== -1) toRunNextFrame.splice(index, 1); toKeepAlive.delete(callback); }, process: frameData => { if (isProcessing) { flushNextFrame = true; return; } isProcessing = true; [toRun, toRunNextFrame] = [toRunNextFrame, toRun]; toRunNextFrame.length = 0; numToRun = toRun.length; if (numToRun) { for (let i = 0; i < numToRun; i++) { const callback = toRun[i]; callback(frameData); if (toKeepAlive.has(callback)) { step.schedule(callback); runNextFrame(); } } } isProcessing = false; if (flushNextFrame) { flushNextFrame = false; step.process(frameData); } } }; return step; } const maxElapsed = 40; let useDefaultElapsed = true; let runNextFrame = false; let isProcessing = false; const frame = { delta: 0, timestamp: 0 }; const stepsOrder = ["read", "update", "preRender", "render", "postRender"]; const steps = stepsOrder.reduce((acc, key) => { acc[key] = createRenderStep(() => runNextFrame = true); return acc; }, {}); const sync = stepsOrder.reduce((acc, key) => { const step = steps[key]; acc[key] = (process, keepAlive = false, immediate = false) => { if (!runNextFrame) startLoop(); return step.schedule(process, keepAlive, immediate); }; return acc; }, {}); const cancelSync = stepsOrder.reduce((acc, key) => { acc[key] = steps[key].cancel; return acc; }, {}); const flushSync = stepsOrder.reduce((acc, key) => { acc[key] = () => steps[key].process(frame); return acc; }, {}); const processStep = stepId => steps[stepId].process(frame); const processFrame = timestamp => { runNextFrame = false; frame.delta = useDefaultElapsed ? defaultTimestep : Math.max(Math.min(timestamp - frame.timestamp, maxElapsed), 1); frame.timestamp = timestamp; isProcessing = true; stepsOrder.forEach(processStep); isProcessing = false; if (runNextFrame) { useDefaultElapsed = false; onNextFrame(processFrame); } }; const startLoop = () => { runNextFrame = true; useDefaultElapsed = true; if (!isProcessing) onNextFrame(processFrame); }; const getFrameData = () => frame; var counter = 0; var incrementId = function () { return counter++; }; var useId = function () { return useConstant(incrementId); }; exports.LayoutGroupContext = LayoutGroupContext; exports.PresenceContext = PresenceContext; exports.cancelSync = cancelSync; exports.env = env; exports.flushSync = flushSync; exports.getFrameData = getFrameData; exports.isBrowser = isBrowser; exports.sync = sync; exports.useConstant = useConstant; exports.useId = useId; exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect; exports.useUnmountEffect = useUnmountEffect;