掌握HTML5拖拽:提高交互体验⚡

520 阅读2分钟

通过10分钟时间学习HTML5中的拖拽功能,让用户交互体验更加流畅。

今天给大家带来HTML5中新增的属性来实现拖拽功能,先来看看实现后的效果,如下:

拖拽功能
在该例子中,可以将任意一个元素进行拖拽到另一个元素上进行替换。

前言

HTML5新增了一个可拖拽的属性,即draggable,属性draggable 设置为 "true",这个元素变成可拖拽的,并且该属性是可以设置在任意元素上,包括图像和链接,在拖拽时为该元素增加dragstart监听事件,在结束拖拽时添加ondragend监听事件。

基础结构

HTML部分

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HTML5拖拽实现</title>
    <link rel="stylesheet" href="./index.css">
</head>
<body>
    <div class="list">
        <div class="list-item" draggable="true" style="--color: #b01a01">
            <img src="https://picsum.photos/id/237/200" alt="">
            <span>1</span>
        </div>
        <div class="list-item" draggable="true" style="--color: #70d265">
            <img src="https://picsum.photos/seed/picsum/200" alt="">
            <span>2</span>
        </div>
        <div class="list-item" draggable="true" style="--color: #f0e941">
            <img src="https://picsum.photos/200" alt="">
            <span>3</span>
        </div>
        <div class="list-item" draggable="true" style="--color: #da8218">
            <img src="https://picsum.photos/200?grayscale" alt="">
            <span>4</span>
        </div>
        <div class="list-item" draggable="true" style="--color: #f1e867">
            <img src="https://picsum.photos/200?blur=2" alt="">
            <span>5</span>
        </div>
    </div>

    <script src="./index.js"></script>
</body>
</html>

CSS部分

* {
    margin0;
    padding0;
}
body {
    height100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color#333;
}
.list-item {
    width400px;
    height70px;
    background-colorvar(--color);
    border-radius8px;
    margin15px 0;
    padding-left20px;
    display: flex;
    align-items: center;
    /* 光标变移动图标 */
    cursor: move;
}
.list-item img {
    width50px;
    height50px;
    object-fit: cover;
    object-position: top;
    margin-right20px;
}
.list-item span {
    color#fff;
    font-size22px;
    letter-spacing2px;
    text-shadow1px 1px 2px rgba(000, .5);
}
/* 接下来添加拖拽时的样式 */
.list-item.moving{
    background-color: transparent;
    border2px dashed #ccc;
}
.list-item.moving img,
.list-item.moving span {
    display: none;
}

定义了HTML和CSS样式后,网页效果图如下。

核心部分

核心代码如下:

const list = document.querySelector(".list");
const item = document.querySelectorAll(".list-item");

// 判断房前拖动的是哪个元素
let sourceNode;
// 开始拖动的事件
list.ondragstart = (e) => {
  sourceNode = e.target;
  // 记录起始位置
  record(item);
  // 添加moving样式
  setTimeout(() => {
    e.target.classList.add("moving");
  }, 0);
  // 设置被拖动元素允许移动到新的位置
  e.dataTransfer.effectAllowed = "move";
};

// 在元素正在拖动到放置目标时触发
list.ondragover = (e) => {
  e.preventDefault();
};

// 元素在拖拽过程中
list.ondragenter = (e) => {
  e.preventDefault();
  if (e.target === list || e.target === sourceNode) {
    return false;
  }
  const children = [...list.children];
  const sourceIndex = children.indexOf(sourceNode);
  const targetIndex = children.indexOf(e.target);
  if (sourceIndex < targetIndex) {
    // insertBefore(要插入的节点,在谁前面)
    // 下方
    list.insertBefore(sourceNode, e.target.nextElementSibling);
    record([sourceNode]);
  } else {
    // 上方
    list.insertBefore(sourceNode, e.target);
    record([sourceNode]);
  }
  last([e.target, sourceNode]);
};

// 拖动结束时取消虚线
list.ondragend = (e) => {
  e.target.classList.remove("moving");
};

// 记录起始位置
function record(eleAll) {
  for (let i = 0; i < eleAll.length; i++) {
    const { top, left } = eleAll[i].getBoundingClientRect();
    eleAll[i]._top = top;
    eleAll[i]._left = left;
  }
}

// 记录最后的位置,并执行动画
function last(eleAll) {
  for (let i = 0; i < eleAll.length; i++) {
    const dom = eleAll[i];
    const { top, left } = dom.getBoundingClientRect();
    if (dom._left) {
      dom.style.transform = `translate3d(${dom._left - left}px, ${
        dom._top - top
      }px, 0px)`;
      let rafid = requestAnimationFrame(function () {
        dom.style.transition = "transform 0.3s ease-out";
        dom.style.transform = "none";
      });
      dom.addEventListener("transitionend"() => {
        dom.style.transition = "none";
        cancelAnimationFrame(rafid);
      });
    }
  }
}

总结

通过上述例子,想要实现拖拽需要做的以下几点:

  • 在节点声明能够拖拽,设置draggable属性为true
  • 监听ondragstart事件,用于记录拖拽前的起始位置
  • 监听ondragenter事件,用于记录拖拽过程中的位置变化
  • 监听ondragend事件,用于记录拖拽后的终点位置