最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue實現(xiàn)移動端touch拖拽排序

 更新時間:2022年07月14日 10:06:29   作者:iatkotworld  
這篇文章主要為大家詳細介紹了vue實現(xiàn)移動端touch拖拽排序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

本文實例為大家分享了vue實現(xiàn)移動端touch拖拽排序的具體代碼,供大家參考,具體內(nèi)容如下

功能介紹:

在移動端開發(fā)中,希望實現(xiàn)類似支付寶應(yīng)用管理頁面的可拖拽排序交互。

大致需求:

1、卡片按照一定順序排序,超出橫向范圍換行顯示;
2、手指長按卡片,可進行拖拽控制,卡片追隨手指移動;
3、卡片移動到相應(yīng)位置,該位置上的卡片向后或向前更換位置,當(dāng)前位置空出;
4、松開手指,卡片可回到原位置或新位置進行展示;

整體思路:

1、卡片實行flex彈性布局,通過數(shù)組的遍歷可自動顯示在相應(yīng)位置;
2、手指長按可使用定時器來判斷,若手指松開,則關(guān)閉定時器,等待下次操作再啟用;
3、跟隨手指移動的卡片可使用absolute定位控制,同時根據(jù)手指位置判斷當(dāng)前所在位置;
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標(biāo)簽,改為absolute定位,通過動態(tài)修改li標(biāo)簽top、left屬性實現(xiàn)跟隨手指移動效果。

<ul>
? ? <li v-show="selectItem.name" class="selectBox" ref="selectBox">
? ? ? ? {{selectItem.name}}
? ? </li>
</ul>
ul {
? ? position: relative;
? ? // 此li標(biāo)簽的樣式與循環(huán)li標(biāo)簽內(nèi)的div樣式保持一致
? ? // 背景色加深,代表被手指選中
? ? .selectBox {
? ? ? ? position: absolute;
? ? ? ? width: calc(25% - 10px);
? ? ? ? height: 36px;
? ? ? ? border-radius: 18px;
? ? ? ? background-color:#6981c8;
? ? ? ? color:white;
? ? }
}

當(dāng)卡片被選中,將卡片內(nèi)容賦值給全局變量,判斷卡片顯示隱藏(v-show判斷,隱藏但占位),實現(xiàn)選中元素位置空出效果:

手指位置通過touchmove獲?。?/p>

<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
? ? ? ? };
? ? ? ? // 鼠標(biāo)原始位置
? ? ? ? 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,
? ? ? // 長按標(biāo)識
? ? ? 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
? ? ? ? };
? ? ? ? // 鼠標(biāo)原始位置
? ? ? ? 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 = {};
? ? },
? ? /**
? ? ?* 計算當(dāng)前移動卡片位于卡片的哪一行哪一列
? ? ?*/
? ? 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 lang="scss" 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>

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • vue從后端獲取到文件的?url?地址及前端根據(jù)?url?地址下載文件的實現(xiàn)思路

    vue從后端獲取到文件的?url?地址及前端根據(jù)?url?地址下載文件的實現(xiàn)思路

    這篇文章主要介紹了vue?中從后端獲取到文件的?url?地址及前端根據(jù)?url?地址下載文件,項目用的是?vben?admin?框架,用的是?vue3?+?TS,后端返回的是文件的?url?地址,對vue后端獲取?url?地址的相關(guān)知識感興趣的朋友一起看看吧
    2024-02-02
  • vue 解決computed修改data數(shù)據(jù)的問題

    vue 解決computed修改data數(shù)據(jù)的問題

    今天小編就為大家分享一篇vue 解決computed修改data數(shù)據(jù)的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • vue3 setup語法糖下的定時器的使用與銷毀

    vue3 setup語法糖下的定時器的使用與銷毀

    如果在組件中需要使用定時器,注意在銷毀組件的時候,要對定時器進行銷毀,否則時間長了會導(dǎo)致頁面卡頓,這篇文章給大家介紹vue3 setup語法糖下的定時器的使用與銷毀的知識,感興趣的朋友一起看看吧
    2024-02-02
  • 原生echart和vue-echart的使用詳解

    原生echart和vue-echart的使用詳解

    這篇文章主要為大家詳細介紹了原生echart和vue-echart的使用,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • vue中使用refs定位dom出現(xiàn)undefined的解決方法

    vue中使用refs定位dom出現(xiàn)undefined的解決方法

    本篇文章主要介紹了vue中使用refs定位dom出現(xiàn)undefined的解決方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • vue利用插件實現(xiàn)按比例切割圖片

    vue利用插件實現(xiàn)按比例切割圖片

    這篇文章主要為大家詳細介紹了vue利用插件實現(xiàn)按比例切割圖片,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • webpack 3 + Vue2 使用dotenv配置多環(huán)境的步驟

    webpack 3 + Vue2 使用dotenv配置多環(huán)境的步驟

    這篇文章主要介紹了webpack 3 + Vue2 使用dotenv配置多環(huán)境,env文件在配置文件都可以用, vue頁面用的時候需要在 webpack.base.conf.js 重新配置,需要的朋友可以參考下
    2023-11-11
  • Vue仿百度搜索功能

    Vue仿百度搜索功能

    這篇文章主要為大家詳細介紹了Vue仿百度搜索功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • vue小白入門教程

    vue小白入門教程

    vue是一套用于構(gòu)建用戶界面的漸進式框架,本文通過實例給大家介紹了vue入門教程適用小白初學(xué)者,非常不錯,具有參考借鑒價值,需要的朋友參考下吧
    2018-04-04
  • 手把手教你Vue3?按需引入?Echarts的過程(收藏)

    手把手教你Vue3?按需引入?Echarts的過程(收藏)

    新項目采用?Vue3?作為前端項目框架,避免不了要使用?echarts,但是在使用的時候,出現(xiàn)了與?Vue2?使用不一樣的地方,所以特別記下來,希望給到有需要的同學(xué)一些幫助
    2023-10-10

最新評論

庆城县| 大关县| 来宾市| 榆林市| 潜山县| 昌邑市| 来安县| 朔州市| 霍林郭勒市| 长治县| 台中县| 吴忠市| 白水县| 奉节县| 枞阳县| 和政县| 久治县| 南阳市| 肥乡县| 潼南县| 嵊州市| 浪卡子县| 郴州市| 南宫市| 大渡口区| 扶绥县| 酉阳| 什邡市| 山阴县| 德兴市| 格尔木市| 怀集县| 英吉沙县| 玛曲县| 淮安市| 威远县| 教育| 东海县| 宁南县| 丰宁| 桑植县|