React中使用Framer Motion实现拖拽元素刷新后保持位置
问题
构建支持页面元素拖拽的React应用时,页面刷新后元素会回到初始位置。尝试通过onDragStart和onDragEnd获取拖拽的初始与最终位置并设置为元素初始位置,但无法准确获取位置,也无法保持拖拽后的位置。
相关组件代码:
<motion.div drag dragMomentum={false} dragConstraints={{ right: 1200, }} initial={{ x: -1, y: 367 }} ref={myRef} onDragStart={(event, info) => { initailPosition(event, info); }} onDragEnd={(event, info) => { finalPosition(event, info); }} className={Style.imgCntainer} > <img onPointerDown={(e) => { dragControls.start(e); }} className={Style.file} src={data[0]?.fileLink} alt="img" /> </motion.div>
处理函数代码:
const finalPosition = (event, info) => { console.log("end", info.offset.y, info.offset.x); } const initailPosition = (event, info) => { console.log("start", info.offset.y, info.offset.x); }
解决方案
核心思路
要解决刷新后位置重置的问题,需要持久化存储拖拽后的位置,并在组件初始化时读取该位置;同时修正位置计算逻辑,确保能准确获取拖拽后的最终坐标。
具体实现步骤
- 用状态管理拖拽位置:不再依赖仅首次渲染生效的
initial属性,改用useState存储当前位置,初始化时优先从localStorage读取已保存的位置。 - 拖拽结束时保存位置:在
onDragEnd中计算最终位置,更新状态并将位置存入localStorage,实现持久化。 - 修正位置计算逻辑:
info.offset是拖拽过程中的相对偏移量,需要结合当前状态的位置计算最终绝对坐标,而非直接使用偏移量作为最终位置。
修改后的完整代码
import { motion } from "framer-motion"; import { useState } from "react"; function DraggableImage({ data }) { // 初始化时从localStorage读取位置,无存储则用默认值 const [dragPosition, setDragPosition] = useState(() => { const savedPos = localStorage.getItem("draggableImagePosition"); return savedPos ? JSON.parse(savedPos) : { x: -1, y: 367 }; }); const handleDragEnd = (_, info) => { // 计算拖拽后的最终位置:当前位置 + 拖拽偏移量 const newPosition = { x: dragPosition.x + info.offset.x, y: dragPosition.y + info.offset.y }; setDragPosition(newPosition); // 保存到localStorage localStorage.setItem("draggableImagePosition", JSON.stringify(newPosition)); }; return ( <motion.div drag dragMomentum={false} dragConstraints={{ right: 1200 }} // 用animate绑定状态,实时更新位置 animate={{ x: dragPosition.x, y: dragPosition.y }} className={Style.imgCntainer} onDragEnd={handleDragEnd} > <img className={Style.file} src={data[0]?.fileLink} alt="img" /> </motion.div> ); }
关键说明
- 持久化存储:使用
localStorage保存位置,确保页面刷新后数据不丢失;若需要跨页同步,可改用sessionStorage或后端存储。 - 位置计算:
info.offset.x/info.offset.y是拖拽开始到结束的相对移动距离,必须和当前位置相加才能得到最终的绝对坐标。 - 移除冗余代码:无需手动调用
dragControls.start,motion的drag属性已自带完整拖拽功能。 - 状态驱动渲染:通过
animate绑定dragPosition状态,确保拖拽后位置实时更新,而非仅依赖首次渲染的initial属性。
内容的提问来源于stack exchange,提问作者Asif Hossain
相关产品推荐
相关产品推荐

