Vue上下滑动抽屉菜单实现
上下拉-抽屉菜单实现
在开发中⼀般我们都使⽤左右侧换菜单,⽽上下抽屉菜单却很少见,今天我们实现⼀个简单的上下拉抽屉菜单。
树仁大学⾸先我们先直观的看下上下拉抽屉菜单的实现效果:
在学习抽屉菜单前我们先学习标签的touch事件。
⼀、事件:
1、标签所对应touch事件:
touchstart事件:当⼿指触摸屏幕时候触发,即使已经有⼀个⼿指放在屏幕上也会触发。
touchmove事件:当⼿指在屏幕上滑动的时候连续地触发。在这个事件发⽣期间,调⽤preventDefault()事件可以阻⽌滚动。
touchend事件:当⼿指从屏幕上离开的时候触发。
touchcancel事件:当系统停⽌跟踪触摸的时候触发。关于这个事件的确切出发时间,⽂档中并没有具体说明,咱们只能去猜测了。
厄勒克特拉
2、⽽每种touch的event事件都包含如下属性:
clientX:触摸⽬标在视⼝中的x坐标。
clientY:触摸⽬标在视⼝中的y坐标。
identifier:标识触摸的唯⼀ID。
pageX:触摸⽬标在页⾯中的x坐标。
pageY:触摸⽬标在页⾯中的y坐标。
screenX:触摸⽬标在屏幕中的x坐标。
screenY:触摸⽬标在屏幕中的y坐标。
target:触⽬的DOM节点⽬标。
3、事件监听处理
a) 声明标签对,并设置ref属性
<div ref="drawer">
...
</div>
b) 通过ref获取当前div标签,对div标签进⾏监听
console.log('====touchstart======', event)
})
this.$refs.drawer.addEventListener('touchmove', (event) => {
console.log('====touchmove======', event)
})
this.$refs.drawer.addEventListener('touchend', (event) => {
console.log('====touchend======', event)
})
this.$refs.drawer.addEventListener('touchcancel', (event) => {
console.log('=====touchcancel=====', event)
})
学习完事件后,接下来就实现抽屉效果。⾸先我们将抽屉布局固定到底部,接下来我们只需要改变抽屉的⾼度即可实现打开抽屉和关闭抽屉效果。
4、实现步骤:
touchstart事件触发时记录⽤户触摸的位置
touchmove事件触发时计算⽤户移动的距离
touchend事件触发时根据⽤户移动的偏移量的正负值判断⽅向,并设置抽屉到打开回或关闭状态
a) touchstart事件触发处理:
this.$refs.drawer.addEventListener('touchstart', (event) => {
//记录点击坐标
防范未然this.startPos.y = event.targetTouches[0].pageY
this.defaultHeight = this.currentHeight
})
b) touchmove事件计算处理
//计算移动距离
if (event.targetTouches.length > 1) {
return
}
let touch = event.targetTouches[0]
let dy = ((this.startPos.y - touch.pageY) * 100) / 667
if (this.isToped && !this.isScollToTop && dy < 0) {
this.currentHeight = Math.max(
this.window.startHeight,
this.defaultHeight + dy
)
console.log(this.currentHeight)
event.preventDefault()
} el if (this.isToped && dy > 0 && this.isScollToTop) { //向上
event.preventDefault()
this.currentHeight = Math.min(
dHeight,
this.defaultHeight + dy
)
console.log(this.currentHeight)
}
hot tokyo
})
c) touchend事件触发时处理:
this.$refs.drawer.addEventListener('touchend', (event) => { console.log('====touchend======', event)
// 阈值 = 差值的20%
const threshold =
Math.abs(this.defaultHeight - this.currentHeight) * 0.2 if (!this.isToped || threshold === 0) {
return
}
const scrollDy = Math.dPos.y - this.startPos.y) const isDirectorTop = dPos.y - this.startPos.y < 0 const isToTop =
(isDirectorTop && scrollDy > threshold) ||
(!isDirectorTop && scrollDy < threshold)
// 60Hz, 16.6ms
let id = tInterval(() => {
if (isToTop) {
this.isScollToTop = fal
this.currentHeight = dHeight
clearInterval(id)
} el {
this.currentHeight = this.window.startHeight
this.isScollToTop = true
clearInterval(id)
}
}, 10)
})
d)⽤户点击头部打开或关闭抽屉事件处理:
openDrawable() {
// 60Hz, 16.6ms
let id = tInterval(() => {
if (this.isScollToTop) {
this.isScollToTop = fal
this.currentHeight = dHeight
clearInterval(id)
} el {
this.currentHeight = this.window.startHeight
this.isScollToTop = true
clearInterval(id)
}
}, 10)
dakota
},
最后附上完整抽屉组件分装代码:
<template>
<div class="VerticalDrawable" : ref="drawer"> <div class="title" @click="openDrawable">{{ title }}</div>
<div class="listview" @scroll.passive="onListScrolled($event)"> <div v-for="(item, index) in list" :key="index">
<div class="listItem">{{ item }}</div>
</div>
</div>
</div>
</template>
<script>
export default {
components: {},
props: {
title: {
type: String,
default: '图书馆借阅记录',
},g queen
list: {
我爱春天type: Array,
default: () => {
return []
},whynot
},
},
data() {
return {
isToped: true, //判断列表的第⼀个元素是否位于顶部
isScollToTop: true, //true 向顶部滑动
defaultHeight: 46, //记录默认⾼度 %
currentHeight: 46, //记录当前窗体⾼度%
window: {
startHeight: 46,
endHeight: 82,
},
startPos: {
y: 0,
},
endPos: {
y: 0,
},
}
},
computed: {
heightStyle() {
heightStyle() {
return {
height: this.currentHeight + '%',
}
},
},
created() {},
mounted() {
this.handleTounchListener()
},
methods: {
openDrawable() {
// 60Hz, 16.6ms
let id = tInterval(() => {
if (this.isScollToTop) {
this.isScollToTop = fal
this.currentHeight = dHeight
clearInterval(id)
} el {
this.currentHeight = this.window.startHeight
this.isScollToTop = true
clearInterval(id)
}
}, 10)
},
i will always love you歌词onListScrolled(event) {
let top = event.target.scrollTop
this.isToped = top === 0
},
handleTounchListener() {
this.$refs.drawer.addEventListener('touchstart', (event) => { //记录点击坐标
this.startPos.y = event.targetTouches[0].pageY
this.defaultHeight = this.currentHeight
})
this.$refs.drawer.addEventListener('touchmove', (event) => { //计算移动距离
if (event.targetTouches.length > 1) {
return
}
let touch = event.targetTouches[0]
let dy = ((this.startPos.y - touch.pageY) * 100) / 667
if (this.isToped && !this.isScollToTop && dy < 0) {
this.currentHeight = Math.max(
this.window.startHeight,
this.defaultHeight + dy
)
console.log(this.currentHeight)
event.preventDefault()
} el if (this.isToped && dy > 0 && this.isScollToTop) {
阿格尼丝//向上
event.preventDefault()
this.currentHeight = Math.min(
dHeight,
this.defaultHeight + dy
)
console.log(this.currentHeight)
}
})
this.$refs.drawer.addEventListener('touchend', (event) => { console.log('====touchend======', event)
// 阈值 = 差值的20%
const threshold =