大家好,欢迎来到IT知识分享网。
随意拖拽、拉伸元素的功能是现在大热的自定义图表的重要组成功能,本文以最简单的视角搞懂随意拖拽、拉伸元素功能,完成这个功能需要先了解原生
drag
&&vue-ruler-tool
&&@smallwei/avue
demo在线体验地址:zhao-wenchao110.gitee.io/customdrag
一、了解HTML5原生拖拽 drag
1-1、了解拖拽事件的流程
其实拖拽功能的实现无非就是三个步骤:
选中元素 —> 拖动元素 —> 释放元素;
1-2、如何选中元素
HTML5中只需要将元素身上设置属性 draggable
为 true
,那么就可以按住鼠标左键选中元素,进行拖放了,其中 draggable
的属性可以设置几个值:
true
:允许拖动false
:禁止拖动auto
:跟随浏览器定义是否可以拖动
<div draggable="true"></div>
IT知识分享网
1-3、拖动事件
针对对象 | 事件名称 | 说明 |
---|---|---|
被拖动的元素 | dragstart | 在元素开始被拖动时候触发 |
drag | 在元素被拖动时反复触发 | |
dragend | 在拖动操作完成时触发 | |
目的地对象 | dragenter | 当被拖动元素进入目的地元素所占据的屏幕空间时触发 |
dragover | 当被拖动元素在目的地元素内时触发 | |
dragleave | 当被拖动元素没有放下就离开目的地元素时触发 |
其中需要注意的是 dragenter
&& dragover
两个事件,他们是拒绝接收所有被拖放的元素,所以在使用时需要 .preventDefault
阻止默认的事件冒泡。
1-4、释放
针对对象 | 事件名称 | 说明 |
---|---|---|
目的地对象 | drop | 当被拖动元素在目的地元素里放下时触发,一般需要取消浏览器的默认行为。 |
本文只能做最简单的知识点普及,如果需要更深入了解原生drag事件,我个人推荐两篇文章
-
HTML5原生拖拽/拖放 Drag & Drop 详解
-
h5原生draggable拖拽事件详解
另外使用vue
框架也可以使用二次封装的 vuedraggable
- Vue.Draggable中文文档参考 | 官方文档翻译
二、了解标尺功能 vue-ruler-tool
使用后便会在上边和左边出现标尺,如上图所见;
2-1、安装
IT知识分享网npm install --save vue-ruler-tool
2-2、使用
<vue-ruler-tool
v-model="dashboard.presetLine"
class="vueRuler"
:step-length="50"
:parent="true"
:position="'relative'"
:is-scale-revise="true"
:visible.sync="dashboard.presetLineVisible"
>
<div></div>
</vue-ruler-tool>
<script>
import VueRulerTool from 'vue-ruler-tool'
export default {
components: {
VueRulerTool
},
}
</script>
2-3、属性
parent
类型:Boolean
默认值: false
限制组件大小在父级内部
IT知识分享网<vue-ruler-tool :parent="true" >
position
类型:String
默认值: relative
可能值:['absolute', 'fixed', 'relative', 'static', 'inherit']
规定标尺工具的定位类型
<vue-ruler-tool :position="'fixed'" >
复制代码
isHotKey
类型: Boolean
默认值: true
快捷键键开关,目前仅支持快捷键R
标尺显示开关
<vue-ruler-tool :is-hot-key="ture" >
复制代码
visible
类型: Boolean
默认值: true
是否显示,如果设为false则隐藏,可通过.sync接收来自R
快捷键的修改
<v-ruler :visible.sync="visible" >
data() {
return {
visible: true
}
}
isScaleRevise
类型: Boolean
默认值: false
刻度修正(根据content进行刻度重置),意思就是从内容的位置开始从0计数
<vue-ruler-tool :is-scale-revise="ture" >
复制代码
topSpacing
类型: Number
默认值: 0,
标尺与窗口的上间距,如果你的position
不为fixed
,此项必填
leftSpacing
类型: Number
默认值: 0
标尺与窗口的左间距,如果你的position
不为fixed
,此项必填
presetLine
类型: Array
默认值: []
接受格式:[{ type: 'l', site: 50 }, { type: 'v', site: 180 }]
预置参考线l
代表水平线,v
代表垂直线,site
为Number类型
<vue-ruler-tool :preset-line="[{ type: 'l', site: 100 }, { type: 'v', site: 200 }]" >
复制代码
contentLayout
类型: Object
默认值: { top: 50, left: 50 }
内容部分布局分布,及内容摆放位置
<vue-ruler-tool :content-layout="{left:200,top:100}" >
2-4、方法
quickGeneration
参数:[{ type: 'l', site: 100 }, { type: 'v', site: 200 }]
快速设置参考线,一般用来通过弹窗让用户输入
<vue-ruler-tool ref='rulerTool' >
let params=[
{ type: 'l', site: 100 },
{ type: 'v', site: 200 }
]
this.$refs.rulerTool.quickGeneration(params)
三、Avue
该组件使用文档地址:avuejs.com/default/dra…
3-1、安装
yarn add @smallwei/avue -S # 或者:npm i @smallwei/avue -S
3-2、使用
// src/main
import Avue from '@smallwei/avue';
import '@smallwei/avue/lib/index.css';
Vue.use(Avue);
四、自定义拖拽 demo 实现
4-1、思路
首先我们要了解自定义图表中拖拽功能的基本功能要求:
- 每个组件图表拖拽到工作台中都是需要复制的
- 图表组件宽与高都是可以拉伸的
- 组件的拖拽体验需要丝滑
- 最后工作台的组件都是需要持久化存储的
4-2、设置拖拽组件区域
template 部分
<ul class="col toolsBox">
<!-- 组件区 -->
<li v-for="item in arr1" :key="item.id" class="li" draggable="true" @dragstart="dragStartFn(item.id)" @dragend="dragEnd()">
<div class="item">{{ item.name }}</div>
</li>
<li class="save" @click="saveWidgetsFn">
保存
</li>
</ul>
data中的数据
arr1: [
{ id: 'a', name: '1' },
{ id: 'b', name: '2' },
{ id: 'c', name: '3' },
{ id: 'd', name: '4' },
{ id: 'e', name: '5' }
],
1、固定组件根据data中的数据结构渲染,数据内最重要的是id
,是用来判定组件的type种类,具有唯一性;
2、并且要给每个组件都设置 draggable="true"
表示为可拖拽;
3、设置 dragstart
元素被拖拽时触发事件存储组件唯一 id,在拖拽完成时触发 dragend
清空组件 id;
4-3、工作台区域
template 部分
<div class="col big-father">
<!-- 标尺 -->
<vue-ruler-tool
v-model="dashboard.presetLine" // 此处绑定辅助线坐标
class="vueRuler"
:step-length="50" // 标尺间隔
:parent="true" // 限制组件大小在父级内部
:position="'relative'"
:is-scale-revise="true" // 刻度从 0 开始
:visible.sync="dashboard.presetLineVisible" // 辅助线是否显示
>
<!-- 工作台 -->
<div //此元素是内部工作台区域范围
id="workbench"
class="workbench"
@drop="widgetOnDraggedFn($event)" // 组件被拖拽到工作台中复制一个新组件数据,并且将定位保存到其中
@dragover="dragOverFn($event)" // 设置阻止冒泡事件
@mousedown.prevent="handleMousedown" // 设置点击工作台,取消avue组件外部一圈的辅助线
>
// 该组件可以改变内部元素的宽高及定位
<avue-draggable
v-for="(item2,index2) in widgets" // widgets是工作台组件数据容器
:key="index2" // 此处key用index,因为组件可能会重复使用,但是index不会重复,并且后面取消avue组件外部一圈的辅助线需要index
ref="draggableAvue"
style="position: absolute;" // 子绝父相
:index="index2" // 将唯一表示index绑定在组件身上,后面又用
:width="item2.value.width"
:height="item2.value.height"
:left="item2.value.left"
:top="item2.value.top"
@focus="handleFocus" // 聚焦到组件时触发
@blur="handleBlur" // 失焦时触发,在失焦时需要将最后调整好的宽高定位保存到data中的widgets,并且需要获取辅助线和移除辅助线
>
<div
class="item2"
>
{{ item2.name }}
</div>
</avue-draggable>
</div>
</vue-ruler-tool>
</div>
js 部分
<script>
import VueRulerTool from 'vue-ruler-tool'
export default {
components: {
VueRulerTool
},
data() {
return {
// 定义要被拖拽对象的数组
arr1: [
{ id: 'a', name: '1' },
{ id: 'b', name: '2' },
{ id: 'c', name: '3' },
{ id: 'd', name: '4' },
{ id: 'e', name: '5' }
],
dragWidgetId: '', // 当前从工具栏拖拽的组件种类Id
currentIndex: '', // 当前工作台上操作的组件index
// 工作台大屏画布,保存到表gaea_report_dashboard中
dashboard: {
id: null,
title: '', // 大屏页面标题
backgroundColor: '', // 大屏背景色
backgroundImage: '', // 大屏背景图片
presetLine: [], // 辅助线
presetLineVisible: true // 辅助线是否显示
},
// 大屏画布中的组件
widgets: [
// {
// // type和value最终存到数据库中去,保存到gaea_report_dashboard_widget中
// id: '',
// value: {
// width: 200,
// height: 200,
// left: 200,
// top: 200
// }
// }
] // 工作区中拖放的组件
}
},
created() {
// 持久化数据操作
if (JSON.parse(localStorage.getItem('saveWidgetsFn'))) {
this.widgets = JSON.parse(localStorage.getItem('saveWidgetsFn'))
}
},
methods: {
dragStartFn(id) {
this.dragWidgetId = id
},
dragEnd() {
this.dragWidgetId = ''
},
/* 拖拽到的内容区域 */
widgetOnDraggedFn(e) {
// 获取结束坐标和列名
const eventX = e.clientX // 结束在屏幕的x坐标
const eventY = e.clientY // 结束在屏幕的y坐标
// 获取工作台 dom 的 top&left 定位
const workbenchPosition = this.getDomTopLeftById('workbench')
const widgetTopInWorkbench = eventY - workbenchPosition.top - 25
const widgetLeftInWorkbench = eventX - workbenchPosition.left - 50
// 找出复制元素的 数据内容
const obj = this.arr1.find(item => item.id === this.dragWidgetId)
// 在工作台增加 一个新标签
this.widgets.push({
id: this.dragWidgetId,
name: obj.name,
value: {
width: 100,
height: 50,
left: widgetLeftInWorkbench,
top: widgetTopInWorkbench
}
})
},
dragOverFn(e) {
e.preventDefault()
e.stopPropagation()
},
// 获取dom在屏幕中的top和left
getDomTopLeftById(id) {
const dom = document.getElementById(id)
let top = 0
let left = 0
if (dom != null) {
top = dom.getBoundingClientRect().top
left = dom.getBoundingClientRect().left
}
return { top: top, left: left }
},
// avue
handleFocus({ index, left, top, width, height }) {
},
handleBlur({ index, left, top, width, height }) {
this.$refs.draggableAvue[index].setActive(true)
// 保存新增修改的组件
if (left !== 0 && top !== 0) {
this.widgets[index].value = {
width,
height,
left,
top
}
}
// 判定如果 currentIndex 数组超过1个,则需要把上一个选中取消,只保留下一个选中
if (this.currentIndex !== index && this.currentIndex !== '') {
this.$refs.draggableAvue[this.currentIndex].setActive(false)
}
this.currentIndex = index
},
// 存储 widgets
saveWidgetsFn() {
localStorage.setItem('saveWidgetsFn', JSON.stringify(this.widgets))
alert('存储成功!')
},
handleMousedown() {
if (this.currentIndex !== '') this.$refs.draggableAvue[this.currentIndex].setActive(false)
}
}
}
</script>
结语
本文只是简单的写了个demo实现了随意拖拽拉伸元素功能的实现,仅仅只是自定义图表众多复杂功能中的一环,以后还会更新其余功能的实现demo与思路,希望大家多多支持~!;
gitee 本文Demo的代码地址
封面优秀开源的自定义图表,本人也是使用了该图表时所学甚多
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/9433.html