问题表象: fix定位的浮层,在浮层上存在滚动内容,触发滚动,在ios上会偶尔触发底层的滚动。
方法1、
给body加overflow:hidden,同时给body的height为100vh。
问题,会让body 内的滚动内容还原到初始位置。
可以在遮罩弹起前记录当时位置,在遮罩关闭后还原。
问题,内容会闪,如果部分遮罩的背景是透明的话,可以明显的看到后面的位置变了。
方法2、
在遮罩弹起后,将所有的滚动事件禁止掉,同时在需要滚动的元素添加特殊attribute,只允许事件对象中带有特定的attribute的元素触发touchmove。
问题,安卓上良好解决,ios上,你在所允许的元素上触发滚动,他有时会触发该遮罩层的滚动,有时会穿透到底层,引起底层的滚动,看起来很恶心,难搞啊。
方法3、
解决方法,禁止所有滚动,手动实现滚动。
封装如下组件,具体方法如下。
思想:
-
didNount禁止所有滚动。
-
unmount取消滚动禁止。
-
touchStart记录该滚动元素初始高度,初始的touch位置。
-
findParent递归向上查询滚动是否在允许滚动的区域内,确认才允许其滚动。
缺点如下:
- 代价较大,本应该用css来实现,但却用一堆js来实现。
- 这种滚动方式也丧失了ios中原有的橡皮筋效果。
- 没有实现多层滚动的逻辑。
import React, {Component} from 'react';
/*
接收props id
解决ios浮层上滚动穿透问题
禁止所有滚动 手动实现滚动
*/
export default class ScrollAllow extends Component {
constructor(props) {
super(props);
this.touchStart = 0; // touchstart的初始高度
this.initStart = 0; // 滚动元素的初始高度
}
// 创建后禁止默认的滑动事件
componentDidMount(){
window.addEventListener('touchmove', this.scrollFun, {passive: false});
window.addEventListener('touchstart', this.touchstart, {passive: false});
}
// moveStart 记录初始滑动的位置
touchstart = event => {
let {id} = this.props;
this.touchStart = event.targetTouches[0].clientY;
var dom = document.getElementById(id);
if(dom) {
this.initStart = dom.scrollTop;
}
};
// 判断所滑动元素是否在该id下 如果不在则不进行滚动
findParent = (dom) => {
let {id} = this.props;
let nodeData = dom.getAttribute('id') === id;
if (nodeData) {
return dom;
} else if (dom.nodeName === 'BODY') {
return false;
}
let parentNode = dom.parentNode;
return this.findParent(parentNode);
};
// 滚动事件记录滚动高度 并手动执行滚动
scrollFun = (event) => {
let {id} = this.props;
var dom = document.getElementById(id);
const clickedDom = event.target;
// 判断该滑动事件是否在此id下
let isThisIdDom = this.findParent(clickedDom);
if(isThisIdDom){
dom.scrollTop = (this.touchStart - event.touches[0].clientY) + this.initStart;
}
return event.preventDefault();
};
// 卸载后恢复滚动事件
componentWillUnmount(){
window.removeEventListener('touchmove', this.scrollFun);
window.removeEventListener('touchstart', this.scrollFun);
}
render() {
let {id} = this.props;
return (
<div id={id}>
{this.props.children}
</div>
);
}
}