使用h5 drag api 实现拖动

本文介绍了一种使用HTML5拖放API实现列表项拖拽排序的方法。通过React实现了一个可拖拽排序的列表组件,展示了如何在拖动过程中更新列表项的位置。

实现原理

  1. 利用html5的拖放api进行目标元素拖动
  2. 在目标元素最终拖放结束时,获取到结束时需要放置元素位置的索引
  3. 通过目标元素索引以及放置元素索引更新数据
  4. 期间可以通过h5 的 drag api进行样式控制以及拖放索引获取

实现效果

拖拽时:
在这里插入图片描述
拖拽后:
在这里插入图片描述

代码

import "./styles.css";
import React, { useEffect, useState, useRef } from "react";

const defaultItem = [
  { title: "项目1234", key: "aa" },
  { title: "项目fawe", key: "bb" },
  { title: "项目00", key: "cc" },
  { title: "项目+++", key: "dd" },
  { title: "项目@#$%23", key: "ee" },
  { title: "项目+-*/345", key: "ff" },
  { title: "项目项目项项目", key: "gg" },
  { title: "asdf wr s ", key: "hh" }
];

export default function App(props) {
  // 获取显示的项目
  const indicator = (window.localStorage.getItem("user-indicator") &&
    JSON.parse(window.localStorage.getItem("user-indicator"))) || [
    ...defaultItem
  ];

  const [list, setList] = useState([...indicator]);

  const startRef = useRef(null);
  const dragDiv = useRef(null);

  // 初始化索引
  let oriPos = -1;

  // 初始化回调(如果需要)
  useEffect(() => {
    props && props.call && props.call();
  }, []);

  // 根据前后变化的索引进行数据更新
  const changePosition = (dragIndex, hoverIndex) => {
    // 根据市前排还是后排进行数据重组
    let dataFinal = [];
    if (dragIndex < hoverIndex) {
      // 向后排序
      let dataArr1 = [...list].splice(0, dragIndex);
      let dataArr2 = [...list].splice(dragIndex + 1, hoverIndex - dragIndex);
      let dataArr3 = [list[dragIndex]];
      let dataArr4 = [...list].splice(hoverIndex + 1);

      if (hoverIndex - dragIndex === 1) {
        // 边界处理
        dataArr2 = [...list].splice(hoverIndex + 1, 1);
      }

      dataFinal = dataFinal.concat(dataArr1, dataArr2, dataArr3, dataArr4);
    } else {
      // 向前排序
      let dataArr1 = [...list].splice(0, hoverIndex);
      let dataArr2 = [list[dragIndex]];
      let dataArr3 = [...list].splice(hoverIndex, dragIndex - hoverIndex);
      let dataArr4 = [...list].splice(dragIndex + 1);

      if (dragIndex - hoverIndex === 1) {
        dataArr3 = [...list].splice(hoverIndex, 1);
      }

      dataFinal = dataFinal.concat(dataArr1, dataArr2, dataArr3, dataArr4);
    }
    setList(dataFinal);
  };

  // 开始拖动时
  const onDragStart = (e, index) => {
    startRef.current = index;
    oriPos = index;
    e.target.classList.add("drag");
  };

  // 结束拖动时
  const onDragEnd = (e, index) => {
    e.target.classList.remove("drag");
    dragDiv.current.getElementsByClassName("over-down").length > 0 &&
      dragDiv.current
        .getElementsByClassName("over-down")[0]
        .classList.remove("over-down");
    dragDiv.current.getElementsByClassName("over-up").length > 0 &&
      dragDiv.current
        .getElementsByClassName("over-up")[0]
        .classList.remove("over-up");

    if (startRef.current === index) {
      return;
    }
    // 进行数据更新
    changePosition(index, startRef.current);
  };

  // 进入元素时
  const onDragEnter = (e, hoverIndex) => {
    // 排除移动的元素
    if (startRef.current === hoverIndex) {
      return;
    }

    if (oriPos < hoverIndex) {
      // 向下排序
      e.target.classList.add("over-down");
    } else if (oriPos > hoverIndex) {
      // 向上排序
      e.target.classList.add("over-up");
    }

    // 将当前移动元素的index赋值给当前拖动的元素,避免出现两个元素抖动
    startRef.current = hoverIndex;
  };

  // 移出元素时
  const onDragLeave = (e, hoverIndex) => {
    e.target.classList.remove("over");
    e.target.classList.remove("over-up");
    e.target.classList.remove("over-down");
  };

  // 当正在拖动时
  const onDragOver = (e) => {
    e.dataTransfer.dropEffect = "link";
    e.preventDefault();
  };

  return (
    <div className="App">
      <div className="indicator-list" ref={dragDiv}>
        {list.map((item, index) => {
          return (
            <div
              className="ac"
              draggable
              key={item.key}
              onDragStart={($event) => onDragStart($event, index)}
              onDragEnd={($event) => onDragEnd($event, index)}
              onDragEnter={($event) => onDragEnter($event, index)}
              onDragLeave={($event) => onDragLeave($event, index)}
              onDragOver={($event) => onDragOver($event)}
            >
              {item.title}
            </div>
          );
        })}
      </div>
    </div>
  );
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值