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

如何让鼠标拖拽触发的Framer Motion动画流畅且避免重渲染?

如何在Framer Motion中实现无重渲染的流畅拖拽步进动画

我正在开发一个鼠标拖拽步进调整盒子大小的功能,使用Framer Motion添加动画时遇到了两难问题:

  • 用useState管理盒子参数时,动画流畅但每次鼠标移动都会触发组件重渲染
  • 用useAnimation避免重渲染时,动画播放过程中鼠标移动会出现卡顿,换成set方法虽然不卡但会丢失动画效果

问题根源

使用useAnimation时卡顿的核心原因是:每次mousemove触发都调用controls.start(),会不断创建新的动画实例、中断正在进行的动画,导致动画队列混乱,最终出现卡顿。

解决方案

通过useRef跟踪当前目标状态,仅当目标状态发生变化时才启动动画,既避免组件重渲染,又保证动画流畅:

import { useRef, type MouseEvent as ReactMouseEvent } from 'react'
import { motion, useAnimation } from 'framer-motion'

const STEP_SIZE = 40;

export default function App() {
  console.log('render')
  const controls = useAnimation()
  // 用ref存储当前目标参数,更新ref不会触发组件重渲染
  const currentParams = useRef({ top: 0, height: 0 })

  const handlerMouseDown = (e: ReactMouseEvent<HTMLDivElement>) => {
    const initialPosition = e.clientY
    const handlerMouseMove = (e: MouseEvent) => {
      const movement = e.clientY - initialPosition
      const steps = Math.ceil(Math.abs(movement) / STEP_SIZE)
      const newTop = movement > 0 ? initialPosition : initialPosition - steps * STEP_SIZE
      const newHeight = steps * STEP_SIZE

      // 仅当目标参数变化时启动动画,避免频繁创建动画实例
      if (newTop !== currentParams.current.top || newHeight !== currentParams.current.height) {
        currentParams.current = { top: newTop, height: newHeight }
        controls.start({
          top: newTop,
          height: newHeight,
          // 调整动画曲线和时长,适配拖拽步进的手感
          ease: "easeOut",
          duration: 0.15
        })
      }
    }

    const handlerMouseUp = () => {
      window.removeEventListener('mousemove', handlerMouseMove)
      window.removeEventListener('mouseup', handlerMouseUp)
    }

    window.addEventListener('mousemove', handlerMouseMove)
    window.addEventListener('mouseup', handlerMouseUp)
  }

  return (
    <div className="Container" onMouseDown={handlerMouseDown}>
      <motion.div className="Selector" animate={controls}></motion.div>
    </div>
  );
}

方案说明

  1. 避免重渲染:使用useRef存储目标参数,更新ref不会触发React组件的重渲染流程
  2. 流畅动画:仅在计算出的新参数与当前ref存储的参数不一致时,才调用controls.start()启动动画,避免频繁中断动画
  3. 手感优化:通过调整ease和duration参数,可以让步进动画的过渡更自然,匹配拖拽操作的节奏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 14:53:17