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

Next.js/React滑块点击后动画延迟1-3秒,疑useState所致求优化方案

问题分析与解决方案

核心问题

  1. 闭包陷阱:点击事件中访问的isLong和pathIndex是组件当前渲染周期的旧值,React的useState更新是异步的,这会导致动画逻辑判断错误,进而引发延迟或异常。
  2. 无用状态:color状态未被使用,却会触发组件重渲染,增加不必要的性能开销。
  3. Motion状态与React状态不同步:progress(Framer Motion的useMotionValue)和pathIndex(React状态)未绑定同步,页面初始化或状态更新时可能出现状态不一致,导致动画触发延迟。

优化方案

1. 用useRef替代useState存储状态

useRef的current属性是实时可变的,不会被闭包捕获旧值,能避免异步更新带来的状态不一致问题,同时减少组件因useState更新触发的重渲染。

2. 移除无用状态

删掉未使用的color状态,减少组件重渲染次数。

3. 同步Motion状态与逻辑判断

直接通过progress的当前值或ref存储的状态来控制动画逻辑,确保状态实时生效。

修改后的代码

import { motion, useMotionValue, animate, useTransform } from 'framer-motion'
import * as React from 'react'
import { useRef, useEffect } from 'react'
import { getIndex, useFlubber } from "./use-flubber";
import { paths } from './paths.jsx'

const colors = [
  '#000000',
  '#000000'
]

export default function AnimationBar(){
  // 用useRef替代useState,避免闭包陷阱
  const isLongRef = useRef(false);
  const pathIndexRef = useRef(1);
  
  const progress = useMotionValue(pathIndexRef.current);
  const fill = useTransform(progress, paths.map(getIndex), colors);
  const path = useFlubber(progress, paths);

  // 初始化时同步progress和pathIndex
  useEffect(() => {
    progress.set(pathIndexRef.current);
  }, []);

  return(
    <div className="justify-center mx-auto">
    <motion.svg
      width="300"
      height="100"
      viewBox="0 0 300 100"
      initial="hidden"
      animate="visible"
      className="justify-center mx-auto">
      
      <motion.line 
        x1="50"
        y1="30"
        x2="250"
        y2="30"
        stroke="#570df8"
        variants={{
          short: { x2: 50 },
          long: { x2: 200 }
        }}
        animate={isLongRef.current ? "long" : "short"}
      />

      <motion.g
        transform="translate(40 18)"
        clipPath="url(#_clipPath_rwZJXlpW1ubtVOGd58AkztYmsTFUvvCx)"
      >
        <motion.path
          d={path}
          fill={fill}
          stroke="none"
        />
      </motion.g>
      
      <motion.text
        x={75}
        y={40}
        fontSize={24}
        fill={fill}
        animate={isLongRef.current ? "black" : "white"}
      >
        {isLongRef.current ? "Stop" : "Start"}
      </motion.text>
      
      <motion.line
        x1="50"
        y1="30"
        x2="50"
        y2="30"
        stroke="black"
        opacity="0"
        onClick={() => {
          // 实时更新ref的值
          isLongRef.current = !isLongRef.current;
          const currentPathIndex = pathIndexRef.current;

          const animation = animate(progress, currentPathIndex, {
            duration: 0.3,
            ease: "easeInOut",
            onComplete: () => {
              if (!isLongRef.current) {
                progress.set(0);
                pathIndexRef.current = 1;
              } else {
                pathIndexRef.current = 0;
              }
            }
          });
      
          return () => animation.stop();
        }}
      />
    </motion.svg>
    </div>
  )
}

额外优化建议

  • 检查use-flubber自定义hook的实现,确保其内部没有性能瓶颈(比如频繁的计算或不必要的重渲染)。
  • 页面初始化时的动画延迟,可以尝试将initial="hidden"改为initial={false},避免初始动画的额外开销,或者调整Framer Motion的初始配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 00:40:19