如何用Framer Motion实现基于鼠标移动的平滑Transform动画?
解决Framer Motion中卡片3D旋转的平滑过渡问题
核心问题分析
你当前直接通过transform.set()赋值旋转参数,属于瞬时更新,不会触发Framer Motion的过渡动画。要实现平滑效果,需要利用Framer Motion的**弹簧动画(Spring)**来驱动旋转值的渐进变化,而非直接设置静态值。
修改后的完整代码
import { motion, useMotionValue, useSpring, useTransform } from "framer-motion"; import { useState, useRef } from "react"; const HoverCard = ({ i }: { i: number }) => { const [isMaximized, setIsMaximized] = useState<boolean>(false); const cardRef = useRef<HTMLDivElement>(null); // 用弹簧动画控制X/Y轴旋转角度,参数可调整动画质感 const rotateX = useSpring(0, { stiffness: 200, damping: 25 }); const rotateY = useSpring(0, { stiffness: 200, damping: 25 }); // 将两个旋转值组合成最终的transform字符串 const transform = useTransform([rotateX, rotateY], ([x, y]) => `perspective(1000px) rotateX(${x}deg) rotateY(${y}deg)` ); const maxRotation = 18; // 限制最大旋转角度,避免过度倾斜 const message = ['1', '2', '3', "4", '5', '6', '!', '']; const handleClick = () => { setIsMaximized(!isMaximized); }; const handleMouseMove = (e: MouseEvent<HTMLDivElement>) => { if (!cardRef.current) return; const rect = cardRef.current.getBoundingClientRect(); // 计算鼠标在卡片内的相对位置(0-1范围) const xPercent = (e.clientX - rect.left) / rect.width; const yPercent = (e.clientY - rect.top) / rect.height; // 转换为相对于中心的偏移,映射到设定的旋转角度范围 const targetRotateY = (xPercent - 0.5) * maxRotation * 2; const targetRotateX = -(yPercent - 0.5) * maxRotation * 2; // 更新弹簧动画目标值,自动触发平滑过渡 rotateX.set(targetRotateX); rotateY.set(targetRotateY); }; const handleMouseLeave = () => { // 鼠标离开时复位到初始角度 rotateX.set(0); rotateY.set(0); }; return ( <> <div ref={cardRef} className={`rounded-[8px] aspect-[16/9]`}> <motion.div onMouseMove={handleMouseMove} onMouseLeave={handleMouseLeave} layout={true} style={{ transform }} className={`bg-black h-full p-4`} onClick={handleClick} > <motion.div layout="position" className="text-white" > {message[i]} </motion.div> </motion.div> </div> </> ); }; export default HoverCard;
关键改动说明
- 用
useSpring实现平滑过渡:useSpring会自动处理值的渐进变化,stiffness(刚度)和damping(阻尼)参数可调整动画的弹性和缓动速度,贴近目标交互的质感。 - 简化角度计算逻辑:直接在鼠标移动事件中计算相对位置并映射到旋转角度,避免依赖
state更新,减少不必要的组件重渲染。 useTransform组合旋转值:将两个独立的弹簧旋转值合并为最终的transform字符串,Framer Motion会自动处理该值的动画更新。- 新增鼠标离开复位:当鼠标移出卡片时,触发弹簧动画复位到初始角度,完善交互闭环。
- 移除冗余状态:删掉
isHovered、mouseX、mouseY等非必要状态变量,优化性能。
额外优化建议
- 调整
useSpring的stiffness和damping参数,比如将stiffness设为150、damping设为20,可让动画更柔和。 - 若需完全匹配目标网站的旋转范围,可修改
maxRotation值(原网站旋转角度约在±20度区间)。
内容的提问来源于stack exchange,提问作者Ashbury
相关产品推荐
相关产品推荐

