移动端Picker组件滚动穿透问题踩坑

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

使用移动端组件库Vant的时间选择器时,遇到了这个问题:在弹出来的Picker组件选择时间时,会触发弹层后面页面的滚动,这就非常头疼了,显而易见的Bug。

Bug什么样

先来看看场景吧,这是一个无限加载的可滚动列表,需求是在上滑的时候隐藏表单,显示查询按钮,点击查询时出现表单。

bug1.gif

时间没选完,就触发了后面页面的滚动,原因就是因为这个时间选择器Picker发生了滚动穿透,随即弹层后面页面发生滚动,触发了表单隐藏,这可头疼,时间没法选择。再提出解决方案之前先来看看这个需求实现的过程。

分析一下

为节省篇幅,只保留了关键逻辑,其中showSearch布尔值表示表单的显隐

vue组件内

<template>
<div class="personal">
    <transition name="fade-transform"  mode="out-in" appear>
	<div class="search-container" v-if="showSearch">
            <div class="search-form">
                <van-form :model="form" ref="form" :label-style="{color:'#636B7E',fontSize:'13px'}">
                    <van-field v-model="form.bxcrxm" placeholder="请输入姓名" clearable />
                        <van-row type="flex" justify="space-between" align="center">
                           <van-col :span="11">
                                <van-field
                                    clickable
                                    readonly
                                    placeholder="开始时间"
                                    @click="showStartPicker = true"
                                    />
                                    <van-popup v-model="showStartPicker" position="bottom">
                                        <van-datetime-picker
                                            type="datetime"
                                            @cancel="showStartPicker = false"
                                        />
                                      </van-popup>
                            </van-col>
                            <van-col :span="1" >
                                        <div class="line">
                                        </div>
                                </van-col>
                                <van-col :span="11">
                                        <van-field
                                            readonly
                                            clickable
                                            placeholder="结束时间"
                                            @click="showEndPicker = true"
                                         />
                                        <van-popup v-model="showEndPicker" position="bottom">
                                        <van-datetime-picker
                                            type="datetime"
                                            @cancel="showEndPicker = false"
                                        />
                                        </van-popup>
                            </van-col>
                        </van-row>
                </van-form>
             </div>
       </div>
    </transition>
     <div class="list-container">
         //列表容器
     </div>
   </div>
   </template>
<script>
 import {touchStart,touchEnd}  from '@/utils/touch'
 export default {
     data(){
         showSearch:true,
         showStartPicker:false
     },
     mounted () {
         this.bindTouch()
     },
     methods:{
        bindTouch(){
            document.querySelector('.personal').addEventListener('touchstart', touchStart)
            document.querySelector('.personal').addEventListener('touchend', (e)=> { touchEnd(e ,this)})
	},
     }
 }
</script>
复制代码

引入下文提到的touch.js,Dom初始化完成后给根元素添加触摸监听,上滑的时候改变ShowSearch的值,这里有个小技巧,利用箭头函数将this传递进去,需求做完了,但没完全做完。该处理我们说的Bug了

touch.js

单独维护的js文件去判断相关手指滑动逻辑,放在utils目录下。

var touchStartX = null
var touchStartY = null
export const touchStart=(e)=> {
    touchStartX = e.touches[0].clientX;
    touchStartY = e.touches[0].clientY;
}
export const touchEnd=(e,that)=> {
    let deltaX = e.changedTouches[0].clientX - touchStartX;
    let deltaY = e.changedTouches[0].clientY - touchStartY;
    if (Math.abs(deltaX) > 50 && Math.abs(deltaX) > Math.abs(deltaY)) {
        if (deltaX >= 0) {
            //左滑
        } else {
            //右滑
        }
    } else if (Math.abs(deltaY) > 50 && Math.abs(deltaX) < Math.abs(deltaY)) {
        if (deltaY < 0) {
            //上滑
            that.showSearch = false
        } else {
        }
    } else {
    }
}
复制代码

可以看到在touchEnd函数中传入了上滑的逻辑里对Vue文件中的showSearch进行了更改。

解决掉它

前面是根据手指的滑动改变showSearch的值,并没有考虑表单内部的交互。再回到这个Bug上,既然是在显示了Picker组件后产生的,那么就从这里解决, 准备一个 showStartPicker(开始时间Picker显隐布尔值,结束时间同理)数据,在模版中使用,并动态的改变。这样就可以知道Picker组件的状态了(显示或者隐藏),于是就可以在时间选择器打开Picker弹层的时候给后面页面解绑触摸滚动事件,关闭Picker弹层的时候注册相应事件。

监听showStartPicker,完成上述操作。

扫描二维码关注公众号,回复: 13155157 查看本文章
 watch: {
    showStartPicker(newVal){
        if(newVal) {
                this.unBindTouch()
        }else{
                this.bindTouch()
        }
    }
}
复制代码

最终效果

result.gif

如果有更简单的方法,欢迎在评论区提出。

猜你喜欢

转载自juejin.im/post/7013600126715461640