Open AlexZ33 opened 3 years ago
实现效果:为了防止滑动穿透,需要在弹框的touchmove事件中添加e.preventDefault()阻止默认的行为,但是如果弹窗的内容太长需要滚动,在需要滚动的内容的父级元素上添加 v-scroll即可
基本思路:利用Vue.directive()中的bind钩子,在钩子中对需要滚动的元素绑定touchstart,touchmove和touchend事件,之后利用e.touches判断滚动方向及距离
需要html元素结构满足要求:
1.绑定v-scroll的元素要给高度。 因为为了让内部滚动。。外层元素一定要有高度
2.绑定v-scroll的内部必须是类名为vscroller的节点 在v-scroll的代码中通过这个类来定位需要滚动的元素
<div v-scroll> <div class="vscroller"> 内容 内容 内容 </div> </div>
v-scroll的实现代码
import Vue from 'vue' Vue.directive('scroll', { bind: function(el, binding) { let needScroll = false, scroller = null, oldY = null, totalDetaY = 0, containerH, scrollerH, pageY, detaY, oldTime = null // 获取需要绑定元素及需要滚动的元素 el.style['overflow'] = 'hidden' // 为了避免内容溢出 scroller = el.querySelector('.vscroller') || null // 为滚动内容添加touch事件的监听器 if (scroller) { scroller.addEventListener('touchstart', function(e) { needScroll = isNeedScroll() }) scroller.addEventListener('touchmove', function(e) { e.preventDefault(); if(needScroll) { computePosition(e) handleScroll() } }) scroller.addEventListener('touchend', function(e) { oldY = null }) } // 内部函数 function isNeedScroll() { containerH = el.getBoundingClientRect().height scrollerH = scroller.getBoundingClientRect().height if (containerH >= scrollerH) return false return true } function computePosition(e) { pageY = e.touches[0].pageY detaY = 0 // 计算移动距离, 更新旧的位置坐标 if (!oldY) oldY = pageY detaY = pageY - oldY oldY = pageY } function handleScroll() { totalDetaY += detaY // 判断是否是滑动边界 if (totalDetaY > 0) totalDetaY = 0 if (containerH - totalDetaY > scrollerH) totalDetaY = containerH - scrollerH translate(scroller, 0, totalDetaY) } function translate(target, x, y) { target.style['transform'] = `translate(${x}px, ${y}px)` } } })
实现细节:
1.如果在bind中直接调用 elemetn.getBoundingClientRect() 方法可能会获取不到位置信息,因为bind钩子执行时元素还没被渲染到页面中。所以选择在touch事件的监听器中调用
isNeedScroll() 方法主要用于判断是否需要滚动,如果内容高度不足够的话,那么不需要滚动。同时,这个方法在touchstart中调用,而不在touchmove中,每次滑动只调用一次,有利于提高性能。
通过css translate方法进行位置的更改,性能更好 (相关知识点:合成层?---需要学习一波)
将逻辑拆分成不同方法,以便后续功能扩展(目前只能竖着滑,应该可以通过v-scroll="'h'|'v'"判断滑动方向)
实现效果:为了防止滑动穿透,需要在弹框的touchmove事件中添加e.preventDefault()阻止默认的行为,但是如果弹窗的内容太长需要滚动,在需要滚动的内容的父级元素上添加 v-scroll即可
基本思路:利用Vue.directive()中的bind钩子,在钩子中对需要滚动的元素绑定touchstart,touchmove和touchend事件,之后利用e.touches判断滚动方向及距离
需要html元素结构满足要求:
1.绑定v-scroll的元素要给高度。 因为为了让内部滚动。。外层元素一定要有高度
2.绑定v-scroll的内部必须是类名为vscroller的节点 在v-scroll的代码中通过这个类来定位需要滚动的元素
v-scroll的实现代码
实现细节:
1.如果在bind中直接调用 elemetn.getBoundingClientRect() 方法可能会获取不到位置信息,因为bind钩子执行时元素还没被渲染到页面中。所以选择在touch事件的监听器中调用
isNeedScroll() 方法主要用于判断是否需要滚动,如果内容高度不足够的话,那么不需要滚动。同时,这个方法在touchstart中调用,而不在touchmove中,每次滑动只调用一次,有利于提高性能。
通过css translate方法进行位置的更改,性能更好 (相关知识点:合成层?---需要学习一波)
将逻辑拆分成不同方法,以便后续功能扩展(目前只能竖着滑,应该可以通过v-scroll="'h'|'v'"判断滑动方向)