持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
前言
本文是**手摸手带你实现可视化系统(高仿dataV)**的第三篇--拖放组件到主舞台上 一、 【可视化系统】如何实现编辑面板的动态缩放?
也欢迎持续关注我的专栏# 前端可视化 ,一起学习更多可视化领域知识。
通过之前两篇文章的介绍,我想你大概已经掌握了如何制作一个编辑面板(主舞台),和如何实现组件在编辑面板上自由变换(拖拽、缩放、旋转等)。
我们今天主要讲的是,如何从组件库中拖动一个组件到主舞台上,文字不好理解,我们直接看下最终要实现的效果。
让我们奔着这个效果,来一步步实现。
通读本文你将收获:
- Html5 新属性draggable的使用方式
- drag相关事件和api
- 从零到一实现拖放的实践经验分享
需求分析
这个需求其实就是向主舞台添加组件,我最早实现的一版是点击组件图标,向舞台上添加组件数据,给组件一个初始位置。 这样做大概有两方面的问题:
- 初始位置的定制上,不管放在哪都觉得不够优雅;
- 其次编辑面板有层级的概念,后添加的组件会重叠的盖到先添加的组件之上
出于这样的考虑,感觉更优雅的方式就是把主动权交给使用方,主动拖动组件到合适的位置,然后释放。
那么接下来我们就明确需求了: 实现组件拖放到主舞台。
轮子&梯子
首先想到的就是sortableJS, 因为之前做list拖拽排序的时候有用到过这个三方库。感觉还是蛮强大的。
官方示例看了下,基本上都是和列表相关的,侧重于列表的移动和拖动排序,就像它的名字:sortable.
我们舞台上是有层级的概念的,这种挤压碰撞的效果显然不是我们想要的。
之前有了解到html5有支持最新的dragable属性,轻松实现拖拽,刚好尝试体验一下,所以,直接放弃了sortableJS,拥抱dragable.
拥抱dragable
学习api只需要两步:第一步搜索MDN,第二步官方demo跑一下。
提炼一下MDN的重要信息:
- dragable 属性(true/false)用来表示元素是否可被拖拽;
- 核心的事件有这么三个(dragstart\dragenter\drop)
- 拖放的目标位置元素需要阻止浏览器的默认事件
- 拖动完成需要完成数据传递(在事件回调event中保存)
开始撸代码
<div
class="chart"
v-for="widget in group.widgets"
+ draggable="true"
:key="widget.name"
@click="UseIt(widget)"
+ @dragstart="e => dragstart(e, widget)"
+ @dragenter="dragenter"
>
<img class="chart-img" :src="widget.img" draggable="false" />
<div class="chart-label">{{ widget.label }}</div>
</div>
我们先是给组件添加了draggable="true"
,声明可拖拽; 然后绑定dragstart、dragenter事件。
我们这里给dragstart绑定了组件的数据,就是为了传递给主舞台。
dragstart(event, widget) {
// 获取完整的组件属性
const _widget = this.getWidgetData(widget);
// event.dataTransfer.setData 方法,记录数据
event.dataTransfer.setData("text/plain", JSON.stringify(_widget));
// 代表拖拽的模式,copy代表复制
event.dataTransfer.effectAllowed = "copy";
},
上面代码的注释已经说明了问题,对具体某个方法不明白可以看看mdn
到这里我们才算完成了一半,我们只做了 1.使组件图标可拖动; 2.拖动时绑定了数据。
我们还需要在组件拖动到主舞台上时,获取到组件数据,并在相应位置绘制出该组件。
主舞台的处理
<div
class="screen"
ref="screen"
:style="screenConfig"
@click="loadSetting('screen')"
+ @dragover="dragover"
+ @drop="drop"
>
还是添加两个事件回调,但是和组件上绑定的不一样哦~
dragover(event) {
// 阻止浏览器的默认事件
event.preventDefault();
},
drop(event) {
// 获取组件数据
const data = event.dataTransfer.getData("text/plain");
// 获取组件拖动到舞台上的相对位置
const { layerX, layerY } = event;
const config = JSON.parse(data);
const widget = {
...config,
posX: layerX / this.scale - (config.width || 0) / 2,
posY: layerY / this.scale - (config.height || 0) / 2,
};
//添加组件到主舞台
this.addElements(widget);
event.preventDefault();
},
获取组件拖动到舞台上的相对位置, 这个地方的layerX, layerY
是拖动元素在目标元素上的相对位置,以左上角为坐标原点。
我们这里更新实际位置
posX: layerX / this.scale - (config.width || 0) / 2,
,解释一下这样处理的原因,因为我们的主舞台是经过 this.scale 倍缩放的,所以layerX / this.scale
才是实际在舞台上的位置,然后-(config.width || 0) / 2
,是为了以拖动位置为组件的中心点来渲染组件。
总结
以上,我们就完成了文章开头动图实现的拖放效果了。
系列小结: 目前我们已经完成了可视化系统最核心部分: 舞台搭建、组件拖放、组件变换。 紧接着我们需要完成组件配置表单部分,通过配置项动态生成表单供用户配置。
我将在最近完成这部分的工作并以文章的形式和大家交流,请持续关注~
更文不易,请多多点赞、收藏,这将是我持续创作的动力!