问题描述
滑动穿透:浮层上的触控会导致底层元素滑动。
问题探究:
1、给body加overflow:hidden,pc端可以锁scroll,移动端无效
pc端可以直接overflow:hidden
解决
2、给body加overflow:hidden及绝对定位,背景会定位到顶部,如果是单屏页面可以,长页面不适用
如果弹出浮层时背景本来就没有滚动距离,可以overflow:hidden
加绝对定位解决
3、禁用touchmove事件,如@touchmove.prevent,对于弹层不需要的滑动的元素来说非常好用,因为scroll是touchmove触发的,直接禁用就不会滑动穿透了,其实是直接就没有系统滑动事件了。但是显然不适合弹层需要滑动的情况
如果弹层时不需要滚动的,可以直接禁用touchmove就可以了
4、专门解决滑动穿透的第三方,存在巨大的兼容性问题。比如tua-body-scroll-lock,android可以完美解决,ios整个屏幕都不能滑动了。高星的body-scroll-lock据说android全挂,就没有试了。
第三方有兼容性问题,可以自己判断ua选用
5、终极解决方案:vant的popup
合理完美的解决方案,不存在兼容问题,适用于任何情况的popup。如果你不想为了锁背景引入一个根本用不到的库,可以一起来研究下popup的实现原理。
原理探究
如果不想看源码想直接知道结论的话可以看这里:
因为常见会滑动穿透的场景都是:
- 子元素本来就不可滚动,在子元素上滑动引起背景滚动,
- 子元素可以滚动,但已经滚动到顶部或者底部,继续滑动的话就会滑动穿透
所以如果子元素本身不可滚动,或者子元素氪滚动,但已经滚动到顶部或者底部时直接对touchmove进行默认事件阻止就可以阻止滑动穿透了。因为scroll事件是通过touchmove触发的,禁止掉就不会触发系统的scroll事件了。这样就可以完美解决可滚动元素可以滚动但其背景在滑动时不为所动的效果了。
如果你想看看popup到底时如何做的可以来看看下面的源码:
源码分析:
src/popup/index.js文件中主要是参数及界面显示的处理。
// src/popup/index.js
import {
createNamespace, isDef } from '../utils';
import {
PopupMixin } from '../mixins/popup';
import Icon from '../icon';
const [createComponent, bem] = createNamespace('popup');
export default createComponent({
// 穿透处理的代码在这里混入
mixins: [PopupMixin],
props: {
round: Boolean,
duration: Number,
closeable: Boolean,
transition: String,
safeAreaInsetBottom: Boolean,
closeIcon: {
type: String,
default: 'cross'
},
closeIconPosition: {
type: String,
default: 'top-right'
},
position: {
type: String,
default: 'center'
},
overlay: {
type: Boolean,
default: true
},
closeOnClickOverlay: {
type: Boolean,
default: true
}
},
beforeCreate() {
const createEmitter = eventName => event => this.$emit(eventName, event);
this.onClick = createEmitter('click');
this.onOpened = createEmitter('opened');
this.onClosed = createEmitter('closed');
},
render() {
if (!this.shouldRender) {
return;
}
const {
round, position, duration } = this;
const transitionName =
this.transition ||
(position === 'center' ? 'van-fade' : `van-popup-slide-${
position}`);
const style = {
};
if (isDef(duration)) {
style.transitionDuration = `${
duration}s`;
}
return (
<transition
name={