diff --git a/.changeset/tasty-kangaroos-greet.md b/.changeset/tasty-kangaroos-greet.md new file mode 100644 index 000000000000..407057155c56 --- /dev/null +++ b/.changeset/tasty-kangaroos-greet.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: add `onAnimationFrame` lifecycle function diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index fd8e999da763..816e2b0a113d 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -198,6 +198,32 @@ export function afterUpdate(fn) { init_update_callbacks(component_context).a.push(fn); } +/** + * The `onAnimationFrame` function schedules a callback to run on `requestAnimationFrame`. It must be called inside an effect (e.g. during component initialisation). + * + * `onAnimationFrame` does not run inside [server-side components](https://svelte.dev/docs/svelte/svelte-server#render). + * + * @template T + * @param {() => NotFunction | Promise> | (() => any)} fn + * @returns {void} + */ +export function onAnimationFrame(fn) { + if (component_context === null) { + lifecycle_outside_component('onAnimationFrame'); + } + + user_effect(() => { + let frame = requestAnimationFrame(function next() { + frame = requestAnimationFrame(next); + fn(); + }); + + return () => { + cancelAnimationFrame(frame); + }; + }); +} + /** * Legacy-mode: Init callbacks object for onMount/beforeUpdate/afterUpdate * @param {ComponentContext} context diff --git a/packages/svelte/src/index-server.js b/packages/svelte/src/index-server.js index 0f1aff8f5aa7..2fc726617ce4 100644 --- a/packages/svelte/src/index-server.js +++ b/packages/svelte/src/index-server.js @@ -12,6 +12,7 @@ export function onDestroy(fn) { export { noop as beforeUpdate, noop as afterUpdate, + noop as onAnimationFrame, noop as onMount, noop as flushSync, run as untrack diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 6f12daf18778..bc1ce3ca980d 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -410,6 +410,13 @@ declare module 'svelte' { * @deprecated Use [`$effect`](https://svelte.dev/docs/svelte/$effect) instead * */ export function afterUpdate(fn: () => void): void; + /** + * The `onAnimationFrame` function schedules a callback to run on `requestAnimationFrame`. It must be called inside an effect (e.g. during component initialisation). + * + * `onAnimationFrame` does not run inside [server-side components](https://svelte.dev/docs/svelte/svelte-server#render). + * + * */ + export function onAnimationFrame(fn: () => NotFunction | Promise> | (() => any)): void; /** * Create a snippet programmatically * */