我要造轮子系列第二个组件是经常使用的拖拽组件。javascript
不少时候,咱们须要让用户来自定义本身想要的菜单顺序,或者一些按钮的排序,那么这个时候,怎么给用户自定义顺序呢? 拖拽无疑是最简单易懂的,由于玩过手机的都知道怎么拖动桌面的app来改变位置。css
drag-ary
让用户加上自定义内容常见的拖拽操做是什么样的呢?整过过程大概有下面几个步骤:html
一、用鼠标点击被拖拽的元素vue
二、按住鼠标不放,移动鼠标java
三、拖拽元素到必定位置,放开鼠标git
这里的过程涉及到三个dom事件:onmousedown,onmousemove,onmouseup。因此拖拽的基本思路就是:github
一、用鼠标点击被拖拽的元素触发onmousedownnpm
(1)设置当前元素的可拖拽为true,表示能够拖拽数组
(2)记录当前鼠标的坐标x,ymarkdown
(3)记录当前元素的坐标x,y
二、移动鼠标触发onmousemove
(1)判断元素是否可拖拽,若是是则进入步骤2,不然直接返回
(2)若是元素可拖拽,则设置元素的坐标
元素的x坐标 = 鼠标移动的横向距离+元素原本的x坐标 = 鼠标如今的x坐标 - 鼠标以前的x坐标 + 元素原本的x坐标
元素的y坐标 = 鼠标移动的横向距离+元素原本的y坐标 = 鼠标如今的y坐标 - 鼠标以前的y坐标 + 元素原本的y坐标
三、放开鼠标触发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>
复制代码
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
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
复制代码