FramerJS:如何让屏幕不同位置的相似图层同时无限重复动画?
Hey there, I get it—you’re trying to build a twinkling star scene in FramerJS where multiple similar layers (only differing in position and size) animate in an infinite loop simultaneously, but only the first layer in your array is completing the second state animation, right? I’ve run into this exact issue before, and it almost always boils down to shared state or scoping problems in your loop logic. Let’s fix this step by step.
The Root Cause
Chances are, you were using a single shared state variable for all your stars, or your loop wasn’t properly encapsulating each layer’s animation logic. In JavaScript, if you don’t isolate each layer’s state/animation context (like skipping a function wrapper for star creation), all layers end up referencing the same state or closure variable—leading to only the first layer behaving as expected.
Solution 1: Use Per-Layer State + Built-in states.loop
FramerJS has a built-in states.loop method that makes infinite state cycles a breeze. The key is to create independent state configurations for each star so they animate on their own.
// Helper function to create individual stars with their own animation function createStar(xPos, yPos, starSize) { // Create the star layer const star = new Layer({ x: xPos, y: yPos, width: starSize, height: starSize, borderRadius: "50%", backgroundColor: "#ffffff", opacity: 0 // Start hidden }); // Add unique states for this star star.states.add({ twinkleBright: { opacity: 1, scale: 1.2 }, twinkleDim: { opacity: 0, scale: 0.8 } }); // Configure animation settings (duration, curve) star.states.animationOptions = { duration: 0.7, curve: "easeInOut" }; // Start infinite loop between bright and dim states star.states.loop(["twinkleBright", "twinkleDim"], true); return star; } // Generate 15 random stars across the screen for (let i = 0; i < 15; i++) { const randomX = Utils.random(0, Screen.width - 30); const randomY = Utils.random(0, Screen.height - 30); const randomSize = Utils.random(6, 22); createStar(randomX, randomY, randomSize); }
How This Works:
- Each star gets its own
statesobject, so there’s no shared state conflict. - The
states.loopmethod handles the infinite cycle automatically—no need for manualsetTimeoutchains. - The helper function
createStarencapsulates all logic for one star, avoiding scoping bugs in the loop.
Solution 2: Independent State Listeners (Alternative)
If you prefer using Framer’s State class for more control, you can bind a unique state to each star and listen for changes to trigger animations:
function createStar(xPos, yPos, starSize) { const star = new Layer({ x: xPos, y: yPos, width: starSize, height: starSize, borderRadius: "50%", backgroundColor: "#ffffff", opacity: 0 }); // Create a unique state for this star const starState = new State({ isBright: false }); // Animate when the state changes starState.onChange("isBright", (isBright) => { star.animate({ opacity: isBright ? 1 : 0, scale: isBright ? 1.2 : 0.8, duration: 0.7, curve: "easeInOut" }); }); // Toggle state every 1.4 seconds (matches animation duration * 2) setInterval(() => { starState.isBright = !starState.isBright; }, 1400); return star; } // Generate stars same as before for (let i = 0; i < 15; i++) { createStar(Utils.random(0, Screen.width - 30), Utils.random(0, Screen.height - 30), Utils.random(6, 22)); }
Why Your Original Code Failed
Most likely, you were doing one of these:
- Using a single global
Stateobject for all stars—so when you toggled the state, all stars tried to animate, but scoping issues meant only the first one registered the full cycle. - Not encapsulating star creation in a function—leading to all layers referencing the same loop variable, so only the last (or first) star got the correct animation binding.
By isolating each star’s state and animation logic, you ensure every layer runs its own infinite loop smoothly.
内容的提问来源于stack exchange,提问作者Colin Sygiel

