Framer Motion拖拽SVG位移异常:viewBox与窗口尺寸不匹配
解决Framer Motion在SVG拖拽时viewBox导致的位移缩放问题
当SVG的viewBox与实际视口尺寸不匹配时,Framer Motion默认的拖拽逻辑会直接使用屏幕像素坐标,导致元素位移和鼠标移动不成比例。要解决这个问题,你可以通过SVG的**CTM(当前变换矩阵)**将屏幕坐标转换为SVG内部的用户坐标系,再结合Framer的useMotionValue和useTransform钩子实现适配。
实现步骤
- 获取SVG元素的DOM引用,用于计算CTM
- 用
useMotionValue跟踪拖拽的原始屏幕坐标 - 通过
useTransform将屏幕坐标转换为SVG内部坐标 - 将转换后的坐标绑定到可拖拽元素的位置属性
完整代码示例
import { motion, useMotionValue, useTransform, useDrag } from "framer-motion"; import { useRef } from "react"; export default function SVGDrag() { const svgRef = useRef(null); // 跟踪拖拽的屏幕坐标,初始值对应元素初始位置 const dragX = useMotionValue(5); const dragY = useMotionValue(5); // 转换屏幕坐标到SVG用户坐标系 const svgX = useTransform(dragX, (value) => { if (!svgRef.current) return value; const ctm = svgRef.current.getScreenCTM(); return (value - ctm.e) / ctm.a; }); const svgY = useTransform(dragY, (value) => { if (!svgRef.current) return value; const ctm = svgRef.current.getScreenCTM(); return (value - ctm.f) / ctm.d; }); // 绑定拖拽事件,更新屏幕坐标值 const bindDrag = useDrag({ onDrag: (_event, info) => { dragX.set(info.point.x); dragY.set(info.point.y); }, initial: { x: 5, y: 5 }, }); return ( <svg ref={svgRef} viewBox="0 0 40 20" style={{ width: "100vw", height: "100vh" }}> <motion.circle {...bindDrag} cx={svgX} cy={svgY} r="0.5" strokeWidth="0.1" fill="blue" /> </svg> ); }
关键逻辑说明
- CTM转换:
getScreenCTM()返回SVG元素到屏幕坐标系的变换矩阵,通过逆运算将屏幕坐标转换为SVG内部用户坐标:- 用户X坐标:
(screenX - ctm.e) / ctm.a - 用户Y坐标:
(screenY - ctm.f) / ctm.d
- 用户X坐标:
- useTransform:实时将原始拖拽的屏幕坐标转换为适配SVG的坐标,确保鼠标移动和元素位移完全匹配
- useDrag:手动绑定拖拽事件获取屏幕坐标,替代Framer默认的拖拽位置绑定,避免直接使用不匹配的像素单位
这种方法不需要修改viewBox,完全通过坐标转换适配现有SVG的缩放比例,完美解决位移不匹配的问题。
内容的提问来源于stack exchange,提问作者Klak031
相关产品推荐
相关产品推荐

