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

Vue3移动端拖拽异常:求无需SortableJS的可行解决方案

解决方案

一、修复原生mousedown/touchstart方案的光标问题

你的方案核心功能正常,光标显示禁止的原因主要是原生draggable属性干扰、未正确处理事件默认行为,以及触摸场景下的样式优先级问题,按以下步骤修复:

  1. 移除原生draggable属性
    既然用自定义触摸/鼠标事件实现拖拽,原生:draggable="true"会和自定义逻辑冲突,直接删除该属性。

  2. 通过全局类控制光标样式
    拖拽开始时给body或目标元素添加自定义类,强制设置光标样式,结束时移除:

    <script setup>
    import { ref } from 'vue'
    const isDragging = ref(false)
    
    function startDrag(event, r, c) {
      isDragging.value = true
      // 阻止触摸默认行为(如页面滚动)
      event.preventDefault()
      // 记录拖拽起始位置等原有逻辑...
    }
    
    function endDrag(event) {
      isDragging.value = false
      // 完成拖拽后的原有逻辑...
    }
    </script>
    
    <style>
    .dragging {
      cursor: move !important;
      /* 移动端触摸场景下强制禁用默认触摸行为 */
      touch-action: none;
    }
    /* 拖拽元素默认光标样式 */
    .canDragIt {
      cursor: move;
    }
    </style>
    
    <template>
      <v-img 
        class="canDragIt"
        :class="{ dragging: isDragging }"
        @mousedown="startDrag($event,r,c)"
        @mousemove="mouseDrag($event,r,c)"
        @mouseup="endDrag($event)"
        @mouseleave="endDrag($event)"
        @touchstart="startDrag($event,r,c)"
        @touchmove="mouseDrag($event,r,c)"
        @touchend="endDrag($event)"
        @dblclick="addNoTrack(r,c)"
        :src="cell.content"
      >
      </v-img>
    </template>
    
  3. 处理dragover默认行为(若保留原生drop逻辑)
    若仍使用原生drop事件,必须在allowDrop函数中阻止默认行为,否则浏览器会判定为不可放置,显示禁止光标:

    function allowDrop(event) {
      event.preventDefault()
      // 可选:明确设置拖拽效果
      if (event.dataTransfer) {
        event.dataTransfer.effectAllowed = 'move'
      }
    }
    

二、无需依赖SortableJS的Vue3移动端拖拽成熟方案

推荐使用VueUse的useDraggable,这是Vue生态中轻量、维护活跃的工具函数,原生支持鼠标和触摸事件,可完全自定义拖拽逻辑,适配你的网格元素交换需求:

  1. 安装VueUse

    npm i @vueuse/core
    
  2. 核心实现示例
    针对n×n网格场景,通过useDraggable监听拖拽状态,结合网格坐标实现元素交换:

    <script setup>
    import { ref } from 'vue'
    import { useDraggable } from '@vueuse/core'
    
    // 假设你的网格数据为二维数组
    const grid = ref([
      [{ id: 1, content: 'img1.jpg' }, { id: 2, content: 'img2.jpg' }],
      [{ id: 3, content: 'img3.jpg' }, { id: 4, content: 'img4.jpg' }]
    ])
    const draggedCell = ref(null) // 记录正在拖拽的单元格信息
    const cellSize = ref(100) // 单元格宽高,根据布局调整
    
    // 为每个网格元素初始化拖拽逻辑
    function initDrag(el, r, c) {
      const { isDragging, style } = useDraggable(el, {
        onStart() {
          draggedCell.value = { r, c, cell: grid.value[r][c] }
          style.value.opacity = '0.5'
        },
        onEnd(event) {
          style.value.opacity = '1'
          // 计算拖拽结束时对应的网格坐标
          const targetR = Math.floor(event.clientY / cellSize.value)
          const targetC = Math.floor(event.clientX / cellSize.value)
          // 校验目标坐标合法性并交换元素
          if (draggedCell.value && targetR >= 0 && targetR < grid.value.length && targetC >= 0 && targetC < grid.value[0].length) {
            const temp = grid.value[targetR][targetC]
            grid.value[targetR][targetC] = draggedCell.value.cell
            grid.value[draggedCell.value.r][draggedCell.value.c] = temp
          }
          draggedCell.value = null
        }
      })
    }
    </script>
    
    <template>
      <div class="grid-container">
        <div 
          v-for="(row, r) in grid" 
          :key="r" 
          class="grid-row"
        >
          <v-img
            v-for="(cell, c) in row"
            :key="cell.id"
            class="canDragIt"
            :ref="el => el && initDrag(el, r, c)"
            @dblclick="addNoTrack(r,c)"
            :src="cell.content"
          />
        </div>
      </div>
    </template>
    
    <style>
    .grid-container {
      display: grid;
      grid-template-columns: repeat(auto-fill, 100px);
    }
    .canDragIt {
      width: 100px;
      height: 100px;
      cursor: move;
      touch-action: none; /* 禁用触摸默认行为 */
    }
    </style>
    

该方案优势:

  • 原生适配触摸与鼠标事件,移动端体验流畅
  • 完全自定义拖拽逻辑,不会强制重排元素,精准匹配你的元素交换需求
  • 维护活跃,兼容最新Vue3及浏览器版本

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 00:35:20