在vue中實現(xiàn)移動端touch拖拽排序的具體代碼,如下內(nèi)容:
功能介紹:
在移動端開發(fā)中,希望實現(xiàn)類似支付寶應(yīng)用管理頁面的可拖拽排序交互。
大致需求:
1、卡片按照一定順序排序,超出橫向范圍換行顯示;
2、手指長按卡片,可進行拖拽控制,卡片追隨手指移動;
3、卡片移動到相應(yīng)位置,該位置上的卡片向后或向前更換位置,當前位置空出;
4、松開手指,卡片可回到原位置或新位置進行展示;
整體思路:
1、卡片實行flex彈性布局,通過數(shù)組的遍歷可自動顯示在相應(yīng)位置;
2、手指長按可使用定時器來判斷,若手指松開,則關(guān)閉定時器,等待下次操作再啟用;
3、跟隨手指移動的卡片可使用absolute定位控制,同時根據(jù)手指位置判斷當前所在位置;
4、位置發(fā)生改變時,控制數(shù)組添加或刪除相應(yīng)元素,從而實現(xiàn)換位效果;
簡單效果展示:
具體實現(xiàn):
一、display:flex+v-for布局:
使用彈性布局實現(xiàn)
<!-- 外層ul控制卡片范圍 --> <ul> <li class="libox" v-for="(item, ind) in list" :key="ind"> <div> <!-- div顯示數(shù)組內(nèi)容 --> {{item.name}} </div> </li> </ul>
data() { return { list: [ { name: '1' }, // 卡片內(nèi)容 { name: '2' }, { name: '3' } ] } },
ul { width: 100%; height: 100%; display: flex; // 彈性布局 flex-wrap: wrap; overflow: hidden; // 超出部分隱藏,目的阻止橫向滾動 .libox { width: 25%; // 這里以4列為例 height: 70px; >div { background-color:#eee; width: calc(100% - 10px); height: 36px; border-radius: 18px; } } }
二、touch事件綁定:
應(yīng)用到touchstart,touchmove,touchend事件,使用定時器實現(xiàn)長按效果:
<div @touchstart="touchstart($event, item)" @touchmove="touchMove($event, item)" @touchend="touchEnd($event, item)" > {{item.name}} </div>
data() { return { timeOutEvent: 0 }; }, methods: { // 手指觸摸事件 touchstart(ev, item) { // 定時器控制長按時間,超過500毫秒開始進行拖拽 this.timeOutEvent = setTimeout(() => { this.longClick = 1; }, 500); }, // 手指在屏幕上移動 touchMove(ev) { // 未達到500毫秒就移動則不觸發(fā)長按,清空定時器 clearTimeout(this.timeOutEvent); }, // 手指離開屏幕 touchEnd() { clearTimeout(this.timeOutEvent); } }
三、卡片移動:
在ul中增加一個獨立的不在循環(huán)中的li標簽,改為absolute定位,通過動態(tài)修改li標簽top、left屬性實現(xiàn)跟隨手指移動效果。
<ul> <li v-show="selectItem.name" class="selectBox" ref="selectBox"> {{selectItem.name}} </li> </ul>
ul { position: relative; // 此li標簽的樣式與循環(huán)li標簽內(nèi)的div樣式保持一致 // 背景色加深,代表被手指選中 .selectBox { position: absolute; width: calc(25% - 10px); height: 36px; border-radius: 18px; background-color:#6981c8; color:white; } }
當卡片被選中,將卡片內(nèi)容賦值給全局變量,判斷卡片顯示隱藏(v-show判斷,隱藏但占位),實現(xiàn)選中元素位置空出效果:
手指位置通過touchmove獲取:
<div @touchstart="touchstart($event, item)" @touchmove="touchMove($event, item)" @touchend="touchEnd($event, item)" @click="listClickHandler(item)" v-show="item.name !== selectItem.name" > {{item.name}} </div>
touchstart(ev, item) { this.timeOutEvent = setTimeout(() => { this.longClick = 1; this.selectItem = item; // 將卡片內(nèi)容賦值給全局變量 const selectDom = ev.target; // li元素 // 元素初始位置 this.oldNodePos = { x: selectDom.offsetLeft, y: selectDom.offsetTop }; // 鼠標原始位置 this.oldMousePos = { x: ev.touches[0].pageX, y: ev.touches[0].pageY }; const lefts = this.oldMousePos.x - this.oldNodePos.x; // x軸偏移量 const tops = this.oldMousePos.y - this.oldNodePos.y; // y軸偏移量 const { pageX, pageY } = ev.touches[0]; // 手指位置 this.$refs.selectBox.style.left = `${pageX - lefts}px`; this.$refs.selectBox.style.top = `${pageY - tops}px`; }, 500); }, touchMove(ev) { clearTimeout(this.timeOutEvent); // this.longClick === 1判斷是否長按 if (this.longClick === 1) { const selectDom = ev.target.parentNode; // li元素 const lefts = this.oldMousePos.x - this.oldNodePos.x; // x軸偏移量 const tops = this.oldMousePos.y - this.oldNodePos.y; // y軸偏移量 const { pageX, pageY } = ev.touches[0]; // 手指位置 this.$refs.selectBox.style.left = `${pageX - lefts}px`; this.$refs.selectBox.style.top = `${pageY - tops}px`; } }
四、獲取手指所在位置:
cardIndex(selDom, moveleft, movetop) { const liWid = selDom.clientWidth; // li寬度 const liHei = selDom.clientHeight; // li高度 const newWidNum = Math.ceil((moveleft / liWid)); // 手指所在列 const newHeiNum = Math.ceil((movetop / liHei)); // 手指所在行 const newPosNum = (newHeiNum - 1) * 4 + newWidNum; // 手指所在位置 // 判斷是否是新位置并且沒有超出列表數(shù)量范圍 if (this.oldIndex !== newPosNum && newPosNum <= this.list.length) { // 將新的位置賦值給全局變量oldIndex this.oldIndex = newPosNum; } }
五、操作數(shù)組(刪除或插入元素):
監(jiān)聽oldIndex的值,若發(fā)生改變則執(zhí)行操作數(shù)組函數(shù)
watch: { oldIndex(newVal) { const oldIndex = this.list.indexOf(this.selectItem); this.list.splice(oldIndex, 1); this.list.splice(newVal - 1, 0, this.selectItem); } },
六、手指離開屏幕:
手指離開屏幕,清空選中的元素selectItem,跟隨手指移動的卡片(li.selectBox)自動隱藏,在循環(huán)中隱藏的卡片(li)則會顯示,實現(xiàn)換位效果。
touchEnd() { clearTimeout(this.timeOutEvent); this.selectItem = {}; }
七、備注:
上面的代碼是基于div容器內(nèi)只有文字沒有其他dom元素實現(xiàn),后發(fā)現(xiàn)若div中存在dom元素例如svg,則【$event】選中的值會變成其子元素,且拖拽排序出現(xiàn)問題,希望知道原因的小伙伴可以評論或私信告訴我一下,非常感謝。
粗暴的解決方式:
div容器增加after蒙版,可設(shè)置為透明色:
div position: relative; &::after { content: ''; width: 100%; height: 100%; background: rgba(255, 177, 177, 0.3); // 背景色 position: absolute; top: 0; left: 0; } }
八、完整代碼:
<template> <div> <ul> <li class="libox" v-for="(item, index) in list" :key="index" :id="'card' + (index + 1)" > <div @touchstart="touchstart($event, item)" @touchmove="touchMove($event, item)" @touchend="touchEnd($event, item)" v-show="item.name !== selectItem.name" > {{item.name}} <svg class="icon svg-icon" aria-hidden="true"> <use :xlink:href="item.icon" rel="external nofollow" ></use> </svg> </div> </li> <li v-show="selectItem.name" class="selectBox" ref="selectBox"> {{selectItem.name}} <svg class="icon svg-icon" aria-hidden="true"> <use :xlink:href="selectItem.icon" rel="external nofollow" ></use> </svg> </li> </ul> </div> </template> <script> export default { data() { return { // 列表數(shù)據(jù) list: [ { name: '1', selected: true, icon: '#icon-mianxingbenzivg' }, { name: '2', selected: true, icon: '#icon-mianxingchizi' }, { name: '3', selected: true, icon: '#icon-mianxingdiannao' }, { name: '4', selected: true, icon: '#icon-mianxingdayinji' }, { name: '5', selected: true, icon: '#icon-mianxingdingshuqi' }, { name: '6', selected: true, icon: '#icon-mianxingheiban' }, { name: '7', selected: true, icon: '#icon-mianxinggangbi' }, { name: '8', selected: true, icon: '#icon-mianxingboshimao' }, { name: '9', selected: true, icon: '#icon-mianxingjisuanqi' }, { name: '10', selected: true, icon: '#icon-mianxinghuaxue' }, { name: '11', selected: true, icon: '#icon-mianxingqianbi' }, { name: '12', selected: true, icon: '#icon-mianxingshubao' }, { name: '13', selected: true, icon: '#icon-mianxingshuicaibi' }, { name: '14', selected: true, icon: '#icon-mianxingtushu' }, ], // 選中元素內(nèi)容 selectItem: {}, timeOutEvent: 0, oldNodePos: { x: 0, y: 0, }, oldMousePos: { x: 0, y: 0 }, oldIndex: 0, // 長按標識 longClick: 0 }; }, watch: { oldIndex(newVal) { const oldIndex = this.list.findIndex(r=> r.name === this.selectItem.name); this.list.splice(oldIndex, 1); this.list.splice(newVal, 0, this.selectItem); } }, methods: { touchstart(ev, item) { this.longClick = 0; const that = this; const selectDom = ev.currentTarget; // div元素 this.timeOutEvent = setTimeout(() => { that.longClick = 1; that.selectItem = item; // 元素初始位置 that.oldNodePos = { x: selectDom.offsetLeft, y: selectDom.offsetTop }; // 鼠標原始位置 that.oldMousePos = { x: ev.touches[0].pageX, y: ev.touches[0].pageY }; const lefts = that.oldMousePos.x - that.oldNodePos.x; // x軸偏移量 const tops = that.oldMousePos.y - that.oldNodePos.y; // y軸偏移量 const { pageX, pageY } = ev.touches[0]; // 手指位置 that.$refs.selectBox.style.left = `${pageX - lefts}px`; that.$refs.selectBox.style.top = `${pageY - tops}px`; }, 500); }, touchMove(ev) { clearTimeout(this.timeOutEvent); const selectDom = ev.currentTarget.parentNode; // li元素 if (this.longClick === 1) { const lefts = this.oldMousePos.x - this.oldNodePos.x; // x軸偏移量 const tops = this.oldMousePos.y - this.oldNodePos.y; // y軸偏移量 const { pageX, pageY } = ev.touches[0]; // 手指位置 this.$refs.selectBox.style.left = `${pageX - lefts}px`; this.$refs.selectBox.style.top = `${pageY - tops}px`; this.cardIndex(selectDom, pageX, pageY); } }, touchEnd() { clearTimeout(this.timeOutEvent); this.selectItem = {}; }, /** * 計算當前移動卡片位于卡片的哪一行哪一列 */ cardIndex(selDom, moveleft, movetop) { const liWid = selDom.clientWidth; const liHei = selDom.clientHeight; const newWidthNum = Math.ceil((moveleft / liWid)); // 哪一列 const newHeightNum = Math.ceil((movetop / liHei)); // 哪一行 const newPositionNum = (newHeightNum - 1) * 4 + newWidthNum; if (this.oldIndex !== newPositionNum - 1) { if (newPositionNum <= this.list.length) { this.oldIndex = newPositionNum - 1; } else { this.oldIndex = this.list.length - 1; } } } } } </script> <style scoped> @mixin myFlexCenter{ display: flex; justify-content: center; align-items: center; } ul { width: 100%; height: 100%; display: flex; flex-wrap: wrap; position: relative; overflow: hidden; .libox { width: 25%; height: 100px; border-right: 1px dashed #cccccc; border-bottom: 1px dashed #cccccc; box-sizing: border-box; @include myFlexCenter; >div { width: calc(100% - 10px); height: 75px; border-radius: 18px; @include myFlexCenter; position: relative; &::after { content: ''; width: 100%; height: 100%; background: rgba(255, 177, 177, 0.3); position: absolute; top: 0; left: 0; } >svg { width: 75px; height: 75px; } } } .selectBox{ position: absolute; width: calc(25% - 10px); height: 75px; border-radius: 18px; >svg { width: 75px; height: 75px; } background-color: rgba(0, 0, 0, 0.1); color:white; @include myFlexCenter; -moz-user-select:none; /*火狐*/ -webkit-user-select:none; /*webkit瀏覽器*/ -ms-user-select:none; /*IE10*/ -khtml-user-select:none; /*早期瀏覽器*/ user-select:none; } } </style>
以上是相關(guān)內(nèi)容,請大家多多關(guān)注。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/127703.html
摘要:全家桶仿騰訊體育一年一度的總決賽,相信球迷用的最多的就是騰訊體育這款,剛好上手,當練手就把這個仿下來。這樣剛進去的時候頁面加載時間明顯減短。可以包含任意異步操作。 Vue2.0全家桶仿騰訊體育APP 一年一度的NBA總決賽,相信球迷用的最多的就是騰訊體育這款APP,剛好上手Vue,當練手就把這個APP仿下來。 showImg(https://segmentfault.com/img/r...
摘要:最近接觸移動端開發(fā)。自己寫一個類似微博的圖片預覽器來學習一下移動端手勢的實現(xiàn)和的屬性的使用。在發(fā)生時將坐標位置向減得到位移量。雙手勢單手勢雙手勢單手勢觸發(fā)移動事件圖片放大功能的實現(xiàn)我采用了的屬性進行縮放,并且設(shè)置來設(shè)置縮放中心位置。 最近接觸vue.js移動端開發(fā)。自己寫一個類似微博的圖片預覽器來學習一下移動端手勢的實現(xiàn)和css3的屬性的使用。 目標分析 首先分析圖片預覽器的功能: 1...
移動端拖拽懸浮按鈕如何用vue實現(xiàn),下面看看具體內(nèi)容: 功能介紹: 開發(fā)中,要考慮用戶體驗,懸浮按鈕不僅要顯示在側(cè)邊,更是可以允許隨意拖拽換位。 需求描述: 1、按鈕懸浮顯示在頁面?zhèn)冗叄弧 ?、當手指長按左鍵按鈕,即可允許拖拽改變位置; 3、按鈕移動結(jié)束,手指松開,計算距離左右兩側(cè)距離并自動移動至側(cè)邊顯示; 4、移動至側(cè)邊后,按鈕根據(jù)具體左右兩次位置判斷改變現(xiàn)實樣式; 整體思路:...
摘要:先看本次文章先實現(xiàn)內(nèi)容拖拽和滑動動畫后續(xù)文章一步一步增加功能比如滾動條下拉加載等功能說點濕的其實代碼量挺大的近行還有另一個類似的庫他的代碼量和差不多因為原理都是一樣的閱讀他們的代碼發(fā)現(xiàn)里面很多邏輯其實都是在做手勢判斷比如拖拽和劃還有部分元 showImg(https://segmentfault.com/img/remote/1460000018779771?w=914&h=129);...
摘要:注意點在鼠標操作拖放期間,有一些事件可能觸發(fā)多次,比如和。可拖拽元素,建議使用,設(shè)定可拖拽元素的鼠標游標,提升交互。在中使用拖拽中使用可以直接綁定到組件上。 什么是 Drag and Drop (拖放)? 簡單來說,HTML5 提供了 Drag and Drop API,允許用戶用鼠標選中一個可拖動元素,移動鼠標拖放到一個可放置到元素的過程。 我相信每個人都或多或少接觸過拖放,比如瀏覽...
閱讀 547·2023-03-27 18:33
閱讀 732·2023-03-26 17:27
閱讀 630·2023-03-26 17:14
閱讀 591·2023-03-17 21:13
閱讀 521·2023-03-17 08:28
閱讀 1801·2023-02-27 22:32
閱讀 1292·2023-02-27 22:27
閱讀 2178·2023-01-20 08:28