vue實現(xiàn)滑動和滾動效果
本文實例為大家分享了vue實現(xiàn)滑動和滾動效果的具體代碼,供大家參考,具體內容如下
面板滑動效果,父組件是resultPanel,子組件是resultOption,仿照了iview中,Select組件的寫法。
<template> ? <div v-if="visiable"> ? ? <div class="transparent" :class="{active:resultPanelStatus==='top'}"></div> ? ? <div class="mapbox-result" ? ? ? ? ?ref="resultPanel" ? ? ? ? ?style="z-index: 101;" ? ? ? ? ?@touchstart="onTouchStart" ? ? ? ? ?@touchmove="onTouchMove" ? ? ? ? ?@touchend="onTouchEnd" ? ? ? ? ?:style="slideEffect" ? ? > ? ? ? <div class="mapbox-result-content"> ? ? ? ? <a class="mapbox-result-close" v-if="closable" @click="close"></a> ? ? ? ? <div class="mapbox-result-header"> ? ? ? ? ? <slot name="header"> ? ? ? ? ? ? <div class="mapbox-result-header-title">共找到【{{header}}】相關{{total}}結果</div> ? ? ? ? ? </slot> ? ? ? ? </div> ? ? ? ? <div ? ? ? ? ? class="mapbox-result-body" ? ? ? ? ? ref="resultBody" ? ? ? ? > ? ? ? ? ? <result-option ? ? ? ? ? ? ref="option" ? ? ? ? ? ? v-for="(item, index) in data" ? ? ? ? ? ? :index="index+1" ? ? ? ? ? ? :name="item.name" ? ? ? ? ? ? :meter="item.meter?item.meter:0" ? ? ? ? ? ? :floor-name="item.floorName" ? ? ? ? ? ? :key="index" ? ? ? ? ? ? v-show="visiable" ? ? ? ? ? ? @on-click-gohere="handleNavigate(index)" ? ? ? ? ? ? @on-click-item="focusResultOnMap(index)" ? ? ? ? ? ></result-option> ? ? ? ? </div> ? ? ? </div> ? ? </div> ? </div> </template> <script> ? import resultOption from './resultOption'; ? ? export default { ? ? name: 'result-panel', ? ? components: {resultOption}, ? ? props: { ? ? ? header: { ? ? ? ? type: String ? ? ? }, ? ? ? // value: { ? ? ? // ? type: Boolean, ? ? ? // ? default: true ? ? ? // }, ? ? ? closable: { ? ? ? ? type: Boolean, ? ? ? ? default: true ? ? ? }, ? ? ? data: { ? ? ? ? type: Array, ? ? ? ? default: [] ? ? ? } ? ? }, ? ? data() { ? ? ? return { ? ? ? ? // visiable: true, ? ? ? ? resultPanelStatus: 'normal', ? ?//'normal'、'top' ? ? ? ? cloneData: this.deepCopy(this.data), ? ? ? ? startY: 0, ?// 開始觸摸屏幕的點 ? ? ? ? endY: 0, ? // 離開屏幕的點 ? ? ? ? moveY: 0, ?// 滑動時的距離 ? ? ? ? disY: 0, ?// 移動距離 ? ? ? ? slideEffect: '' ? ? ?//滑動效果 ? ? ? } ? ? }, ? ? mounted() { ? ? ? // this.$refs.resultBody.style.height = `${this.defaultHeight - 60}px`; ? ? ? // this.$refs.resultBody.style.overflowY = 'hidden'; ? ? }, ? ? computed: { ? ? ? total() { ? ? ? ? return this.data.length; ? ? ? }, ? ? ? defaultHeight() { ? ? ? ? return this.data.length > 3 ? 240 : this.data.length * 60 + 60 ? ? ? ?//當結果大于3時,默認只顯示三個 ? ? ? }, ? ? ? visiable() { ? ? ? ? this.resultPanelStatus = 'normal'; ? ? ? ? this.slideEffect = `transform: translateY(-${this.defaultHeight}px); transition: all .5s`; ? ? ? ? return this.$store.state.resultPanel.show; ? ? ? } ? ? }, ? ? methods: { ? ? ? /** ? ? ? ?* 手指接觸屏幕 ? ? ? ?*/ ? ? ? onTouchStart(ev) { ? ? ? ? ev = ev || event; ? ? ? ? // ev.preventDefault(); ? ? ? ? if (ev.touches.length === 1) { ? ? ? ? ? this.startY = ev.touches[0].clientY; ? ? ? ? } ? ? ? }, ? ? ? ? /** ? ? ? ?* 手指滑動 ? ? ? ?*/ ? ? ? onTouchMove(ev) { ? ? ? ? ev = ev || event; ? ? ? ? console.log("ev.target: ", ev.target); ? ? ? ? // ev.preventDefault(); ? ? ? ? if (ev.touches.length === 1) { ? ? ? ? ? let resultPanel = this.$refs.resultPanel.offsetHeight; ? ? ? ? ? this.moveY = ev.touches[0].clientY; ? ? ? ? ? this.disY = this.moveY - this.startY; ? ? ? ? ? if (this.disY < 0 && -this.defaultHeight + this.disY > -resultPanel && this.resultPanelStatus === 'normal') { ?//向上滑動 ? ? ? ? ? ? this.slideEffect = `transform: translateY(${-this.defaultHeight + this.disY}px); transition: all 0s;`; ? ? ? ? ? ? //內容隨著面板上滑出現(xiàn)的動畫 ? ? ? ? ? ? this.$refs.resultBody.style.transition = 'all .5s'; ? ? ? ? ? ? this.$refs.resultBody.style.height = `${this.$refs.resultPanel.offsetHeight - 60}px`; ? ? ? ? ? } else if (this.resultPanelStatus === 'top' && this.disY < 0) { ? ? ? ? ? ? this.scroll(); ? ? ? ? ? } else if (this.disY > 0 && this.resultPanelStatus === 'top') { ? ? ?//向下滑動 /*當手指向下滑動時,如果滑動的起始點不在非內容區(qū)以及scrollTop不為0,則為滾動,否則面板隨著手指滑動并隱藏滾動條,以防止下滑過程中,能夠滾動數(shù)據(jù)*/ ? ? ? ? ? ? if (this.$refs.resultBody.scrollTop > 0 && ev.target !== document.getElementsByClassName("mapbox-result-header")[0]) { ? ? ? ? ? ? ? this.scroll(); ? ? ? ? ? ? } else { ? ? ? ? ? ? ? this.slideEffect = `transform: translateY(${-resultPanel + this.disY}px); transition: all 0s`; ? ? ? ? ? ? ? this.$refs.resultBody.style.overflowY = 'hidden'; ? ? ? ? ? ? } ? //當處于normal狀態(tài),手指向下滑,則下滑 ? ? ? ? ? } else if (this.disY > 0 && this.resultPanelStatus === 'normal') { ? ? ? ? ? ? this.slideEffect = `transform: translateY(${-this.defaultHeight + this.disY}px); transition: all 0s`; ? ? ? ? ? } ? ? ? ? } ? ? ? }, ? ? ? ? /** ? ? ? ?* 離開屏幕 ? ? ? ?*/ ? ? ? onTouchEnd(ev) { ? ? ? ? ev = ev || event; ? ? ? ? // ev.preventDefault(); ? ? ? ? if (ev.changedTouches.length === 1) { ? ? ? ? ? this.endY = ev.changedTouches[0].clientY; ? ? ? ? ? this.disY = this.endY - this.startY; ? ? ? ? ? if (this.disY > 0 && this.resultPanelStatus === 'top') { ? //向下滑動 ? ? ?/*當手指向下滑動時,如果滑動的起始點不在非內容區(qū)以及scrollTop不為0,則為滾動,否則面板滑動到默認位置*/ ? ? ? ? ? ? if (this.$refs.resultBody.scrollTop > 0 && ev.target !== document.getElementsByClassName("mapbox-result-header")[0]) { ?? ? ? ? ? ? ? ? this.scroll(); ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? this.normal(); ? ? ? ? ? ? } //手指離開的時候,出現(xiàn)滾動條,已解決第一次滑動內容的時候,滾動條才會出現(xiàn)而內容沒有滑動的問題 ? ? ? ? ? } else if (this.disY < 0 && this.resultPanelStatus === 'normal') { ? //向上滑動 ? ? ? ? ? ? this.top(); ? ? ? ? ? ? this.move(); ? ? ? ? ? } else if (this.disY < 0 && this.resultPanelStatus === 'top') { ? ? ? ? ? ? this.scroll(); ? ? ? ? ? } else if (this.disY > 0 && this.resultPanelStatus === 'normal') { ? ? ? ? ? ? this.normal(); ? //處于normal狀態(tài)下滑,手指離開屏幕,回歸normal狀態(tài) ? ? ? ? ? } ? ? ? ? } ? ? ? }, ? //當?shù)侥J高度時,設置狀態(tài)為正常狀態(tài),并且隱藏滾動條,將scrollTop置0,以避免內前面的內容被隱藏 ? ? ? normal() { ? ? ? ? // this.$refs.resultBody.style.overflowY = 'hidden'; ? ? ? ? this.slideEffect = `transform: translateY(${-this.defaultHeight}px); transition: all .5s;`; ? ? ? ? this.resultPanelStatus = 'normal'; ? ? ? ? this.$refs.resultBody.scrollTop = 0; ? ? ? }, ? ? ? ? top() { ? ? ? ? this.slideEffect = 'transform: translateY(-100%); transition: all .5s;'; ? ? ? ? this.resultPanelStatus = 'top'; ? ? ? }, ? ? ? ? move() { ? ? ? ? // this.$refs.resultBody.style.height = `${-this.disY + this.defaultHeight}px`; ? ? ? ? this.$refs.resultBody.style.overflowY = 'auto'; ? ? ? }, ? ? ? ? scroll() { ? ? ? ? this.$refs.resultBody.style.overflowY = 'auto'; ? ? ? }, ? ? ? ? close(ev) { ?// click事件會和touchestart事件沖突 ? ? ? ? //當面板處于最高狀態(tài)被關閉時,恢復到正常高度狀態(tài),以避免下次打開仍處于最高處 ? ? ? ? this.normal(); ? ? ? ? // this.$refs.resultBody.scrollTop = 0; ? ? ? ? // this.$refs.resultBody.style.overflowY = 'hidden'; ? ? ? ? this.$store.state.resultPanel.show = false; ? ? ? ? this.$emit('on-cancel'); ? ? ? }, ? ? ? ? handleNavigate(_index) { ? ? ? ? // this.$emit("on-item-click", JSON.parse(JSON.stringify(this.cloneData[_index])), _index); ?//這個是獲取行的元素,和索引 ? ? ? ? this.$emit("on-click-gohere", _index); ?// 這個是獲取索引 ? ? ? }, ? ? ? focusResultOnMap(_index) { ? ? ? ? this.$emit("on-click-item", _index); ?// 這個是獲取索引 ? ? ? }, ? ? ? // deepCopy ? ? ? deepCopy(data) { ? ? ? ? const t = this.typeOf(data); ? ? ? ? let o; ? ? ? ? ? if (t === 'array') { ? ? ? ? ? o = []; ? ? ? ? } else if (t === 'object') { ? ? ? ? ? o = {}; ? ? ? ? } else { ? ? ? ? ? return data; ? ? ? ? } ? ? ? ? ? if (t === 'array') { ? ? ? ? ? for (let i = 0; i < data.length; i++) { ? ? ? ? ? ? o.push(this.deepCopy(data[i])); ? ? ? ? ? } ? ? ? ? } else if (t === 'object') { ? ? ? ? ? for (let i in data) { ? ? ? ? ? ? o[i] = this.deepCopy(data[i]); ? ? ? ? ? } ? ? ? ? } ? ? ? ? return o; ? ? ? }, ? ? ? ? typeOf(obj) { ? ? ? ? const toString = Object.prototype.toString; ? ? ? ? const map = { ? ? ? ? ? '[object Boolean]': 'boolean', ? ? ? ? ? '[object Number]': 'number', ? ? ? ? ? '[object String]': 'string', ? ? ? ? ? '[object Function]': 'function', ? ? ? ? ? '[object Array]': 'array', ? ? ? ? ? '[object Date]': 'date', ? ? ? ? ? '[object RegExp]': 'regExp', ? ? ? ? ? '[object Undefined]': 'undefined', ? ? ? ? ? '[object Null]': 'null', ? ? ? ? ? '[object Object]': 'object' ? ? ? ? }; ? ? ? ? return map[toString.call(obj)]; ? ? ? } ? ? } ? } </script> ? <style type="text/less" scoped> //scoped是指這個樣式只能用于當前組件 ? .transparent { ? ? bottom: 0; ? ? left: 0; ? ? position: absolute; ? ? right: 0; ? ? top: 0; ? ? background-color: rgba(0, 0, 0, 0.3); ? ? opacity: 0; ? ? transition: opacity .3s; ? ? z-index: -1000000000; ? } ? ? .transparent.active { ? ? opacity: 1; ? ? z-index: 0; ? } ? ? .mapbox-result { ? ? height: calc(100% - 2.8vw); ? ? background: #fff; ? ? position: absolute; ? ? font-family: PingFangSC-Regular; ? ? font-size: 12px; ? ? color: #4A4A4A; ? ? bottom: 0; ? ? width: 94.4vw; ? ? margin: 0 2.8vw; ? ? outline: 0; ? ? overflow: auto; ? ? box-sizing: border-box; ? ? top: 100%; ? ? overflow: hidden; ? ? border-radius: 5px 5px 0 0; ? ? box-shadow: 0 0 12px 0px rgba(153, 153, 153, 0.25); ? } ? ? .mapbox-result-content { ? ? position: relative; ? ? background-color: #fff; ? ? border: 0; ? } ? ? .mapbox-result-header { ? ? padding: 24px 10vw; ? ? line-height: 1; ? ? text-align: center; ? } ? ? .mapbox-result-header-title { ? ? white-space: nowrap; ? } ? ? .mapbox-result-close { ? ? position: absolute; ? ? width: 16px; ? ? height: 16px; ? ? background: url('../../assets/close-black@2x.png'); ? ? background-size: 100% 100%; ? ? background-repeat: no-repeat; ? ? right: 5.6vw; ? ? top: 22px ? } ? ? .mapbox-result-body { ? ? height: auto; ? } </style>
<template> ? <div class="mapbox-result-option"> ? ? <div class="mapbox-result-option-content"> ? ? ? <!--<button class="mapbox-btn mapbox-btn-primary mapbox-result-option-btn mapbox-btn-right" @click="handleClick"> ? ? ? ? <i class="mapbox-result-option-icon"></i> ? ? ? </button>--> ? ? ? <a class="mapbox-result-option-nav" @click="handleClick"></a> ? ? ? <div class="mapbox-result-option-item" @click="resultItemClick"> ? ? ? ? <div class="mapbox-result-option-item-main"> ? ? ? ? ? <p class="mapbox-result-option-title"> ? ? ? ? ? ? <span class="mapbox-result-option-order">{{index}}</span> ? ? ? ? ? ? {{name}} ? ? ? ? ? </p> ? ? ? ? ? <p class="mapbox-result-option-note"> ? ? ? ? ? ? {{floorName}},距離當前位置{{meter}}米 ? ? ? ? ? </p> ? ? ? ? </div> ? ? ? </div> ? ? </div> ? </div> </template> <script> ? export default { ? ? name: 'result-option', ? ? props: { ? ? ? value: { ? ? ? ? type: Boolean, ? ? ? ? default: true ? ? ? }, ? ? ? index: { ? ? ? ? type: Number ? ? ? }, ? ? ? name: { ? ? ? ? type: String ? ? ? }, ? ? ? meter: { ? ? ? ? type: Number ? ? ? }, ? ? ? floorName: { ? ? ? ? type: String ? ? ? } ? ? }, ? ? data() { ? ? ? return { ? ? ? } ? ? }, ? ? methods: { ? ? ? handleClick() { ? ? ? ? this.$emit("on-click-gohere"); ? ? ? }, ? ? ? resultItemClick() { ? ? ? ? this.$emit("on-click-item"); ? ? ? } ? ? } ? } ? </script> <style type="text/less" scoped> ? .mapbox-result-option { ? ? height: 60px; ? ? width: calc(100% - 8.3vw); ? ? display: block; ? ? border-bottom: 1px solid #dbd6d6; ? ? box-sizing: border-box; ? ? margin: 0 auto; ? ? overflow: hidden; ? } ? ? .mapbox-result-option-content { ? ? padding: 0; ? ? margin: 0; ? ? font-family: PingFangSC-Regular; ? ? font-size: 12px; ? ? color: #4A4A4A; ? ? position: relative; ? ? display: inline-block; ? ? width: 100%; ? } ? ? .mapbox-btn { ? ? display: inline-block; ? ? margin-bottom: 0; ? ? font-weight: 400; ? ? text-align: center; ? ? vertical-align: middle; ? ? touch-action: manipulation; ? ? background-image: none; ? ? border: 1px solid transparent; ? ? white-space: nowrap; ? ? line-height: 1.5; ? } ? ? .mapbox-result-option-btn { ? ? position: relative; ? ? border-radius: 50%; ? ? height: 30px; ? ? width: 8.3vw; ? ? padding: 0; ? ? outline: none; ? ? margin: 15px 4.2vw 15px 0; ? ? z-index: 1; ?/*避免文字擋住了按鈕*/ ? } ? ? .mapbox-btn-primary { ? ? color: #fff; ? ? background-color: #2A70FE; ? ? border-color: #2A70FE; ? } ? ? .mapbox-btn-right { ? ? float: right; ? ? margin-right: 4.2vw; ? } ? ? .mapbox-result-option-icon { ? ? position: absolute; ? ? top: 50%; ? ? left: 50%; ? ? transform: translate(-50%, -50%); ? ? background-size: 100% 100%; ? ? width: 2.9vw; ? ? height: 18px; ? ? background: url("../../../static/image/icon_nav3.png") no-repeat; ? } ? .mapbox-result-option-nav { ? ? background: url("../../assets/btn_route_planning_normal.png"); ? ? width: 30px; ? ? height: 30px; ? ? background-size: 100% 100%; ? ? background-repeat: no-repeat; ? ? float: right; ? ? display: block; ? ? position: absolute; ? ? right: 0; ? ? top: 15px; ? ? z-index: 1; ? } ? ? .mapbox-result-option-item { ? ? display: block; ? ? position: relative; ? ? margin: 10px auto; ? } ? ? .mapbox-result-option-item-main { ? ? display: block; ? ? vertical-align: middle; ? ? font-size: 16px; ? ? color: #4A4A4A; ? } ? ? .mapbox-result-option-title { ? ? font: 15px/21px PingFangSC-Regular; ? ? position: relative; ? } ? ? .mapbox-result-option-order { ? ? font: 15px/21px PingFangSC-Medium; ? ? position: relative; ? ? margin-left: 1.9vw; ? ? margin-right: 4.6vw; ? } ? ? .mapbox-result-option-note { ? ? font: 12px/16px PingFangSC-Regular; ? ? color: #9B9B9B; ? ? white-space: normal; ? ? position: relative; ? ? margin-left: 12.5vw; ? ? margin-top: 3px; ? } </style>
ev = ev || event,這個寫法是兼容各個瀏覽器,在Firefox瀏覽器中,事件綁定的函數(shù)獲取事件本身,是通過函數(shù)中傳入的,而IE等瀏覽器中,則可以通過window.event或者event的方式來獲取函數(shù)本身。
touchstart和click事件沖突解決: 去掉touchstart,touchmove和touchend事件中的e.preventDefault(); 它會阻止后面事件的觸發(fā);但去掉preventDefault事件會有問題,在微信網頁中打開這個網頁,向下滑動時會觸發(fā)微信的下拉事件,但是在App中應用這組件就不會有這個問題。有一個解決微信網頁中,手指向下滑動觸發(fā)了微信的下拉刷新事件的方法,就是使用setTimeout。
setTimeout(() => {e.preventDefault(); }, ?200);
這樣子可以在click事件發(fā)生后,再阻止之后的默認事件的觸發(fā)。
滾動事件:滾動事件是在touchmove和touchend中觸發(fā)的,面板的上滑事件和滾動事件不同時進行。
上滑時,判斷面板狀態(tài),如果處于top狀態(tài),則觸發(fā)scroll事件,手指離開面板時,仍是scroll事件;如果是處于normal狀態(tài),則是上滑面板,手指離開面板時,設置面板為top狀態(tài),并設置內容的滾動條可見;初始面板上滑到頂部時,第二次上滑面板則會觸滾動條,內容可滾動;
下滑時,判斷是否處于top狀態(tài),如果處于top狀態(tài),當內容區(qū)的scrollTop大于0,且手指初始位置位于內容區(qū),那么就觸發(fā)滾動,否則觸發(fā)面板下滑;當處于normal狀態(tài)時,下滑的話,可以采用不觸發(fā)任何事件,或者可以下滑,但手指離開屏幕時,回歸到默認位置,這里使用了后者的做法。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Vue 解決通過this.$refs來獲取DOM或者組件報錯問題
這篇文章主要介紹了Vue 解決通過this.$refs來獲取DOM或者組件報錯問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07Vue中的Object.defineProperty全面理解
這篇文章主要介紹了Vue中的Object.defineProperty全面理解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-04-04解決Vue2?axios發(fā)請求報400錯誤"Error:?Request?failed?with?s
這篇文章主要給大家介紹了關于如何解決Vue2?axios發(fā)請求報400錯誤"Error:?Request?failed?with?status?code?400"的相關資料,在Vue應用程序中我們通常會使用axios作為網絡請求庫,需要的朋友可以參考下2023-07-07