React-draggable组件渲染位置偏移问题及修复方案咨询
问题解决:单词磁力贴加载位置偏移修复
问题根源
你当前代码的核心问题是保存的是鼠标点击点的坐标,而非Word组件左上角的坐标:
- 在
endPosition函数中,你直接使用position.clientX/clientY(鼠标指针在页面上的绝对坐标)保存位置,但Draggable组件的定位逻辑是基于组件左上角的偏移坐标。 - 当点击组件非左上角位置时,保存的鼠标坐标和组件左上角坐标存在差值,页面加载时就会出现对应偏移。
解决方案
使用react-draggable提供的data对象获取组件的真实左上角位置,而非鼠标坐标:
react-draggable的onStop回调参数为(event, data),其中data.x和data.y是组件相对于父容器的左上角偏移量,这正是需要保存的位置。- 修正
words.map中的key值(原代码误用数组作为id,应使用word自身的id),避免列表渲染异常。 - 用受控属性
position替代defaultPosition,确保加载时精准匹配保存的位置。
修改后的完整代码
import React from 'react' import Draggable from 'react-draggable' import Scene from './Scene' import Word from './Word' import Header from './Header' const FridgeMagnets = () => { const [words, setWords] = React.useState([]) // 设置拖拽边界 const viewportWidth = window.innerWidth const bounds = { left: viewportWidth * 0.025, top: 15, right: viewportWidth * 0.44, bottom: viewportWidth } React.useEffect(() => { // 从localStorage加载数据 const localStorageWords = JSON.parse(localStorage.getItem('words')) if (localStorageWords) { setWords(localStorageWords) } else { setWords([ { text: 'word', position: { x: 910, y: 210 }, id: 1 }, { text: 'the', position: { x: 310, y: 210 }, id: 2 }, { text: 'power', position: { x: 310, y: 510 }, id: 3 }, { text: 'are', position: { x: 110, y: 610 }, id: 4 }, { text: 'magnet', position: { x: 110, y: 650 }, id: 5 }, { text: 'fridge', position: { x: 120, y: 700 }, id: 6 }, { text: 'local', position: { x: 210, y: 410 }, id: 7 }, { text: 'yes', position: { x: 310, y: 80 }, id: 8 } ]) } }, []) // 更新单词位置(使用draggable提供的data对象) const endPosition = (event, data) => { // 找到当前拖拽的单词(通过data.source.id关联) const updatedWords = words.map(word => { if (word.id === Number(data.source.id)) { return { ...word, position: { x: data.x, y: data.y } } } return word }) setWords(updatedWords) } // 保存到localStorage const handleSave = () => { localStorage.setItem('words', JSON.stringify(words)) } return ( <Scene> <div className="relative"> <Header /> </div> {words.map(word => { return ( <Draggable key={word.id} position={word.position} // 受控属性,确保加载时精准定位 bounds={bounds} onStop={endPosition} > <div id={word.id}> <Word text={word.text} id={word.id} /> </div> </Draggable> ) })} <button onClick={handleSave}>save</button> </Scene> ) } export default FridgeMagnets
关键修改点说明
- 移除了冗余的
dragWord状态,通过data.source.id直接关联当前拖拽的单词,逻辑更简洁。 - 用
position替代defaultPosition,react-draggable中position是受控属性,能确保加载时完全匹配保存的位置,而defaultPosition仅在初始渲染生效。 - 使用
data.x/data.y保存组件左上角的偏移量,彻底解决鼠标点击位置导致的偏移问题。 - 用
word.id作为Draggable的key,避免列表重渲染时的组件复用异常。
内容的提问来源于stack exchange,提问作者Adk1ns
相关产品推荐
相关产品推荐

