一款好用到爆的可视化拖拽库

嗨,大家好,我是徐小夕,之前一直在研究可视化零代码相关的技术实践,也做了很多可视化搭建的产品,比如:

  • H5-Dooring(页面可视化搭建平台)
  • V6.Dooring(数据大屏可视化平台)
  • formManager(表单搭建引擎)
  • Next-Admin(基于nextjs和antd5.0的中后台管理系统)

最近在研发智能搭建系统(WEP)的时候发现一款非常好用的可视化拖拽插件------draggable 。它在 github 上有17.4k star,提供了很多非常精美的拖拽案例, 我们使用它可以轻松实现可视化拖拽,组件排序,网格拖拽等效果,而且浏览器兼容性也非常不错,原生 javascript 开发, 可以轻松集成到 reactvue 等主流框架中。

接下来我就和大家一起介绍一下这款开源插件。

安装与使用

我们可以使用如下方式安装:

bash 复制代码
# yarn add shopify/draggable
pnpm add shopify/draggable

在项目里使用:

js 复制代码
import {
    Draggable,
    Sortable,
    Droppable,
    Swappable,
  } from 'shopify/draggable'

github地址: https://github.com/Shopify/draggable

接下来我就来和大家分享几个非常有价值的使用案例。

1. 3D效果拖拽

代码实现:

js 复制代码
// eslint-disable-next-line import/no-unresolved
import {Draggable} from '@shopify/draggable';

// eslint-disable-next-line shopify/strict-component-boundaries
import Plate from '../../components/Plate';

export default function Home() {
  const containerSelector = '#Home .PlateWrapper';
  const container = document.querySelector(containerSelector);

  if (!container) {
    return false;
  }

  const draggable = new Draggable(container, {
    draggable: '.Plate',
  });
  const plates = new Plate(container);

  // --- Draggable events --- //
  draggable.on('drag:start', (evt) => {
    plates.setThreshold();
    plates.setInitialMousePosition(evt.sensorEvent);
  });

  draggable.on('drag:move', (evt) => {
    // rAF seems to cause the animation to get stuck?
    // requestAnimationFrame(() => {});
    plates.dragWarp(evt.source, evt.sensorEvent);
  });

  draggable.on('drag:stop', () => {
    plates.resetWarp();
  });

  return draggable;
}

2. 可拖拽的开关效果

代码如下:

js 复制代码
// eslint-disable-next-line import/no-unresolved
import {Draggable} from '@shopify/draggable';

function translateMirror(mirror, mirrorCoords, containerRect) {
  if (mirrorCoords.top < containerRect.top || mirrorCoords.left < containerRect.left) {
    return;
  }

  requestAnimationFrame(() => {
    mirror.style.transform = `translate3d(${mirrorCoords.left}px, ${mirrorCoords.top}px, 0)`;
  });
}

function calcOffset(offset) {
  return offset * 2 * 0.5;
}

export default function DragEvents() {
  const toggleClass = 'PillSwitch--isOn';
  const containers = document.querySelectorAll('#DragEvents .PillSwitch');

  if (containers.length === 0) {
    return false;
  }

  const draggable = new Draggable(containers, {
    draggable: '.PillSwitchControl',
    delay: 0,
  });

  let isToggled = false;
  let initialMousePosition;
  let containerRect;
  let dragRect;
  let dragThreshold;
  let headings;
  let headingText;

  // --- Draggable events --- //
  draggable.on('drag:start', (evt) => {
    initialMousePosition = {
      x: evt.sensorEvent.clientX,
      y: evt.sensorEvent.clientY,
    };
  });

  draggable.on('mirror:created', (evt) => {
    containerRect = evt.sourceContainer.getBoundingClientRect();
    dragRect = evt.source.getBoundingClientRect();

    const containerRectQuarter = containerRect.width / 4;
    dragThreshold = isToggled ? containerRectQuarter * -1 : containerRectQuarter;
    headings = {
      source: evt.originalSource.querySelector('[data-switch-on]'),
      mirror: evt.mirror.querySelector('[data-switch-on]'),
    };
    headingText = {
      on: headings.source.dataset.switchOn,
      off: headings.source.dataset.switchOff,
    };
  });

  draggable.on('mirror:move', (evt) => {
    evt.cancel();
    const offsetX = calcOffset(evt.sensorEvent.clientX - initialMousePosition.x);
    const offsetY = calcOffset(initialMousePosition.y - evt.sensorEvent.clientY);
    const offsetValue = offsetX > offsetY ? offsetX : offsetY;
    const mirrorCoords = {
      top: dragRect.top - offsetValue,
      left: dragRect.left + offsetValue,
    };

    translateMirror(evt.mirror, mirrorCoords, containerRect);

    if (isToggled && offsetValue < dragThreshold) {
      evt.sourceContainer.classList.remove(toggleClass);
      headings.source.textContent = headingText.off;
      headings.mirror.textContent = headingText.off;
      isToggled = false;
    } else if (!isToggled && offsetValue > dragThreshold) {
      evt.sourceContainer.classList.add(toggleClass);
      headings.source.textContent = headingText.on;
      headings.mirror.textContent = headingText.on;
      isToggled = true;
    }
  });

  const triggerMouseUpOnESC = (evt) => {
    if (evt.key === 'Escape') {
      draggable.cancel();
    }
  };

  draggable.on('drag:start', () => {
    document.addEventListener('keyup', triggerMouseUpOnESC);
  });

  return draggable;
}

3.可拖拽的网格元素

源码地址: https://github.com/Shopify/draggable/tree/master/examples/src/content/Droppable/UniqueDropzone

4. 可拖拽的列表

源码地址: https://github.com/Shopify/draggable/tree/master/examples/src/content/Sortable/SimpleList

5. 卡牌拖拽效果

源码地址: https://github.com/Shopify/draggable/tree/master/examples/src/content/Sortable/Transformed

6. 多容器拖拽效果

源码地址: https://github.com/Shopify/draggable/tree/master/examples/src/content/Sortable/MultipleContainers

7. 不规则网格拖拽

源码地址:https://github.com/Shopify/draggable/tree/master/examples/src/content/Swappable/Floated

8. 拖拽排序动画

源码地址: https://github.com/Shopify/draggable/tree/master/examples/src/content/Plugins/SortAnimation

当然还有很多有意思的拖拽案例, 大家也可以去体验一下。

今天就分享到这啦,祝大家节日快乐, 博学!

如果有收获,记得点赞 + 再看哦, 欢迎在评论区评论, 分享你的收藏干货~

更多推荐

可视化表单&试卷搭建平台技术详解

爆肝1000小时, Dooring零代码搭建平台3.5正式上线

相关推荐
码上成长3 分钟前
包管理提速:pnpm + Workspace + Changesets 搭建版本体系
前端·前端框架
Bigger6 分钟前
Tauri(十九)——实现 macOS 划词监控的完整实践
前端·rust·app
ganshenml1 小时前
【Web】证书(SSL/TLS)与域名之间的关系:完整、通俗、可落地的讲解
前端·网络协议·ssl
用户41429296072391 小时前
淘宝实时商品API接口:采集竞品商品详情页的价格、SKU 规格、库存数量、卖点文案、图文内容、售后政策(运费、退换货规则)、评价核心标签
数据挖掘·数据分析·数据可视化
这是个栗子2 小时前
npm报错 : 无法加载文件 npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
HIT_Weston2 小时前
44、【Ubuntu】【Gitlab】拉出内网 Web 服务:http.server 分析(一)
前端·ubuntu·gitlab
华仔啊3 小时前
Vue3 如何实现图片懒加载?其实一个 Intersection Observer 就搞定了
前端·vue.js
JamesGosling6663 小时前
深入理解内容安全策略(CSP):原理、作用与实践指南
前端·浏览器
不要想太多3 小时前
前端进阶系列之《浏览器渲染原理》
前端
g***96903 小时前
Node.js npm 安装过程中 EBUSY 错误的分析与解决方案
前端·npm·node.js