随意拖拽、拉伸元素的功用是现在大热的自界说图表的重要组成功用,本文以最简略的视角搞懂随意拖拽、拉伸元素功用,完结这个功用需求先了解原生
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>
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、装置
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
约束组件巨细在父级内部
<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"
// 子绝父相
: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的代码地址
封面优异开源的自界说图表,自己也是运用了该图表时所学甚多