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

Tab容器触摸滚动异常问题排查及替代实现方案咨询

问题描述

我有一个包含若干Tab的容器div,在桌面端通过onMouseMove事件实现了拖拽滚动,对应的处理函数如下:

function dragging(e: React.MouseEvent<HTMLDivElement, MouseEvent>) {
  if (!filterTabsContainer.current) return;
  filterTabsContainer.current.scrollLeft -= e.movementX;
}

但需要在移动端也实现该功能,而触摸事件对象中不存在movementX属性,因此我编写了如下触摸事件处理逻辑:

let previousTouch: number = 0;
let movementX: number = 0;
let prevScrollLeft: number = 0;

function draggingTouch(e: React.TouchEvent<HTMLDivElement>) {
    if (!filterTabsContainer.current) return;

    const touch = e.touches[0];

    /* 此处保存上次scrollLeft的值,否则再次触摸拖拽时会回到0,prevScrollLeft将在onTouchEnd的处理函数中赋值;*/

    if (prevScrollLeft > 0) {
      filterTabsContainer.current.scrollLeft = prevScrollLeft - movementX;
    } else {
      filterTabsContainer.current.scrollLeft -= movementX;
    }

    if (previousTouch) {
      movementX = touch.pageX - previousTouch;
    }
    previousTouch = touch.pageX;
}

该逻辑首次拖拽时正常,但从上次拖拽后的位置再次拖拽时,仅能滚动约5px后便停止,无法正常工作。请问是否有其他可行的实现方案?


可行实现方案

核心思路是在touchstart阶段记录触摸初始位置和容器当前滚动值,touchmove阶段基于初始值计算偏移量,直接更新滚动位置,避免状态混乱。

完整代码实现

import { useRef, useEffect } from 'react';

// 用ref保存状态,避免组件重渲染影响值的稳定性
const filterTabsContainer = useRef<HTMLDivElement>(null);
const touchStartX = useRef<number>(0);
const startScrollLeft = useRef<number>(0);

// 触摸开始:记录初始基准值
const handleTouchStart = (e: React.TouchEvent<HTMLDivElement>) => {
  if (!filterTabsContainer.current) return;
  const touch = e.touches[0];
  touchStartX.current = touch.pageX;
  startScrollLeft.current = filterTabsContainer.current.scrollLeft;
};

// 触摸移动:计算偏移并更新滚动
const handleTouchMove = (e: React.TouchEvent<HTMLDivElement>) => {
  if (!filterTabsContainer.current) return;
  const touch = e.touches[0];
  // 计算触摸移动的横向偏移量
  const deltaX = touch.pageX - touchStartX.current;
  // 和桌面端逻辑对齐:初始滚动位置减去偏移量
  filterTabsContainer.current.scrollLeft = startScrollLeft.current - deltaX;
};

// 触摸结束:重置状态(非必填,但能避免异常场景的状态残留)
const handleTouchEnd = () => {
  touchStartX.current = 0;
  startScrollLeft.current = 0;
};

// 绑定/解绑事件监听器
useEffect(() => {
  const container = filterTabsContainer.current;
  if (!container) return;

  // 桌面端拖拽逻辑
  const mouseMoveHandler = (e: MouseEvent) => {
    container.scrollLeft -= e.movementX;
  };

  container.addEventListener('mousemove', mouseMoveHandler);
  container.addEventListener('touchstart', handleTouchStart);
  container.addEventListener('touchmove', handleTouchMove);
  container.addEventListener('touchend', handleTouchEnd);

  return () => {
    container.removeEventListener('mousemove', mouseMoveHandler);
    container.removeEventListener('touchstart', handleTouchStart);
    container.removeEventListener('touchmove', handleTouchMove);
    container.removeEventListener('touchend', handleTouchEnd);
  };
}, []);

关键说明

  • 每次触摸开始时重新记录基准值,彻底解决二次拖拽时的状态混乱问题
  • 移动端逻辑和桌面端行为完全对齐,保证跨端体验一致
  • 使用useRef保存状态,避免组件重渲染导致的变量重置或错误覆盖
  • 组件卸载时清理事件监听器,防止内存泄漏

内容的提问来源于stack exchange,提问作者DogZoro28

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 23:53:11