Comparing version 3.1.9 to 4.0.0
@@ -1,96 +0,136 @@ | ||
var hasRAF = typeof window !== 'undefined' && window.requestAnimationFrame !== undefined; | ||
import { invariant } from 'hey-listen'; | ||
var prevTime = 0; | ||
var onNextFrame = hasRAF | ||
var onNextFrame = typeof window !== 'undefined' && window.requestAnimationFrame !== undefined | ||
? function (callback) { return window.requestAnimationFrame(callback); } | ||
: function (callback) { | ||
var currentTime = Date.now(); | ||
var timeToCall = Math.max(0, 16.7 - (currentTime - prevTime)); | ||
prevTime = currentTime + timeToCall; | ||
var timestamp = Date.now(); | ||
var timeToCall = Math.max(0, 16.7 - (timestamp - prevTime)); | ||
prevTime = timestamp + timeToCall; | ||
setTimeout(function () { return callback(prevTime); }, timeToCall); | ||
}; | ||
function createRenderStep(startRenderLoop) { | ||
var functionsToRun = []; | ||
var functionsToRunNextFrame = []; | ||
var createStep = (function (setRunNextFrame) { | ||
var processToRun = []; | ||
var processToRunNextFrame = []; | ||
var numThisFrame = 0; | ||
var isProcessing = false; | ||
var i = 0; | ||
return { | ||
cancel: function (callback) { | ||
var indexOfCallback = functionsToRunNextFrame.indexOf(callback); | ||
var cancelled = new WeakSet(); | ||
var toKeepAlive = new WeakSet(); | ||
var renderStep = { | ||
cancel: function (process) { | ||
var indexOfCallback = processToRunNextFrame.indexOf(process); | ||
cancelled.add(process); | ||
if (indexOfCallback !== -1) { | ||
functionsToRunNextFrame.splice(indexOfCallback, 1); | ||
processToRunNextFrame.splice(indexOfCallback, 1); | ||
} | ||
}, | ||
process: function () { | ||
process: function (frame) { | ||
var _a; | ||
isProcessing = true; | ||
_a = [functionsToRunNextFrame, functionsToRun], functionsToRun = _a[0], functionsToRunNextFrame = _a[1]; | ||
functionsToRunNextFrame.length = 0; | ||
numThisFrame = functionsToRun.length; | ||
for (i = 0; i < numThisFrame; i++) { | ||
functionsToRun[i](); | ||
_a = [ | ||
processToRunNextFrame, | ||
processToRun | ||
], processToRun = _a[0], processToRunNextFrame = _a[1]; | ||
processToRunNextFrame.length = 0; | ||
numThisFrame = processToRun.length; | ||
if (numThisFrame) { | ||
var process_1; | ||
for (i = 0; i < numThisFrame; i++) { | ||
process_1 = processToRun[i]; | ||
process_1(frame); | ||
if (toKeepAlive.has(process_1) === true && !cancelled.has(process_1)) { | ||
renderStep.schedule(process_1); | ||
setRunNextFrame(true); | ||
} | ||
} | ||
} | ||
isProcessing = false; | ||
var _a; | ||
}, | ||
schedule: function (callback, immediate) { | ||
if (immediate === void 0) { immediate = false; } | ||
startRenderLoop(); | ||
schedule: function (process, keepAlive, immediate) { | ||
invariant(typeof process === 'function', 'Argument must be a function'); | ||
var addToCurrentBuffer = immediate && isProcessing; | ||
var buffer = addToCurrentBuffer ? functionsToRun : functionsToRunNextFrame; | ||
if (buffer.indexOf(callback) === -1) { | ||
buffer.push(callback); | ||
if (addToCurrentBuffer) { | ||
numThisFrame = functionsToRun.length; | ||
} | ||
var buffer = addToCurrentBuffer ? processToRun : processToRunNextFrame; | ||
if (keepAlive) | ||
toKeepAlive.add(process); | ||
if (buffer.indexOf(process) === -1) { | ||
buffer.push(process); | ||
if (addToCurrentBuffer) | ||
numThisFrame = processToRun.length; | ||
} | ||
}, | ||
} | ||
}; | ||
} | ||
return renderStep; | ||
}); | ||
var HAS_PERFORMANCE_NOW = typeof performance !== 'undefined' && performance.now !== undefined; | ||
var currentTime = HAS_PERFORMANCE_NOW ? function () { return performance.now(); } : function () { return Date.now(); }; | ||
var willRenderNextFrame = false; | ||
var MAX_ELAPSED = 40; | ||
var defaultElapsed = 16.7; | ||
var StepId; | ||
(function (StepId) { | ||
StepId["Read"] = "read"; | ||
StepId["Update"] = "update"; | ||
StepId["Render"] = "render"; | ||
StepId["PostRender"] = "postRender"; | ||
StepId["FixedUpdate"] = "fixedUpdate"; | ||
})(StepId || (StepId = {})); | ||
var maxElapsed = 40; | ||
var defaultElapsed = (1 / 60) * 1000; | ||
var useDefaultElapsed = true; | ||
var currentFramestamp = 0; | ||
var elapsed = 0; | ||
function startRenderLoop() { | ||
if (willRenderNextFrame) | ||
return; | ||
willRenderNextFrame = true; | ||
useDefaultElapsed = true; | ||
onNextFrame(processFrame); | ||
} | ||
var frameStart = createRenderStep(startRenderLoop); | ||
var frameUpdate = createRenderStep(startRenderLoop); | ||
var frameRender = createRenderStep(startRenderLoop); | ||
var frameEnd = createRenderStep(startRenderLoop); | ||
function processFrame(framestamp) { | ||
willRenderNextFrame = false; | ||
elapsed = useDefaultElapsed | ||
var willRunNextFrame = false; | ||
var isProcessing = false; | ||
var frame = { | ||
delta: 0, | ||
timestamp: 0 | ||
}; | ||
var stepsOrder = [ | ||
StepId.Read, | ||
StepId.Update, | ||
StepId.Render, | ||
StepId.PostRender | ||
]; | ||
var setWillRunNextFrame = function (willRun) { return (willRunNextFrame = willRun); }; | ||
var _a = stepsOrder.reduce(function (acc, key) { | ||
var step = createStep(setWillRunNextFrame); | ||
acc.sync[key] = function (process, keepAlive, immediate) { | ||
if (keepAlive === void 0) { keepAlive = false; } | ||
if (immediate === void 0) { immediate = false; } | ||
if (!willRunNextFrame) | ||
startLoop(); | ||
step.schedule(process, keepAlive, immediate); | ||
return process; | ||
}; | ||
acc.cancelSync[key] = function (process) { return step.cancel(process); }; | ||
acc.steps[key] = step; | ||
return acc; | ||
}, { | ||
steps: {}, | ||
sync: {}, | ||
cancelSync: {} | ||
}), steps = _a.steps, sync = _a.sync, cancelSync = _a.cancelSync; | ||
var processStep = function (stepId) { return steps[stepId].process(frame); }; | ||
var processFrame = function (timestamp) { | ||
willRunNextFrame = false; | ||
frame.delta = useDefaultElapsed | ||
? defaultElapsed | ||
: Math.max(Math.min(framestamp - currentFramestamp, MAX_ELAPSED), 1); | ||
: Math.max(Math.min(timestamp - frame.timestamp, maxElapsed), 1); | ||
if (!useDefaultElapsed) | ||
defaultElapsed = elapsed; | ||
currentFramestamp = framestamp; | ||
frameStart.process(); | ||
frameUpdate.process(); | ||
frameRender.process(); | ||
frameEnd.process(); | ||
if (willRenderNextFrame) | ||
defaultElapsed = frame.delta; | ||
frame.timestamp = timestamp; | ||
isProcessing = true; | ||
stepsOrder.forEach(processStep); | ||
isProcessing = false; | ||
if (willRunNextFrame) { | ||
useDefaultElapsed = false; | ||
} | ||
var onFrameStart = frameStart.schedule; | ||
var onFrameUpdate = frameUpdate.schedule; | ||
var onFrameRender = frameRender.schedule; | ||
var onFrameEnd = frameEnd.schedule; | ||
var cancelOnFrameStart = frameStart.cancel; | ||
var cancelOnFrameUpdate = frameUpdate.cancel; | ||
var cancelOnFrameRender = frameRender.cancel; | ||
var cancelOnFrameEnd = frameEnd.cancel; | ||
var timeSinceLastFrame = function () { return elapsed; }; | ||
var currentFrameTime = function () { return currentFramestamp; }; | ||
onNextFrame(processFrame); | ||
} | ||
}; | ||
var startLoop = function () { | ||
willRunNextFrame = true; | ||
useDefaultElapsed = true; | ||
if (!isProcessing) | ||
onNextFrame(processFrame); | ||
}; | ||
var getFrameData = function () { return frame; }; | ||
export { currentTime, onFrameStart, onFrameUpdate, onFrameRender, onFrameEnd, cancelOnFrameStart, cancelOnFrameUpdate, cancelOnFrameRender, cancelOnFrameEnd, timeSinceLastFrame, currentFrameTime }; | ||
export default sync; | ||
export { cancelSync, getFrameData }; |
@@ -7,108 +7,147 @@ (function (global, factory) { | ||
var hasRAF = typeof window !== 'undefined' && window.requestAnimationFrame !== undefined; | ||
var prevTime = 0; | ||
var onNextFrame = hasRAF | ||
var onNextFrame = typeof window !== 'undefined' && window.requestAnimationFrame !== undefined | ||
? function (callback) { return window.requestAnimationFrame(callback); } | ||
: function (callback) { | ||
var currentTime = Date.now(); | ||
var timeToCall = Math.max(0, 16.7 - (currentTime - prevTime)); | ||
prevTime = currentTime + timeToCall; | ||
var timestamp = Date.now(); | ||
var timeToCall = Math.max(0, 16.7 - (timestamp - prevTime)); | ||
prevTime = timestamp + timeToCall; | ||
setTimeout(function () { return callback(prevTime); }, timeToCall); | ||
}; | ||
function createRenderStep(startRenderLoop) { | ||
var functionsToRun = []; | ||
var functionsToRunNextFrame = []; | ||
var HEY_LISTEN = 'Hey, listen! '; | ||
var invariant = function () { }; | ||
if (process.env.NODE_ENV !== 'production') { | ||
invariant = function (check, message) { | ||
if (!check) { | ||
throw new Error(HEY_LISTEN.toUpperCase() + message); | ||
} | ||
}; | ||
} | ||
var createStep = (function (setRunNextFrame) { | ||
var processToRun = []; | ||
var processToRunNextFrame = []; | ||
var numThisFrame = 0; | ||
var isProcessing = false; | ||
var i = 0; | ||
return { | ||
cancel: function (callback) { | ||
var indexOfCallback = functionsToRunNextFrame.indexOf(callback); | ||
var cancelled = new WeakSet(); | ||
var toKeepAlive = new WeakSet(); | ||
var renderStep = { | ||
cancel: function (process) { | ||
var indexOfCallback = processToRunNextFrame.indexOf(process); | ||
cancelled.add(process); | ||
if (indexOfCallback !== -1) { | ||
functionsToRunNextFrame.splice(indexOfCallback, 1); | ||
processToRunNextFrame.splice(indexOfCallback, 1); | ||
} | ||
}, | ||
process: function () { | ||
process: function (frame) { | ||
var _a; | ||
isProcessing = true; | ||
_a = [functionsToRunNextFrame, functionsToRun], functionsToRun = _a[0], functionsToRunNextFrame = _a[1]; | ||
functionsToRunNextFrame.length = 0; | ||
numThisFrame = functionsToRun.length; | ||
for (i = 0; i < numThisFrame; i++) { | ||
functionsToRun[i](); | ||
_a = [ | ||
processToRunNextFrame, | ||
processToRun | ||
], processToRun = _a[0], processToRunNextFrame = _a[1]; | ||
processToRunNextFrame.length = 0; | ||
numThisFrame = processToRun.length; | ||
if (numThisFrame) { | ||
var process_1; | ||
for (i = 0; i < numThisFrame; i++) { | ||
process_1 = processToRun[i]; | ||
process_1(frame); | ||
if (toKeepAlive.has(process_1) === true && !cancelled.has(process_1)) { | ||
renderStep.schedule(process_1); | ||
setRunNextFrame(true); | ||
} | ||
} | ||
} | ||
isProcessing = false; | ||
var _a; | ||
}, | ||
schedule: function (callback, immediate) { | ||
if (immediate === void 0) { immediate = false; } | ||
startRenderLoop(); | ||
schedule: function (process, keepAlive, immediate) { | ||
invariant(typeof process === 'function', 'Argument must be a function'); | ||
var addToCurrentBuffer = immediate && isProcessing; | ||
var buffer = addToCurrentBuffer ? functionsToRun : functionsToRunNextFrame; | ||
if (buffer.indexOf(callback) === -1) { | ||
buffer.push(callback); | ||
if (addToCurrentBuffer) { | ||
numThisFrame = functionsToRun.length; | ||
} | ||
var buffer = addToCurrentBuffer ? processToRun : processToRunNextFrame; | ||
if (keepAlive) | ||
toKeepAlive.add(process); | ||
if (buffer.indexOf(process) === -1) { | ||
buffer.push(process); | ||
if (addToCurrentBuffer) | ||
numThisFrame = processToRun.length; | ||
} | ||
}, | ||
} | ||
}; | ||
} | ||
return renderStep; | ||
}); | ||
var HAS_PERFORMANCE_NOW = typeof performance !== 'undefined' && performance.now !== undefined; | ||
var currentTime = HAS_PERFORMANCE_NOW ? function () { return performance.now(); } : function () { return Date.now(); }; | ||
var willRenderNextFrame = false; | ||
var MAX_ELAPSED = 40; | ||
var defaultElapsed = 16.7; | ||
var StepId; | ||
(function (StepId) { | ||
StepId["Read"] = "read"; | ||
StepId["Update"] = "update"; | ||
StepId["Render"] = "render"; | ||
StepId["PostRender"] = "postRender"; | ||
StepId["FixedUpdate"] = "fixedUpdate"; | ||
})(StepId || (StepId = {})); | ||
var maxElapsed = 40; | ||
var defaultElapsed = (1 / 60) * 1000; | ||
var useDefaultElapsed = true; | ||
var currentFramestamp = 0; | ||
var elapsed = 0; | ||
function startRenderLoop() { | ||
if (willRenderNextFrame) | ||
return; | ||
willRenderNextFrame = true; | ||
useDefaultElapsed = true; | ||
onNextFrame(processFrame); | ||
} | ||
var frameStart = createRenderStep(startRenderLoop); | ||
var frameUpdate = createRenderStep(startRenderLoop); | ||
var frameRender = createRenderStep(startRenderLoop); | ||
var frameEnd = createRenderStep(startRenderLoop); | ||
function processFrame(framestamp) { | ||
willRenderNextFrame = false; | ||
elapsed = useDefaultElapsed | ||
var willRunNextFrame = false; | ||
var isProcessing = false; | ||
var frame = { | ||
delta: 0, | ||
timestamp: 0 | ||
}; | ||
var stepsOrder = [ | ||
StepId.Read, | ||
StepId.Update, | ||
StepId.Render, | ||
StepId.PostRender | ||
]; | ||
var setWillRunNextFrame = function (willRun) { return (willRunNextFrame = willRun); }; | ||
var _a = stepsOrder.reduce(function (acc, key) { | ||
var step = createStep(setWillRunNextFrame); | ||
acc.sync[key] = function (process, keepAlive, immediate) { | ||
if (keepAlive === void 0) { keepAlive = false; } | ||
if (immediate === void 0) { immediate = false; } | ||
if (!willRunNextFrame) | ||
startLoop(); | ||
step.schedule(process, keepAlive, immediate); | ||
return process; | ||
}; | ||
acc.cancelSync[key] = function (process) { return step.cancel(process); }; | ||
acc.steps[key] = step; | ||
return acc; | ||
}, { | ||
steps: {}, | ||
sync: {}, | ||
cancelSync: {} | ||
}), steps = _a.steps, sync = _a.sync, cancelSync = _a.cancelSync; | ||
var processStep = function (stepId) { return steps[stepId].process(frame); }; | ||
var processFrame = function (timestamp) { | ||
willRunNextFrame = false; | ||
frame.delta = useDefaultElapsed | ||
? defaultElapsed | ||
: Math.max(Math.min(framestamp - currentFramestamp, MAX_ELAPSED), 1); | ||
: Math.max(Math.min(timestamp - frame.timestamp, maxElapsed), 1); | ||
if (!useDefaultElapsed) | ||
defaultElapsed = elapsed; | ||
currentFramestamp = framestamp; | ||
frameStart.process(); | ||
frameUpdate.process(); | ||
frameRender.process(); | ||
frameEnd.process(); | ||
if (willRenderNextFrame) | ||
defaultElapsed = frame.delta; | ||
frame.timestamp = timestamp; | ||
isProcessing = true; | ||
stepsOrder.forEach(processStep); | ||
isProcessing = false; | ||
if (willRunNextFrame) { | ||
useDefaultElapsed = false; | ||
} | ||
var onFrameStart = frameStart.schedule; | ||
var onFrameUpdate = frameUpdate.schedule; | ||
var onFrameRender = frameRender.schedule; | ||
var onFrameEnd = frameEnd.schedule; | ||
var cancelOnFrameStart = frameStart.cancel; | ||
var cancelOnFrameUpdate = frameUpdate.cancel; | ||
var cancelOnFrameRender = frameRender.cancel; | ||
var cancelOnFrameEnd = frameEnd.cancel; | ||
var timeSinceLastFrame = function () { return elapsed; }; | ||
var currentFrameTime = function () { return currentFramestamp; }; | ||
onNextFrame(processFrame); | ||
} | ||
}; | ||
var startLoop = function () { | ||
willRunNextFrame = true; | ||
useDefaultElapsed = true; | ||
if (!isProcessing) | ||
onNextFrame(processFrame); | ||
}; | ||
var getFrameData = function () { return frame; }; | ||
exports.currentTime = currentTime; | ||
exports.onFrameStart = onFrameStart; | ||
exports.onFrameUpdate = onFrameUpdate; | ||
exports.onFrameRender = onFrameRender; | ||
exports.onFrameEnd = onFrameEnd; | ||
exports.cancelOnFrameStart = cancelOnFrameStart; | ||
exports.cancelOnFrameUpdate = cancelOnFrameUpdate; | ||
exports.cancelOnFrameRender = cancelOnFrameRender; | ||
exports.cancelOnFrameEnd = cancelOnFrameEnd; | ||
exports.timeSinceLastFrame = timeSinceLastFrame; | ||
exports.currentFrameTime = currentFrameTime; | ||
exports.default = sync; | ||
exports.cancelSync = cancelSync; | ||
exports.getFrameData = getFrameData; | ||
@@ -115,0 +154,0 @@ Object.defineProperty(exports, '__esModule', { value: true }); |
@@ -1,1 +0,1 @@ | ||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(e.framesync={})}(this,function(e){"use strict";var n=0,r="undefined"!=typeof window&&void 0!==window.requestAnimationFrame?function(e){return window.requestAnimationFrame(e)}:function(e){var r=Date.now(),t=Math.max(0,16.7-(r-n));n=r+t,setTimeout(function(){return e(n)},t)};function t(e){var n=[],r=[],t=0,c=!1,o=0;return{cancel:function(e){var n=r.indexOf(e);-1!==n&&r.splice(n,1)},process:function(){for(c=!0,n=(e=[r,n])[0],(r=e[1]).length=0,t=n.length,o=0;o<t;o++)n[o]();var e;c=!1},schedule:function(o,a){void 0===a&&(a=!1),e();var i=a&&c,u=i?n:r;-1===u.indexOf(o)&&(u.push(o),i&&(t=n.length))}}}var c="undefined"!=typeof performance&&void 0!==performance.now?function(){return performance.now()}:function(){return Date.now()},o=!1,a=40,i=16.7,u=!0,f=0,d=0;function s(){o||(o=!0,u=!0,r(v))}var m=t(s),l=t(s),p=t(s),h=t(s);function v(e){o=!1,d=u?i:Math.max(Math.min(e-f,a),1),u||(i=d),f=e,m.process(),l.process(),p.process(),h.process(),o&&(u=!1)}var F=m.schedule,w=l.schedule,x=p.schedule,y=h.schedule,O=m.cancel,M=l.cancel,g=p.cancel,S=h.cancel;e.currentTime=c,e.onFrameStart=F,e.onFrameUpdate=w,e.onFrameRender=x,e.onFrameEnd=y,e.cancelOnFrameStart=O,e.cancelOnFrameUpdate=M,e.cancelOnFrameRender=g,e.cancelOnFrameEnd=S,e.timeSinceLastFrame=function(){return d},e.currentFrameTime=function(){return f},Object.defineProperty(e,"__esModule",{value:!0})}); | ||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(e.framesync={})}(this,function(e){"use strict";var n=0,t="undefined"!=typeof window&&void 0!==window.requestAnimationFrame?function(e){return window.requestAnimationFrame(e)}:function(e){var t=Date.now(),o=Math.max(0,16.7-(t-n));n=t+o,setTimeout(function(){return e(n)},o)},o=function(){};"production"!==process.env.NODE_ENV&&(o=function(e,n){if(!e)throw new Error("Hey, listen! ".toUpperCase()+n)});var r;!function(e){e.Read="read",e.Update="update",e.Render="render",e.PostRender="postRender",e.FixedUpdate="fixedUpdate"}(r||(r={}));var c=1/60*1e3,a=!0,i=!1,u=!1,d={delta:0,timestamp:0},f=[r.Read,r.Update,r.Render,r.PostRender],s=function(e){return i=e},p=f.reduce(function(e,n){var t,r,c,a,u,d,f,p,l,m=(t=s,r=[],c=[],a=0,u=!1,d=0,f=new WeakSet,p=new WeakSet,l={cancel:function(e){var n=c.indexOf(e);f.add(e),-1!==n&&c.splice(n,1)},process:function(e){var n,o;if(u=!0,r=(n=[c,r])[0],(c=n[1]).length=0,a=r.length)for(d=0;d<a;d++)(o=r[d])(e),!0!==p.has(o)||f.has(o)||(l.schedule(o),t(!0));u=!1},schedule:function(e,n,t){o("function"==typeof e,"Argument must be a function");var i=t&&u,d=i?r:c;n&&p.add(e),-1===d.indexOf(e)&&(d.push(e),i&&(a=r.length))}});return e.sync[n]=function(e,n,t){return void 0===n&&(n=!1),void 0===t&&(t=!1),i||w(),m.schedule(e,n,t),e},e.cancelSync[n]=function(e){return m.cancel(e)},e.steps[n]=m,e},{steps:{},sync:{},cancelSync:{}}),l=p.steps,m=p.sync,h=p.cancelSync,y=function(e){return l[e].process(d)},v=function(e){i=!1,d.delta=a?c:Math.max(Math.min(e-d.timestamp,40),1),a||(c=d.delta),d.timestamp=e,u=!0,f.forEach(y),u=!1,i&&(a=!1,t(v))},w=function(){i=!0,a=!0,u||t(v)};e.default=m,e.cancelSync=h,e.getFrameData=function(){return d},Object.defineProperty(e,"__esModule",{value:!0})}); |
@@ -1,6 +0,3 @@ | ||
export interface RenderStep { | ||
readonly schedule: (callback: Function, immediate?: boolean) => void; | ||
readonly cancel: (callback: Function) => void; | ||
readonly process: Function; | ||
} | ||
export default function createRenderStep(startRenderLoop: Function): RenderStep; | ||
import { Step } from './types'; | ||
declare const _default: (setRunNextFrame: (fillRun: boolean) => void) => Step; | ||
export default _default; |
@@ -1,11 +0,20 @@ | ||
export declare const currentTime: () => number; | ||
export declare const onFrameStart: (callback: Function, immediate?: boolean) => void; | ||
export declare const onFrameUpdate: (callback: Function, immediate?: boolean) => void; | ||
export declare const onFrameRender: (callback: Function, immediate?: boolean) => void; | ||
export declare const onFrameEnd: (callback: Function, immediate?: boolean) => void; | ||
export declare const cancelOnFrameStart: (callback: Function) => void; | ||
export declare const cancelOnFrameUpdate: (callback: Function) => void; | ||
export declare const cancelOnFrameRender: (callback: Function) => void; | ||
export declare const cancelOnFrameEnd: (callback: Function) => void; | ||
export declare const timeSinceLastFrame: () => number; | ||
export declare const currentFrameTime: () => number; | ||
import { Process, FrameData } from './types'; | ||
declare const sync: { | ||
read: (process: Process, keepAlive?: boolean, immediate?: boolean) => Process; | ||
update: (process: Process, keepAlive?: boolean, immediate?: boolean) => Process; | ||
render: (process: Process, keepAlive?: boolean, immediate?: boolean) => Process; | ||
postRender: (process: Process, keepAlive?: boolean, immediate?: boolean) => Process; | ||
fixedUpdate: (process: Process, keepAlive?: boolean, immediate?: boolean) => Process; | ||
}, cancelSync: { | ||
read: (process: Process) => void; | ||
update: (process: Process) => void; | ||
render: (process: Process) => void; | ||
postRender: (process: Process) => void; | ||
fixedUpdate: (process: Process) => void; | ||
}; | ||
declare const getFrameData: () => { | ||
delta: number; | ||
timestamp: number; | ||
}; | ||
export default sync; | ||
export { cancelSync, getFrameData, FrameData, Process }; |
193
lib/index.js
@@ -5,107 +5,138 @@ 'use strict'; | ||
var hasRAF = typeof window !== 'undefined' && window.requestAnimationFrame !== undefined; | ||
var heyListen = require('hey-listen'); | ||
var prevTime = 0; | ||
var onNextFrame = hasRAF | ||
var onNextFrame = typeof window !== 'undefined' && window.requestAnimationFrame !== undefined | ||
? function (callback) { return window.requestAnimationFrame(callback); } | ||
: function (callback) { | ||
var currentTime = Date.now(); | ||
var timeToCall = Math.max(0, 16.7 - (currentTime - prevTime)); | ||
prevTime = currentTime + timeToCall; | ||
var timestamp = Date.now(); | ||
var timeToCall = Math.max(0, 16.7 - (timestamp - prevTime)); | ||
prevTime = timestamp + timeToCall; | ||
setTimeout(function () { return callback(prevTime); }, timeToCall); | ||
}; | ||
function createRenderStep(startRenderLoop) { | ||
var functionsToRun = []; | ||
var functionsToRunNextFrame = []; | ||
var createStep = (function (setRunNextFrame) { | ||
var processToRun = []; | ||
var processToRunNextFrame = []; | ||
var numThisFrame = 0; | ||
var isProcessing = false; | ||
var i = 0; | ||
return { | ||
cancel: function (callback) { | ||
var indexOfCallback = functionsToRunNextFrame.indexOf(callback); | ||
var cancelled = new WeakSet(); | ||
var toKeepAlive = new WeakSet(); | ||
var renderStep = { | ||
cancel: function (process) { | ||
var indexOfCallback = processToRunNextFrame.indexOf(process); | ||
cancelled.add(process); | ||
if (indexOfCallback !== -1) { | ||
functionsToRunNextFrame.splice(indexOfCallback, 1); | ||
processToRunNextFrame.splice(indexOfCallback, 1); | ||
} | ||
}, | ||
process: function () { | ||
process: function (frame) { | ||
var _a; | ||
isProcessing = true; | ||
_a = [functionsToRunNextFrame, functionsToRun], functionsToRun = _a[0], functionsToRunNextFrame = _a[1]; | ||
functionsToRunNextFrame.length = 0; | ||
numThisFrame = functionsToRun.length; | ||
for (i = 0; i < numThisFrame; i++) { | ||
functionsToRun[i](); | ||
_a = [ | ||
processToRunNextFrame, | ||
processToRun | ||
], processToRun = _a[0], processToRunNextFrame = _a[1]; | ||
processToRunNextFrame.length = 0; | ||
numThisFrame = processToRun.length; | ||
if (numThisFrame) { | ||
var process_1; | ||
for (i = 0; i < numThisFrame; i++) { | ||
process_1 = processToRun[i]; | ||
process_1(frame); | ||
if (toKeepAlive.has(process_1) === true && !cancelled.has(process_1)) { | ||
renderStep.schedule(process_1); | ||
setRunNextFrame(true); | ||
} | ||
} | ||
} | ||
isProcessing = false; | ||
var _a; | ||
}, | ||
schedule: function (callback, immediate) { | ||
if (immediate === void 0) { immediate = false; } | ||
startRenderLoop(); | ||
schedule: function (process, keepAlive, immediate) { | ||
heyListen.invariant(typeof process === 'function', 'Argument must be a function'); | ||
var addToCurrentBuffer = immediate && isProcessing; | ||
var buffer = addToCurrentBuffer ? functionsToRun : functionsToRunNextFrame; | ||
if (buffer.indexOf(callback) === -1) { | ||
buffer.push(callback); | ||
if (addToCurrentBuffer) { | ||
numThisFrame = functionsToRun.length; | ||
} | ||
var buffer = addToCurrentBuffer ? processToRun : processToRunNextFrame; | ||
if (keepAlive) | ||
toKeepAlive.add(process); | ||
if (buffer.indexOf(process) === -1) { | ||
buffer.push(process); | ||
if (addToCurrentBuffer) | ||
numThisFrame = processToRun.length; | ||
} | ||
}, | ||
} | ||
}; | ||
} | ||
return renderStep; | ||
}); | ||
var HAS_PERFORMANCE_NOW = typeof performance !== 'undefined' && performance.now !== undefined; | ||
var currentTime = HAS_PERFORMANCE_NOW ? function () { return performance.now(); } : function () { return Date.now(); }; | ||
var willRenderNextFrame = false; | ||
var MAX_ELAPSED = 40; | ||
var defaultElapsed = 16.7; | ||
var StepId; | ||
(function (StepId) { | ||
StepId["Read"] = "read"; | ||
StepId["Update"] = "update"; | ||
StepId["Render"] = "render"; | ||
StepId["PostRender"] = "postRender"; | ||
StepId["FixedUpdate"] = "fixedUpdate"; | ||
})(StepId || (StepId = {})); | ||
var maxElapsed = 40; | ||
var defaultElapsed = (1 / 60) * 1000; | ||
var useDefaultElapsed = true; | ||
var currentFramestamp = 0; | ||
var elapsed = 0; | ||
function startRenderLoop() { | ||
if (willRenderNextFrame) | ||
return; | ||
willRenderNextFrame = true; | ||
useDefaultElapsed = true; | ||
onNextFrame(processFrame); | ||
} | ||
var frameStart = createRenderStep(startRenderLoop); | ||
var frameUpdate = createRenderStep(startRenderLoop); | ||
var frameRender = createRenderStep(startRenderLoop); | ||
var frameEnd = createRenderStep(startRenderLoop); | ||
function processFrame(framestamp) { | ||
willRenderNextFrame = false; | ||
elapsed = useDefaultElapsed | ||
var willRunNextFrame = false; | ||
var isProcessing = false; | ||
var frame = { | ||
delta: 0, | ||
timestamp: 0 | ||
}; | ||
var stepsOrder = [ | ||
StepId.Read, | ||
StepId.Update, | ||
StepId.Render, | ||
StepId.PostRender | ||
]; | ||
var setWillRunNextFrame = function (willRun) { return (willRunNextFrame = willRun); }; | ||
var _a = stepsOrder.reduce(function (acc, key) { | ||
var step = createStep(setWillRunNextFrame); | ||
acc.sync[key] = function (process, keepAlive, immediate) { | ||
if (keepAlive === void 0) { keepAlive = false; } | ||
if (immediate === void 0) { immediate = false; } | ||
if (!willRunNextFrame) | ||
startLoop(); | ||
step.schedule(process, keepAlive, immediate); | ||
return process; | ||
}; | ||
acc.cancelSync[key] = function (process) { return step.cancel(process); }; | ||
acc.steps[key] = step; | ||
return acc; | ||
}, { | ||
steps: {}, | ||
sync: {}, | ||
cancelSync: {} | ||
}), steps = _a.steps, sync = _a.sync, cancelSync = _a.cancelSync; | ||
var processStep = function (stepId) { return steps[stepId].process(frame); }; | ||
var processFrame = function (timestamp) { | ||
willRunNextFrame = false; | ||
frame.delta = useDefaultElapsed | ||
? defaultElapsed | ||
: Math.max(Math.min(framestamp - currentFramestamp, MAX_ELAPSED), 1); | ||
: Math.max(Math.min(timestamp - frame.timestamp, maxElapsed), 1); | ||
if (!useDefaultElapsed) | ||
defaultElapsed = elapsed; | ||
currentFramestamp = framestamp; | ||
frameStart.process(); | ||
frameUpdate.process(); | ||
frameRender.process(); | ||
frameEnd.process(); | ||
if (willRenderNextFrame) | ||
defaultElapsed = frame.delta; | ||
frame.timestamp = timestamp; | ||
isProcessing = true; | ||
stepsOrder.forEach(processStep); | ||
isProcessing = false; | ||
if (willRunNextFrame) { | ||
useDefaultElapsed = false; | ||
} | ||
var onFrameStart = frameStart.schedule; | ||
var onFrameUpdate = frameUpdate.schedule; | ||
var onFrameRender = frameRender.schedule; | ||
var onFrameEnd = frameEnd.schedule; | ||
var cancelOnFrameStart = frameStart.cancel; | ||
var cancelOnFrameUpdate = frameUpdate.cancel; | ||
var cancelOnFrameRender = frameRender.cancel; | ||
var cancelOnFrameEnd = frameEnd.cancel; | ||
var timeSinceLastFrame = function () { return elapsed; }; | ||
var currentFrameTime = function () { return currentFramestamp; }; | ||
onNextFrame(processFrame); | ||
} | ||
}; | ||
var startLoop = function () { | ||
willRunNextFrame = true; | ||
useDefaultElapsed = true; | ||
if (!isProcessing) | ||
onNextFrame(processFrame); | ||
}; | ||
var getFrameData = function () { return frame; }; | ||
exports.currentTime = currentTime; | ||
exports.onFrameStart = onFrameStart; | ||
exports.onFrameUpdate = onFrameUpdate; | ||
exports.onFrameRender = onFrameRender; | ||
exports.onFrameEnd = onFrameEnd; | ||
exports.cancelOnFrameStart = cancelOnFrameStart; | ||
exports.cancelOnFrameUpdate = cancelOnFrameUpdate; | ||
exports.cancelOnFrameRender = cancelOnFrameRender; | ||
exports.cancelOnFrameEnd = cancelOnFrameEnd; | ||
exports.timeSinceLastFrame = timeSinceLastFrame; | ||
exports.currentFrameTime = currentFrameTime; | ||
exports.default = sync; | ||
exports.cancelSync = cancelSync; | ||
exports.getFrameData = getFrameData; |
{ | ||
"name": "framesync", | ||
"version": "3.1.9", | ||
"version": "4.0.0", | ||
"description": "A Unity-inspired render loop for JavaScript", | ||
@@ -15,3 +15,3 @@ "main": "lib/index.js", | ||
"measure": "gzip -c dist/framesync.min.js | wc -c", | ||
"prepublishOnly": "npm run lint && npm run build" | ||
"prepublishOnly": "npm run test && npm run build" | ||
}, | ||
@@ -37,6 +37,9 @@ "files": [ | ||
"devDependencies": { | ||
"jest": "^20.0.4", | ||
"@types/jest": "^23.1.1", | ||
"jest": "^23.1.0", | ||
"jest-cli": "^23.1.0", | ||
"rollup-plugin-node-resolve": "^3.4.0", | ||
"rollup-plugin-typescript2": "^0.14.0", | ||
"rollup-plugin-uglify": "^3.0.0", | ||
"ts-jest": "^20.0.10", | ||
"ts-jest": "^21.2.4", | ||
"typescript": "^2.4.2" | ||
@@ -47,9 +50,9 @@ }, | ||
"ts", | ||
"tsx", | ||
"js" | ||
], | ||
"transform": { | ||
".(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js" | ||
"\\.(ts)$": "../../../node_modules/ts-jest/preprocessor.js" | ||
}, | ||
"testRegex": "/_tests/.*\\.(ts|tsx|js)$" | ||
"testRegex": "/_tests/.*\\.(ts|js)$", | ||
"rootDir": "src" | ||
}, | ||
@@ -60,3 +63,6 @@ "unpkg": "./dist/framesync.min.js", | ||
"singleQuote": true | ||
}, | ||
"dependencies": { | ||
"hey-listen": "^1.0.5" | ||
} | ||
} |
113
README.md
@@ -1,2 +0,2 @@ | ||
# <a href="https://popmotion.io/api/framesync"><img src="https://user-images.githubusercontent.com/7850794/38307669-5e7e3f88-380c-11e8-8c26-970c36801910.png" height="61" width="250" alt="Framesync" /></a> | ||
# Framesync | ||
@@ -7,2 +7,4 @@ A tiny frame scheduler for performantly batching reads and renders. | ||
Popmotion batches updates on the `update` step, and Stylefire batches renders on the `render` step. | ||
## Install | ||
@@ -16,42 +18,93 @@ | ||
The Framesync render loop executes four sequential steps, once per frame. | ||
### Render steps | ||
- `frameStart` | ||
- `frameUpdate` | ||
- `frameRender` | ||
- `frameEnd` | ||
Once per frame, the functions scheduled with Framesync are executed in the following order: | ||
Developers can set any function to run at any of these steps using the `on` and `cancel` callbacks: | ||
- `read` | ||
- `update` | ||
- `render` | ||
- `postRender` | ||
- `onFrameStart`, `cancelOnFrameStart` | ||
- `onFrameUpdate`, `cancelOnFrameUpdate` | ||
- `onFrameRender`, `cancelOnFrameRender` | ||
- `onFrameEnd`, `cancelOnFrameEnd` | ||
### Scheduling functions | ||
Framesync also exports some time-measurement methods: | ||
- `currentTime`: The current time as measured by the host platform's most accurate `now` function. | ||
- `currentFrameTime`: The time the current `requestAnimationFrame` was initiated. | ||
- `timeSinceLastFrame`: The duration between the previous frame and the current `currentFrameTime` | ||
Functions can be scheduled to different parts of the render loop with `sync`. | ||
### Example | ||
```javascript | ||
import sync from 'framesync'; | ||
``` | ||
It contains four functions, one to schedule on each of the above steps: | ||
```javascript | ||
import { | ||
timeSinceLastFrame, | ||
onFrameStart, | ||
cancelFrameStart | ||
} from 'framesync'; | ||
sync[stepId](callback, keepAlive, immediate) | ||
``` | ||
function logTimeSinceLastFrame() { | ||
console.log(timeSinceLastFrame()); | ||
onFrameStart(logTimeSinceLastFrame); | ||
} | ||
```javascript | ||
sync.update(() => console.log('update step')); | ||
``` | ||
onFrameStart(logTimeSinceLastFrame); | ||
### Frame data | ||
function stopLogging() { | ||
cancelOnFrameStart(logTimeSinceLastFrame); | ||
} | ||
Each function is provided data about the current frame: | ||
setTimeout(stopLogging, 5000); | ||
```javascript | ||
sync.update(({ delta, timestamp }) => {}); | ||
``` | ||
- `delta`: Time since last frame (in milliseconds) | ||
- `timestamp`: Timestamp of the current frame. | ||
This object is recycled across frames to reduce garbage collection, so values should be destructured if intended to be used asynchronously. | ||
### keepAlive | ||
We can run a function (or set of functions) as an ongoing process by passing `keepAlive: true`: | ||
```javascript | ||
let count = 0; | ||
sync(() => count++, true); | ||
``` | ||
This will keep the process running until it's actively cancelled. | ||
### immediate | ||
The `immediate` option can be used to sync a function on the **current frame step**. | ||
By default, Framesync will schedule functions to run the next time that frame step is fired: | ||
```javascript | ||
sync.update(({ timestamp }) => { | ||
sync.update((frame) => { | ||
// frame.timestamp !== timestamp | ||
)) | ||
}); | ||
``` | ||
By setting `immediate` to `true`, we can add this at the end of the current step: | ||
```javascript | ||
sync.update(({ timestamp }) => { | ||
sync.update((frame) => { | ||
// frame.timestamp === timestamp | ||
}, false, true) | ||
}); | ||
``` | ||
### Cancelling | ||
Synced processes can be cancelled with `cancelSync`: | ||
```javascript | ||
import sync, { cancelSync } from 'framesync'; | ||
let count = 0; | ||
const process = sync.render(() => { | ||
count++; | ||
if (count >= 10) { | ||
cancelSync.render(process); | ||
} | ||
}, true); | ||
``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
26232
14
485
109
1
8
1
+ Addedhey-listen@^1.0.5
+ Addedhey-listen@1.0.8(transitive)