将容器内的元素变为可拖拽

将容器内的元素变为可拖拽

概述

在容器内,我们经常需要将里面的元素变为可拖拽的。例如,你可能想要让用户能够通过拖动来重新排列列表中的项目或者移动图片等。

实现方法

新建拖拽实例

javascript 复制代码
// drag.js
// 将容器内的元素变为可拖拽
const DRAG_ITEM_MASK = "drag__item__mask",
  MOVING = "moving";
export class Drag {
  constructor(container) {
    this.sourceNode = null;
    this.dragItemIndex = null; // 拖拽时dom的索引,拖拽开始时确定值
    this.sourceIndex = null; // 拖拽过程中当前dom的索引,会随着位置改变而改变
    this.targetIndex = null; // 鼠标进入的目标dom的索引,会随着位置改变而改变
    this.container = container;
    this._init();
  }

  _init() {
    const _this = this;
    let children = Array.from(this.container.children);
    _this._setDraggable(children, true);
    this.container.ondragstart = this._ondragstart.bind(this);
    this.container.ondragenter = this._ondragenter.bind(this);
    this.container.ondragend = this._ondragend.bind(this);
  }
  // 拖动开始
  _ondragstart(e) {
    if (!e.target.draggable) return;
    let children = Array.from(this.container.children);
    this._createMask(children);
    setTimeout(() => {
      e.target.classList.add(MOVING);
      const mask = e.target.querySelector(`.${DRAG_ITEM_MASK}`);
      mask.style.background = "#fff";
      mask.style.border = "2px dashed #ff0000";
    }, 0);
    this.sourceNode = e.target;
    this.dragItemIndex = children.indexOf(e.target);
  }
  // 进入某一个可拖动项
  _ondragenter(e) {
    if (!this.sourceNode || e.target.className !== DRAG_ITEM_MASK) {
      return;
    }
    const container = this.container;
    let targetNode = e.target.parentElement;
    let sourceNode = this.sourceNode;
    if (targetNode === container || targetNode === sourceNode) {
      return;
    }
    let children = Array.from(container.children);
    this.sourceIndex = children.indexOf(sourceNode);
    this.targetIndex = children.indexOf(targetNode);

    if (this.targetIndex < 0) return;
    if (this.sourceIndex < this.targetIndex) {
      // 向下拖动
      container.insertBefore(sourceNode, targetNode.nextElementSibling);
    } else {
      // 向上拖动
      container.insertBefore(sourceNode, targetNode);
    }
  }
  // 拖动结束
  _ondragend(e) {
    if (!e.target.draggable) return;
    e.target.classList.remove(MOVING);
    let children = Array.from(this.container.children);
    this._removeMask(children);
    const targetIndex = children.indexOf(e.target);
    this.sourceNode = null;
    this.ondragend &&
      this.ondragend({
        sourceIndex: this.dragItemIndex,
        targetIndex: targetIndex,
      });
  }

  _setDraggable(elements, bool = true) {
    elements.forEach((element) => {
      if (!element.dataset.nodrag) {
        element.draggable = bool;
        element.style.cursor = bool ? "move" : "default";
      }
    });
  }

  _createMask(elements = []) {
    elements.forEach((el) => {
      if (!el.dataset.nodrag) {
        const mask = document.createElement("div");
        mask.className = DRAG_ITEM_MASK;
        mask.style.position = "absolute";
        mask.style.inset = 0;
        mask.style.zIndex = 9999;
        el.style.position = "relative";
        el.appendChild(mask);
      }
    });
  }

  _removeMask(elements = []) {
    elements.forEach((el) => {
      const mask = el.querySelector(`.${DRAG_ITEM_MASK}`);
      mask?.remove();
    });
  }

  distroy() {
    if (this.container) {
      let children = Array.from(this.container.children);
      this._setDraggable(children, null);
      this.container.ondragstart = null;
      this.container.ondragenter = null;
      this.container.ondragend = null;
      this.ondragend = null;
    }
  }
}

使用示例

在指令中使用

js 复制代码
const drag = {
  mounted(el, { value }, vnode) {
    // 指令绑定到元素时调用
    if (!!value === true) {
      const onDragChange = vnode.props["on:dragChange"] ?? (() => {});
      el.drag = new Drag(el);
      el.drag.ondragend = onDragChange;
    }
  },
  updated(el, { value }, vnode) {
    // 元素更新前调用
    if (!!value === false && el.drag) {
      el.drag?.distroy();
      el.drag = null;
    } else if (!!value === true) {
      const onDragChange = vnode.props["on:dragChange"] ?? (() => {});
      el.drag = new Drag(el);
      el.drag.ondragend = onDragChange;
    }
  },
  beforeUnmount(el) {
    // 元素解绑时调用
    el.drag?.distroy();
    el.drag = null;
  },
};

在组件中使用指令 v-drag

vue 复制代码
<div class="drag-box" v-drag="true"></div>
相关推荐
To_OC7 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
To_OC7 小时前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
我是坏垠9 小时前
Crypto、Cipher与Password:Java加密开发的三个核心概念
java·开发语言·python
white_ant9 小时前
JDK LTS 版本升级迁移注意事项大全
java·开发语言
Bobolink_9 小时前
跨境业务网络环境评估,“干净、一致、独享”三个关键指标
开发语言·网络·php·跨境网络·网络环境
你怎么知道我是队长9 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
xingke9 小时前
C 语言多文件编程:(一)头文件、extern、编译链接
c语言·开发语言·多文件
戮漠summer10 小时前
Missing Semester 计算机教育中缺失的一课 Lecture 01 Shell
开发语言·后端·scala
jinyishu_10 小时前
C++ string使用方法
开发语言·c++
EW Frontier10 小时前
三级跳突破864维动作空间——QMIX-Hierarchical多无人机协同通信方法全解析【附python代码】
开发语言·python·无人机·强化学习·通信资源分配