You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;

问题分析

  1. 闭包捕获旧值:useEffect依赖为空数组,组件初始化时仅执行一次,此时position被捕获为初始值{x:0,y:0},后续pan事件触发时始终使用这个旧值计算,导致每次拖动都从0累加。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 13:51:29