先上效果
我要造轮子系列第二个组件是常用的拖拽组件。
很多时候,我们需要让用户来自定义自己想要的菜单顺序,或者一些按钮的排序,那么这个时候,怎么给用户自定义顺序呢?
拖拽无疑是最简单易懂的,因为玩过手机的都知道怎么拖动桌面的app来改变位置。
组件需求分析
- 排序模式-有元素交换模式和元素插入模式,
- 排列模式-有列表模式,和flex布局模式,其中flex布局模式按三等分,后续可以追加参数变为用户传入参数
- 设置盒子总宽度和设置拖拽元素的高度
- 传入
drag-ary
让用户加上自定义内容 - 拖拽后回调返回一个对象分别是两个元素的索引值提供用户使用
拖拽原理
常见的拖拽操作是什么样的呢?整过过程大概有下面几个步骤:
1、用鼠标点击被拖拽的元素
2、按住鼠标不放,移动鼠标
3、拖拽元素到一定位置,放开鼠标
这里的过程涉及到三个dom事件:onmousedown,onmousemove,onmouseup。所以拖拽的基本思路就是:
1、用鼠标点击被拖拽的元素触发onmousedown
(1)设置当前元素的可拖拽为true,表示可以拖拽
(2)记录当前鼠标的坐标x,y
(3)记录当前元素的坐标x,y
2、移动鼠标触发onmousemove
(1)判断元素是否可拖拽,如果是则进入步骤2,否则直接返回
(2)如果元素可拖拽,则设置元素的坐标
元素的x坐标 = 鼠标移动的横向距离+元素本来的x坐标 = 鼠标现在的x坐标 - 鼠标之前的x坐标 + 元素本来的x坐标
元素的y坐标 = 鼠标移动的横向距离+元素本来的y坐标 = 鼠标现在的y坐标 - 鼠标之前的y坐标 + 元素本来的y坐标
3、放开鼠标触发onmouseup
(1)将鼠标的可拖拽状态设置成false
实现拖拽后,就可以作一些边界判断,遍历传入数组,计算拖动元素是否在目标元素的返回并返回索引值,再进行重新排序就可以了
代码
<template>
<div class="drag">
<div :style="{width : boxWidth==='auto'? 'auto': boxWidth+'px'}"
:class="getmode"
>
<div :style="{transform: `translate(${x}px,${y}px)` ,
width:dragInfo.width,height:dragInfo.height,
background:dragInfo.color}"
class="box"
v-show="isDrag" v-html="dragAry[dragInfo.index].html">
</div>
<div :class="[
setBlcock,isTargetDrag&&dragIndex ===index? 'isTargetDrag':'',
!dragMode&&isDrag&&dragInfo.index ===index?'active':'',
isTargetDrag&&dragIndex ===index&&dragMode?'dragModeAct':'']"
:key="index"
:ref="'block'+index"
:style="{background:dragMode&&isDrag && index===Number(dragInfo.index) ?actInfo.color: item.color ,
height:dragHeight==='auto'?'auto':dragHeight+'px'}"
@mousedown.prevent="dragMove($event,index)"
v-for="(item,index) in dragAry"
v-html="dragMode&&isDrag && index===Number(dragInfo.index)?actInfo.html :item.html"
>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'drag',
props: {
//元素排序模式 1:change |0:insert
dragMode: {
type: [Number, Boolean],
default: 1
},
//元素布局模式 flex |list
mode: {
type: String,
default: 'flex'
},
//盒子宽度 'auto'| number
boxWidth: {
type: [Number, String],
default: 'auto'
},
//拖拽元素高度 'auto'| 自定义高度
dragHeight: {
type: [Number,String],
default: 50
},
//传入的元素数组 [color : '',html : '']
dragAry: {
type: Array,
default:[]
}
},
computed: {
// 计算排列模式
getmode() {
return this.mode === 'list' ? 'blockList' : 'blockFlex'
},
// 计算排列模式
setBlcock() {
return this.mode === 'list' ? 'lblock' : 'fblock'
}
},
data() {
return {
x: 0, //拖拽的x坐标
y: 0, // 拖拽的y坐标
isDrag: false,//是否在拖拽
//正在拖拽元素的信息
dragInfo: {
width: '',
height: '',
background: '',
index: 0
},
//目标元素信息
actInfo: {
color: '',
text: ''
},
//是否目标元素
isTargetDrag: false,
//目标元素索引值
dragIndex: null,
}
},
methods: {
//拖拽逻辑
dragMove(ev, index) {
const {color} = this.dragAry[index]
const {clientX, clientY} = ev
const {offsetLeft, offsetTop, offsetWidth, offsetHeight, parentElement} = ev.currentTarget
const dx = clientX - offsetLeft, dy = clientY - offsetTop
let moveX, moveY
this.isDrag = true
this.x = offsetLeft
this.y = offsetTop
this.dragInfo.width = offsetWidth + 'px'
this.dragInfo.height = offsetHeight + 'px'
this.dragInfo.index = index
this.dragInfo.color = color
document.onmousemove = (moveEv) => {
moveEv.preventDefault()
moveX = moveEv.clientX - dx
moveY = moveEv.clientY - dy
if (moveX < 0) {
moveX = 0
}
if (moveX > parentElement.offsetWidth - offsetWidth) {
moveX = parentElement.offsetWidth - offsetWidth
}
if (moveY < 0) {
moveY = 0
}
if (moveY > parentElement.offsetHeight - offsetHeight) {
moveY = parentElement.offsetHeight - offsetHeight
}
this.dragAry.forEach((item, indexs) => {
const [{offsetLeft: ox, offsetTop: oy}] = this.$refs['block' + indexs]
if (moveX + offsetWidth / 2 > ox && moveX + offsetWidth / 2 < ox + offsetWidth &&
moveY + offsetHeight / 2 > oy && moveY + offsetHeight / 2 < oy + offsetHeight) {
this.dragIndex = indexs
this.isTargetDrag = true
this.actInfo.html = this.dragAry[indexs].html
this.actInfo.color = this.dragAry[indexs].color
}
})
this.x = moveX
this.y = moveY
}
document.onmouseup = () => {
if (this.isTargetDrag) {
const tempIndex = index, temp = this.dragAry[tempIndex]
if (this.dragMode) {
this.$set(this.dragAry, tempIndex, this.dragAry[this.dragIndex])
this.$set(this.dragAry, this.dragIndex, temp)
} else {
this.dragAry.splice(this.dragIndex + 1, 0, temp)
this.dragAry.splice(tempIndex, 1)
}
this.$emit('dragMouseup', {index, dragIndex: this.dragIndex})
}
this.isDrag = false
this.isTargetDrag = false
document.onmousemove = null
document.onmousedown = null
}
}
}
}
</script>
<style scoped>
.box {
color: #fff;
position: absolute;
cursor: move;
transition-duration: 100ms;
transition-timing-function: ease-out;
z-index: 111111;
background: red;
}
.active {
background: #fff !important;
border: 1px dashed #000;
}
.blockFlex {
width: 500px;
display: flex;
margin: 0 auto;
flex-wrap: wrap;
position: relative;
transition-duration: 500ms;
transition-timing-function: ease-out;
overflow: hidden;
}
.fblock {
width: calc(calc(100% / 3) - 10px);
margin: 5px;
height: 50px;
color: #fff;
box-sizing: border-box;
background: red;
cursor: move;
transition-duration: 500ms;
transition-timing-function: ease;
overflow: hidden;
}
.blockList {
position: relative;
margin: 0 auto;
transition-duration: 500ms;
transition-timing-function: ease-out;
}
.lblock {
width: 100%;
height: 50px;
margin-bottom: 20px;
color: #fff;
box-sizing: border-box;
background: red;
cursor: move;
transition-duration: 500ms;
transition-timing-function: ease;
}
.isTargetDrag {
cursor: move;
transform: scale(1.1);
transition-duration: 500ms;
transition-timing-function: ease-in;
position: relative;
}
.isTargetDrag:before{
border: 1px dashed red;
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.dragModeAct {
background: #fff !important;
}
</style>
props参数
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
mode | 排列模式 | String | 100 |
drag-mode | 是插入模式还是交换位置 | Number | 0 |
box-width | 盒子宽度 | String | auto |
drag-height | 拖拽元素高度 | String | 100 |
drag-ary | 传入拖拽的数组,object字段可以添加color 和html | Array | [] |
dragMouseup | 返回拖拽的当前和目标索引值 | Events |
最后
这个拖拽组件就这样实现啦,但还是很多不足,或者不能满足很大部分用户的开发需求,不过轮子不是一朝一夕能做好,还是需要时间慢慢打磨、摸索还有什么需求。
另外看到自己的轮子都有200多下载还是有点小激动!!_
最后附上npm和github
npm install nigo-vue-drag
或者
yarn add nigo-vue-drag
仓库地址
git clone https://github.com/shinewen189/nigo-vue-drag.git