我要造輪子系列第二個組件是經常使用的拖拽組件。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
複製代碼