Framesync
Framesync is a JavaScript library designed for scheduling functions into a synchronized render loop, primarily to prevent layout thrashing and ensure predictable execution order within a browser frame. It provides discrete steps for `read`, `update`, `preRender`, `render`, and `postRender` operations, allowing developers to organize DOM interactions efficiently. The current stable version is 6.1.2, and as part of the Popmotion ecosystem, it maintains a steady, mature release cadence focusing on stability and performance. Its primary differentiator is the explicit segregation of frame steps, which is crucial for high-performance animations and UI updates, notably used by libraries like Framer Motion to manage complex transform animations independently. Functions scheduled with Framesync receive frame data including `delta` (time since last frame) and `timestamp`.
Common errors
-
TypeError: framesync__WEBPACK_IMPORTED_MODULE_0__.default.update is not a function
cause Attempting to use CommonJS `require` or an incorrect import statement with a bundler that doesn't handle ESM default exports properly, leading to `sync` being an object with a `default` property, not the `sync` object itself.fixEnsure you are using `import sync from 'framesync';` for the default export. If using CommonJS, try `const sync = require('framesync').default;` (though this is not recommended for pure ESM libraries). -
Uncaught TypeError: cancelSync.render is not a function
cause `cancelSync` is a named export, and its methods (`.read`, `.update`, `.render`, etc.) are functions. This error often occurs if `cancelSync` itself is not correctly imported as a named export.fixEnsure `cancelSync` is imported as a named export: `import { cancelSync } from 'framesync';`
Warnings
- gotcha The `FrameData` object (containing `delta` and `timestamp`) passed to scheduled functions is recycled across frames for performance. If you need to use `delta` or `timestamp` asynchronously (e.g., in a `setTimeout` or `Promise`), you must destructure or copy its values, otherwise, they may change unexpectedly.
- gotcha By default, `sync` functions schedule execution for the *next* time that frame step is fired. To execute a function on the *current* frame step, you must explicitly pass `true` as the third parameter for `immediate` execution.
- gotcha When using the `keepAlive: true` option, a scheduled function will run indefinitely on every frame until it is explicitly cancelled using `cancelSync.<step>(process)`. Forgetting to cancel can lead to memory leaks and unnecessary CPU usage.
Install
-
npm install framesync -
yarn add framesync -
pnpm add framesync
Imports
- sync
const sync = require('framesync');import sync from 'framesync';
- cancelSync
const { cancelSync } = require('framesync');import { cancelSync } from 'framesync'; - FrameData
import type { FrameData } from 'framesync';
Quickstart
import sync, { cancelSync } from 'framesync';
let animationProgress = 0;
const targetProgress = 100;
console.log('Starting framesync animation...');
const updateFunction = ({ delta, timestamp }) => {
// In a real app, 'delta' would be used to calculate frame-rate independent updates.
// For this example, we'll just increment.
animationProgress += 1;
// Simulate some DOM read operation (e.g., getBoundingClientRect)
const elementWidth = document.body?.clientWidth || 0;
console.log(`[Frame ${timestamp.toFixed(2)}] Delta: ${delta.toFixed(2)}ms, Progress: ${animationProgress}, Width: ${elementWidth}`);
if (animationProgress >= targetProgress) {
cancelSync.update(updateFunction);
console.log('Animation complete!');
}
};
sync.update(updateFunction, true); // Schedule to run indefinitely on the update step
// Simulate an immediate render call for some initial setup
sync.render(() => {
// This would typically update DOM properties
// For example: document.body.style.transform = `translateX(${animationProgress}px)`;
console.log('Initial render step executed immediately.');
}, false, true);