React可滑动组件拖动时始终从[0,0]位置起始的问题
滑动组件拖动时位置重置为[0,0]的解决方案
我用React(配合Redux)和hammerjs开发可滑动组件,遇到的问题是:每次拖动组件时,position的x、y值都会从[0,0]开始计算,无法基于组件当前位置累加。
相关代码
import React, { useState, useEffect } from 'react'; import Hammer from 'hammerjs'; import WeatherForecast from './WeatherForecast'; const SwipeableCard = ({ children }) => { const [gesture, setGesture] = useState(null); const [position, setPosition] = useState({ x: 0, y: 0 }); useEffect(() => { const element = document.getElementById('swipeable-card'); const hammer = new Hammer(element); hammer.on('pan', (event) => { setPosition({ x: event.deltaX + position.x, y: event.deltaY + position.y, }); }); setGesture(hammer); return () => { hammer.off('pan'); setGesture(null); }; }, []); return ( <div id="swipeable-card" className="App-swipeable" style={{ transform: `translate(${position.x}px, ${position.y}px)`, }} > <WeatherForecast /> </div> ); }; export default SwipeableCard;
问题分析
- 闭包捕获旧值:
useEffect依赖为空数组,组件初始化时仅执行一次,此时position被捕获为初始值{x:0,y:0},后续pan事件触发时始终使用这个旧值计算,导致每次拖动都从0累加。 - Hammer delta值特性:
event.deltaX/event.deltaY是本次pan手势从开始到当前的偏移量,而非相对于上一次事件的变化量,直接与当前position相加会导致重复计算。
修复代码
import React, { useState, useEffect, useRef } from 'react'; import Hammer from 'hammerjs'; import WeatherForecast from './WeatherForecast'; const SwipeableCard = ({ children }) => { const [position, setPosition] = useState({ x: 0, y: 0 }); // 用ref保存手势开始时的位置,避免闭包问题 const startPositionRef = useRef({ x: 0, y: 0 }); useEffect(() => { const element = document.getElementById('swipeable-card'); const hammer = new Hammer(element); // 手势开始时记录当前组件位置 hammer.on('panstart', () => { startPositionRef.current = { ...position }; }); // 拖动时基于初始位置计算新位置 hammer.on('pan', (event) => { setPosition({ x: startPositionRef.current.x + event.deltaX, y: startPositionRef.current.y + event.deltaY, }); }); // 清理事件监听 return () => { hammer.off('panstart pan'); }; }, [position]); // 依赖position,确保事件逻辑能获取最新状态 return ( <div id="swipeable-card" className="App-swipeable" style={{ transform: `translate(${position.x}px, ${position.y}px)`, }} > <WeatherForecast /> </div> ); }; export default SwipeableCard;
关键修改说明
- 使用
useRef存储手势开始时的位置,避免闭包捕获旧的position值 - 添加
panstart事件监听,在拖动开始时记录组件当前位置 useEffect依赖项加入position,确保每次状态更新后事件处理逻辑能获取最新值- 拖动时直接用开始位置加上本次手势的偏移量,保证计算基于当前组件位置
内容的提问来源于stack exchange,提问作者user3466681
相关产品推荐
相关产品推荐

