vue实现全局 PC 端触摸屏滑动滚动实现

本文实现了一个PC端触摸屏滑动滚动的全局解决方案,通过监听touch和mouse事件实现流畅的滚动效果。主要功能包括:

  1. 检测可滚动容器;
  2. 处理单指触摸滑动;
  3. 支持水平和垂直方向滚动;
  4. 防止文本选择干扰;
  5. 兼容鼠标拖动。代码通过状态管理跟踪触摸位置、滚动元素和拖动状态,并优化了事件处理(移除旧监听、阻止默认行为)。当检测到有效滑动(>5px)时,会动态调整滚动位置并禁用文本选择,确保流畅的滚动体验。

- 根据业务需求实现PC端游览器在触摸屏幕设备滑动滚动(全局)

// 全局 PC 端触摸屏滑动滚动实现
export function initTouchScroll() {
  // 存储触摸状态
  let touchState = {
    startX: 0,
    startY: 0,
    scrollEl: null as HTMLElement | null,
    isScrolling: false,
    isDragging: false,
    startTime: 0
  };

  // 移除可能存在的旧事件监听,避免重复绑定
  document.removeEventListener('touchstart', handleTouchStart);
  document.removeEventListener('touchmove', handleTouchMove);
  document.removeEventListener('touchend', handleTouchEnd);
  document.removeEventListener('touchcancel', handleTouchEnd);

  // 添加新的事件监听
  document.addEventListener('touchstart', handleTouchStart, { passive: false });
  document.addEventListener('touchmove', handleTouchMove, { passive: false });
  document.addEventListener('touchend', handleTouchEnd);
  document.addEventListener('touchcancel', handleTouchEnd);

  // 鼠标拖动也需要处理的话可以添加鼠标事件
  document.removeEventListener('mousedown', handleMouseDown);
  document.removeEventListener('mousemove', handleMouseMove);
  document.removeEventListener('mouseup', handleMouseUp);
  document.addEventListener('mousedown', handleMouseDown);
  document.addEventListener('mousemove', handleMouseMove);
  document.addEventListener('mouseup', handleMouseUp);

  function handleTouchStart(e: TouchEvent) {
    if (e.touches.length !== 1) return;
    
    const touch = e.touches[0];
    const scrollEl = findScrollContainer(touch.target as HTMLElement);
    
    touchState = {
      startX: touch.clientX,
      startY: touch.clientY,
      scrollEl: scrollEl,
      isScrolling: !!scrollEl,
      isDragging: false,
      startTime: Date.now()
    };

    // 提前阻止可能的选择行为
    if (scrollEl) {
      e.preventDefault();
    }
  }

  function handleTouchMove(e: TouchEvent) {
    if (!touchState.isScrolling || !touchState.scrollEl) return;
    if (e.touches.length !== 1) return;

    const touch = e.touches[0];
    const scrollEl = touchState.scrollEl;

    // 计算移动距离
    const deltaX = touchState.startX - touch.clientX;
    const deltaY = touchState.startY - touch.clientY;
    
    // 判断是否是有效滑动(超过5px视为滑动)
    const moveDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
    if (moveDistance > 5) {
      touchState.isDragging = true;
      
      // 禁用文本选择
      document.body.style.userSelect = 'none';
      document.body.style.webkitUserSelect = 'none';
      
      // 处理纵向滚动
      if (scrollEl.scrollHeight > scrollEl.clientHeight) {
        scrollEl.scrollTop += deltaY;
        scrollEl.scrollTop = Math.max(0, Math.min(scrollEl.scrollTop, scrollEl.scrollHeight - scrollEl.clientHeight));
      }

      // 处理横向滚动
      if (scrollEl.scrollWidth > scrollEl.clientWidth) {
        scrollEl.scrollLeft += deltaX;
        scrollEl.scrollLeft = Math.max(0, Math.min(scrollEl.scrollLeft, scrollEl.scrollWidth - scrollEl.clientWidth));
      }

      // 更新起点位置
      touchState.startX = touch.clientX;
      touchState.startY = touch.clientY;

      // 阻止默认行为
      e.preventDefault();
      e.stopPropagation();
    }
  }

  function handleTouchEnd() {
    // 恢复文本选择
    document.body.style.userSelect = '';
    document.body.style.webkitUserSelect = '';
    
    touchState.isScrolling = false;
    touchState.isDragging = false;
    touchState.scrollEl = null;
  }

  // 处理鼠标拖动(可选,解决鼠标拖动选中文本问题)
  function handleMouseDown(e: MouseEvent) {
    const scrollEl = findScrollContainer(e.target as HTMLElement);
    if (!scrollEl) return;

    touchState = {
      startX: e.clientX,
      startY: e.clientY,
      scrollEl: scrollEl,
      isScrolling: true,
      isDragging: false,
      startTime: Date.now()
    };
  }

  function handleMouseMove(e: MouseEvent) {
    if (!touchState.isScrolling || !touchState.scrollEl) return;

    const deltaX = touchState.startX - e.clientX;
    const deltaY = touchState.startY - e.clientY;
    const moveDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

    if (moveDistance > 5) {
      touchState.isDragging = true;
      const scrollEl = touchState.scrollEl;

      // 禁用文本选择
      document.body.style.userSelect = 'none';
      
      // 处理滚动
      if (scrollEl.scrollHeight > scrollEl.clientHeight) {
        scrollEl.scrollTop += deltaY;
        scrollEl.scrollTop = Math.max(0, Math.min(scrollEl.scrollTop, scrollEl.scrollHeight - scrollEl.clientHeight));
      }

      if (scrollEl.scrollWidth > scrollEl.clientWidth) {
        scrollEl.scrollLeft += deltaX;
        scrollEl.scrollLeft = Math.max(0, Math.min(scrollEl.scrollLeft, scrollEl.scrollWidth - scrollEl.clientWidth));
      }

      touchState.startX = e.clientX;
      touchState.startY = e.clientY;

      e.preventDefault();
      e.stopPropagation();
    }
  }

  function handleMouseUp() {
    document.body.style.userSelect = '';
    touchState.isScrolling = false;
    touchState.isDragging = false;
    touchState.scrollEl = null;
  }

  // 查找可滚动容器
  function findScrollContainer(el: HTMLElement | null): HTMLElement | null {
    while (el && el !== document.body && el.nodeType === 1) {
      const style = window.getComputedStyle(el);
      const overflowY = style.overflowY;
      const overflowX = style.overflowX;
      
      const canScrollY = (overflowY === 'auto' || overflowY === 'scroll') && 
                         el.scrollHeight > el.clientHeight;
                         
      const canScrollX = (overflowX === 'auto' || overflowX === 'scroll') && 
                         el.scrollWidth > el.clientWidth;

      if (canScrollY || canScrollX) {
        return el;
      }
      
      el = el.parentElement;
    }

    // 检查body是否可滚动
    if (document.body.scrollHeight > window.innerHeight) {
      return document.body;
    }

    return null;
  }

  // 调试信息,确认函数已执行
  console.log('Touch scroll initialized successfully');
  }

main.ts引入此函数即可,无需更改,粘贴可用

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值