You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用react-spring的useChain实现循环加载动画?

Answer

Great question! The core issue here is that useChain only runs once by default, and your current animation setup doesn't include logic to restart the sequence once it finishes. Let's break down two solid solutions to get your looping loading animation working.

Solution 1: Add loop logic to useChain manually

We can keep using useChain, but we'll add a listener to the last animation in the sequence to restart the chain once it completes. Here's how to adjust your code:

First, update your animation config to explicitly include loop behavior for each individual spring (we'll split the "scale up/down" into a looped animation instead of a multi-step to array):

const animation = { 
  from: { scale: 0, opacity: 0, background: 'red' }, 
  to: { scale: 1, opacity: 1 },
  reverse: { scale: 0, opacity: 0 }, // Defines the "shrink back" state
  config: { duration: 500 },
  loop: true // Makes each spring repeat its own animation
}

Then, modify your component to restart the useChain sequence when the last animation finishes:

import { useSpring, useSpringRef, useChain, animated, useCallback, useEffect } from 'react-spring'

const Loading = () => { 
  const leftBottomRef = useSpringRef() 
  const leftBottom = useSpring({ ref: leftBottomRef, ...animation }) 
  const leftTwoRef = useSpringRef() 
  const leftTwo = useSpring({ ref: leftTwoRef, ...animation }) 
  const centerRef = useSpringRef() 
  const center = useSpring({ ref: centerRef, ...animation }) 
  const rightTwoRef = useSpringRef() 
  const rightTwo = useSpring({ ref: rightTwoRef, ...animation }) 
  const rightTopRef = useSpringRef() 
  const rightTop = useSpring({ ref: rightTopRef, ...animation }) 

  // Define a reusable function to start the chain sequence
  const startChainLoop = useCallback(() => {
    useChain(
      [leftBottomRef, leftTwoRef, centerRef, rightTwoRef, rightTopRef],
      [0, 0.3, 0.6, 0.9, 1.2], // Timing delays between each animation
    )
    // Restart the chain when the last animation finishes its cycle
    rightTopRef.current.on('rest', startChainLoop)
  }, [leftBottomRef, leftTwoRef, centerRef, rightTwoRef, rightTopRef])

  // Start the first chain loop when the component mounts
  useEffect(() => {
    startChainLoop()
    // Clean up the listener when the component unmounts
    return () => {
      rightTopRef.current.off('rest', startChainLoop)
    }
  }, [startChainLoop, rightTopRef])

  return ( 
    <div className='loading'> 
      <div className='loader'> 
        <animated.div style={center} /> 
        <animated.div style={rightTwo} /> 
        <animated.div style={rightTop} /> 
        <animated.div style={leftTwo} /> 
        <animated.div style={center} /> 
        <animated.div style={rightTwo} /> 
        <animated.div style={leftBottom} /> 
        <animated.div style={leftTwo} /> 
        <animated.div style={center} /> 
      </div> 
      loading... 
    </div> 
  ) 
}

Solution 2: Use useTrail for simpler sequential looping

If your goal is to animate multiple elements in a repeating sequence, useTrail is a better fit than useChain + individual useSpring calls. It's designed specifically for sequential animations and supports looping out of the box:

import { useTrail, animated } from 'react-spring'

const Loading = () => {
  // Number of unique animation sequences (matches your 5 original refs)
  const trailCount = 5
  // Create a trail of animations with built-in looping and delays
  const trail = useTrail(trailCount, {
    from: { scale: 0, opacity: 0, background: 'red' },
    to: { scale: 1, opacity: 1 },
    reverse: { scale: 0, opacity: 0 },
    config: { duration: 500 },
    loop: true,
    delay: 300, // Delay between each element's animation start
  })

  return (
    <div className='loading'>
      <div className='loader'>
        {/* Map your elements to the trail styles (match your original layout) */}
        <animated.div style={trail[2]} /> {/* Center */}
        <animated.div style={trail[3]} /> {/* Right Two */}
        <animated.div style={trail[4]} /> {/* Right Top */}
        <animated.div style={trail[1]} /> {/* Left Two */}
        <animated.div style={trail[2]} /> {/* Center */}
        <animated.div style={trail[3]} /> {/* Right Two */}
        <animated.div style={trail[0]} /> {/* Left Bottom */}
        <animated.div style={trail[1]} /> {/* Left Two */}
        <animated.div style={trail[2]} /> {/* Center */}
      </div>
      loading...
    </div>
  )
}

Key fixes for your original code:

  1. No built-in loop for useChain: useChain doesn't auto-repeat, so we need to manually listen for the last animation's rest event to restart the sequence.
  2. Multi-step to array issues: Your original to array made each spring run two animations (scale up then down) once, but combining it with loop: true caused timing conflicts. Splitting into to and reverse makes the loop behavior predictable.
  3. Shared animation states: Reusing the same center/rightTwo styles across multiple elements made them animate in sync. Using useTrail or ensuring each element has its own ref avoids this.

内容的提问来源于stack exchange,提问作者menaka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 06:56:18