可视化拖拽组件库一些技术要点原理分析(一)

简介: 可视化拖拽组件库一些技术要点原理分析(一)

本文主要对以下技术要点进行分析:

  1. 编辑器
  2. 自定义组件
  3. 拖拽
  4. 删除组件、调整图层层级
  5. 放大缩小
  6. 撤消、重做
  7. 组件属性设置
  8. 吸附
  9. 预览、保存代码
  10. 绑定事件
  11. 绑定动画
  12. 导入 PSD
  13. 手机模式

为了让本文更加容易理解,我将以上技术要点结合在一起写了一个可视化拖拽组件库 DEMO:

建议结合源码一起阅读,效果更好(这个 DEMO 使用的是 Vue 技术栈)。

1. 编辑器

先来看一下页面的整体结构。

这一节要讲的编辑器其实就是中间的画布。它的作用是:当从左边组件列表拖拽出一个组件放到画布中时,画布要把这个组件渲染出来。

这个编辑器的实现思路是:

  1. 用一个数组 componentData 维护编辑器中的数据。
  2. 把组件拖拽到画布中时,使用 push() 方法将新的组件数据添加到 componentData
  3. 编辑器使用 v-for 指令遍历 componentData,将每个组件逐个渲染到画布(也可以使用 JSX 语法结合 render() 方法代替)。

编辑器渲染的核心代码如下所示:

<component 
  v-for="item in componentData"
  :key="item.id"
  :is="item.component"
  :style="item.style"
  :propValue="item.propValue"
/>

每个组件数据大概是这样:

{
    component: 'v-text', // 组件名称,需要提前注册到 Vue
    label: '文字', // 左侧组件列表中显示的名字
    propValue: '文字', // 组件所使用的值
    icon: 'el-icon-edit', // 左侧组件列表中显示的名字
    animations: [], // 动画列表
    events: {}, // 事件列表
    style: { // 组件样式
        width: 200,
        height: 33,
        fontSize: 14,
        fontWeight: 500,
        lineHeight: '',
        letterSpacing: 0,
        textAlign: '',
        color: '',
    },
}

在遍历 componentData 组件数据时,主要靠 is 属性来识别出真正要渲染的是哪个组件。

例如要渲染的组件数据是 { component: 'v-text' },则 会被转换为 。当然,你这个组件也要提前注册到 Vue 中。

如果你想了解更多 is 属性的资料,请查看官方文档

2. 自定义组件

原则上使用第三方组件也是可以的,但建议你最好封装一下。不管是第三方组件还是自定义组件,每个组件所需的属性可能都不一样,所以每个组件数据可以暴露出一个属性 propValue 用于传递值。

例如 a 组件只需要一个属性,你的 propValue 可以这样写:propValue: 'aaa'。如果需要多个属性,propValue 则可以是一个对象:

propValue: {
  a: 1,
  b: 'text'
}

在这个 DEMO 组件库中我定义了三个组件。

图片组件 Picture

<template>
    <div style="overflow: hidden">
        <img :src="propValue">
    </div>
</template>
<script>
export default {
    props: {
        propValue: {
            type: String,
            require: true,
        },
    },
}
</script>

按钮组件 VButton:

<template>
    <button class="v-button">{{ propValue }}</button>
</template>
<script>
export default {
    props: {
        propValue: {
            type: String,
            default: '',
        },
    },
}
</script>

文本组件 VText:

<template>
    <textarea 
        v-if="editMode == 'edit'"
        :value="propValue"
        class="text textarea"
        @input="handleInput"
        ref="v-text"
    ></textarea>
    <div v-else class="text disabled">
        <div v-for="(text, index) in propValue.split('\n')" :key="index">{{ text }}</div>
    </div>
</template>
<script>
import { mapState } from 'vuex'
export default {
    props: {
        propValue: {
            type: String,
        },
        element: {
            type: Object,
        },
    },
    computed: mapState([
        'editMode',
    ]),
    methods: {
        handleInput(e) {
            this.$emit('input', this.element, e.target.value)
        },
    },
}
</script>

3. 拖拽

从组件列表到画布

一个元素如果要设为可拖拽,必须给它添加一个 draggable 属性。另外,在将组件列表中的组件拖拽到画布中,还有两个事件是起到关键作用的:

  1. dragstart 事件,在拖拽刚开始时触发。它主要用于将拖拽的组件信息传递给画布。
  2. drop 事件,在拖拽结束时触发。主要用于接收拖拽的组件信息。

先来看一下左侧组件列表的代码:

<div @dragstart="handleDragStart" class="component-list">
    <div v-for="(item, index) in componentList" :key="index" class="list" draggable :data-index="index">
        <i :class="item.icon"></i>
        <span>{{ item.label }}</span>
    </div>
</div>
handleDragStart(e) {
    e.dataTransfer.setData('index', e.target.dataset.index)
}

可以看到给列表中的每一个组件都设置了 draggable 属性。另外,在触发 dragstart 事件时,使用 dataTransfer.setData() 传输数据。再来看一下接收数据的代码:

<div class="content" @drop="handleDrop" @dragover="handleDragOver" @click="deselectCurComponent">
    <Editor />
</div>
handleDrop(e) {
    e.preventDefault()
    e.stopPropagation()
    const component = deepCopy(componentList[e.dataTransfer.getData('index')])
    this.$store.commit('addComponent', component)
}

触发 drop 事件时,使用 dataTransfer.getData() 接收传输过来的索引数据,然后根据索引找到对应的组件数据,再添加到画布,从而渲染组件。

组件在画布中移动

首先需要将画布设为相对定位 position: relative,然后将每个组件设为绝对定位 position: absolute。除了这一点外,还要通过监听三个事件来进行移动:

  1. mousedown 事件,在组件上按下鼠标时,记录组件当前的位置,即 xy 坐标(为了方便讲解,这里使用的坐标轴,实际上 xy 对应的是 css 中的 lefttop
  2. mousemove 事件,每次鼠标移动时,都用当前最新的 xy 坐标减去最开始的 xy 坐标,从而计算出移动距离,再改变组件位置。
  3. mouseup 事件,鼠标抬起时结束移动。
handleMouseDown(e) {
    e.stopPropagation()
    this.$store.commit('setCurComponent', { component: this.element, zIndex: this.zIndex })
    const pos = { ...this.defaultStyle }
    const startY = e.clientY
    const startX = e.clientX
    // 如果直接修改属性,值的类型会变为字符串,所以要转为数值型
    const startTop = Number(pos.top)
    const startLeft = Number(pos.left)
    const move = (moveEvent) => {
        const currX = moveEvent.clientX
        const currY = moveEvent.clientY
        pos.top = currY - startY + startTop
        pos.left = currX - startX + startLeft
        // 修改当前组件样式
        this.$store.commit('setShapeStyle', pos)
    }
    const up = () => {
        document.removeEventListener('mousemove', move)
        document.removeEventListener('mouseup', up)
    }
    document.addEventListener('mousemove', move)
    document.addEventListener('mouseup', up)
}

PS: 有很多网友反馈拖拽的时候有卡顿现象,其实解决方案很简单,把浏览器的控制台关掉即可。

4. 删除组件、调整图层层级

改变图层层级

由于拖拽组件到画布中是有先后顺序的,所以可以按照数据顺序来分配图层层级。

例如画布新增了五个组件 abcde,那它们在画布数据中的顺序为 [a, b, c, d, e],图层层级和索引一一对应,即它们的 z-index 属性值是 01234(后来居上)。用代码表示如下:

<div v-for="(item, index) in componentData" :zIndex="index">div>

如果不了解 z-index 属性的,请看一下 MDN 文档

理解了这一点之后,改变图层层级就很容易做到了。改变图层层级,即是改变组件数据在 componentData 数组中的顺序。例如有 [a, b, c] 三个组件,它们的图层层级从低到高顺序为 abc(索引越大,层级越高)。

如果要将 b 组件上移,只需将它和 c 调换顺序即可:

const temp = componentData[1]
componentData[1] = componentData[2]
componentData[2] = temp

同理,置顶置底也是一样,例如我要将 a 组件置顶,只需将 a 和最后一个组件调换顺序即可:

const temp = componentData[0]
componentData[0] = componentData[componentData.lenght - 1]
componentData[componentData.lenght - 1] = temp

删除组件

删除组件非常简单,一行代码搞定:componentData.splice(index, 1)

目录
相关文章
|
前端开发 算法 数据可视化
可拖拽流程图组件开发
可拖拽流程图组件开发
516 0
|
API Windows
NSIS使用教程(安装包制作安装文件教程,如何封装打包文件) 中文版
原文:NSIS使用教程(安装包制作安装文件教程,如何封装打包文件) 中文版 nsis中文版(Nullsoft Scriptable Install System)是一个专业的开源的可以用来封闭Windows程序的实用工具,是一个开源的 Windows 系统下安装程序制作程序。
5094 0
|
数据可视化 索引
可视化拖拽组件库一些技术要点原理分析(二)
可视化拖拽组件库一些技术要点原理分析(二)
545 0
|
7月前
|
JavaScript 前端开发 API
Vue.js 快速上手指南
Vue.js 是轻量级渐进式前端框架,核心聚焦视图层,上手简单、生态丰富。本文提供可直接复制的快速入门指南,涵盖 Vue3 组合式 API、响应式数据、指令、组件化、生命周期及 Axios 异步请求等实战内容,附完整示例与避坑提示,零基础也能快速搭建交互式页面,助力高效开发。
|
11月前
|
机器学习/深度学习 人工智能 前端开发
完蛋,GitHub 70000+ star,我把你(AI)当朋友,你却要代替我,前端同学这下彻底蹦不住了,真的不留一点活路!!!
Screenshot-to-Code 是一款 AI 驱动的开源工具,能将截图、Figma 设计稿或录屏内容快速转化为前端代码。支持 HTML、React、Vue 等多种技术栈,适配 GPT-4 Vision、Claude Sonnet 等 AI 模型,具备视频转原型、本地部署、高精度识别等功能,助力设计快速迭代与开发提效。
548 0
|
人工智能 JavaScript 开发工具
【完全免费】VS Code 最好用的 12 款 AI 代码提示插件!!!
🎉 探索12款免费VSCode AI代码提示插件:Codeium、Codegeex、CodeFuse、TONGYI Lingma、Comate、iFlyCode、Fitten Code、Bito AI、Mintlify Doc Writer、Kodezi AI、aiXcoder、IntelliCode。这些插件提供智能补全、代码生成、注释、优化,支持多种语言,提升编程效率!🚀👩‍💻👨‍💻
29851 121
|
存储 前端开发 JavaScript
Retool 是什么,怎么样? Retool 低代码工具测评
Retool 是面向企业的低代码开发平台。使用 Retool 可快速搭建后台管理工具,比如快速构建 admin 后台管理、销售 ERP、客户 CRM、数据分析看板、amazon 云端文件上传管理等基于数据库或 API 的企业工具。 新事物刚出现,没亲身体验前,总是很难理解。我们总会把新事物与我们已认知的东西来做对比,有人说 Retool 是帮你配置好的 Vue & React 、是可视化拖拽编程平台、是在线前端生成器(并不是)。这些说法都只描述了 Retool 很小的一个点,Retool 是新一代低代码开发平台,是程序员的新效率工具,是历史上不曾有的新工具,拿旧地图看新世界总会有很大偏差,建
1903 0
|
JavaScript API
vue3父子组件相互调用方法详解
vue3父子组件相互调用方法详解
10121 10
|
Web App开发 数据可视化 数据挖掘
利用R语言进行聚类分析实战(数据+代码+可视化+详细分析)
利用R语言进行聚类分析实战(数据+代码+可视化+详细分析)
|
JSON JavaScript API
Vue2和Vue3axios的如何使用,Vue3全局配置axios,axios全局配置
Vue2和Vue3axios的如何使用,Vue3全局配置axios,axios全局配置