From 20a64f57619514eaf1c0d121644529ac160d99b3 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Tue, 9 Jul 2024 12:34:56 +0200 Subject: [PATCH] Render loop optimisations (#2722) * Unrolling renderloop * Improving render loop performance --- packages/framer-motion/package.json | 4 +-- .../framer-motion/src/frameloop/batcher.ts | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/framer-motion/package.json b/packages/framer-motion/package.json index 3239778e40..7b244e848f 100644 --- a/packages/framer-motion/package.json +++ b/packages/framer-motion/package.json @@ -85,11 +85,11 @@ "bundlesize": [ { "path": "./dist/size-rollup-motion.js", - "maxSize": "33.4 kB" + "maxSize": "33.45 kB" }, { "path": "./dist/size-rollup-m.js", - "maxSize": "5.8 kB" + "maxSize": "5.85 kB" }, { "path": "./dist/size-rollup-dom-animation.js", diff --git a/packages/framer-motion/src/frameloop/batcher.ts b/packages/framer-motion/src/frameloop/batcher.ts index 07bdcc347f..11c823244a 100644 --- a/packages/framer-motion/src/frameloop/batcher.ts +++ b/packages/framer-motion/src/frameloop/batcher.ts @@ -26,14 +26,15 @@ export function createRenderBatcher( isProcessing: false, } + const flagRunNextFrame = () => (runNextFrame = true) + const steps = stepsOrder.reduce((acc, key) => { - acc[key] = createRenderStep(() => (runNextFrame = true)) + acc[key] = createRenderStep(flagRunNextFrame) return acc }, {} as Steps) - const processStep = (stepId: StepId) => { - steps[stepId].process(state) - } + const { read, resolveKeyframes, update, preRender, render, postRender } = + steps const processBatch = () => { const timestamp = MotionGlobalConfig.useManualTiming @@ -47,7 +48,15 @@ export function createRenderBatcher( state.timestamp = timestamp state.isProcessing = true - stepsOrder.forEach(processStep) + + // Unrolled render loop for better per-frame performance + read.process(state) + resolveKeyframes.process(state) + update.process(state) + preRender.process(state) + render.process(state) + postRender.process(state) + state.isProcessing = false if (runNextFrame && allowKeepAlive) { @@ -75,8 +84,11 @@ export function createRenderBatcher( return acc }, {} as Batcher) - const cancel = (process: Process) => - stepsOrder.forEach((key) => steps[key].cancel(process)) + const cancel = (process: Process) => { + for (let i = 0; i < stepsOrder.length; i++) { + steps[stepsOrder[i]].cancel(process) + } + } return { schedule, cancel, state, steps } }