IOS浏览器禁用橡皮筋效果
橡皮筋效果是指什么?
使用 IOS 端 浏览器打开网页,在滚动条滚动到顶部之后,手指继续向下滑动,页面依旧会被下拉,且被强制下拉出来的部分是空白的,且下拉到一定程度会产生页面刷新
哪些地方会出现橡皮筋效果
在 IOS 端凡属滚动容器都会出现橡皮筋效果,不过 body 的橡皮筋效果,除了橡皮筋的回弹效果外,如果继续下拉到一定程度会产生页面刷新。而其他非 body 的滚动容器的橡皮筋效果,只会有下拉回弹,并不会产生页面刷新
如何触发橡皮筋效果
必须同时满足以下两个条件
- 必须是滚动容器
- 下拉到顶部之后继续下拉
如何禁用橡皮筋效果?
如果不考虑兼容性问题,css 大法即可解决:
css
body {
/* 禁用默认的下拉刷新和边界效果,但是依然可以进行滑动导航 */
overscroll-behavior-y: none;
}
can i use: overscroll-behavior-y
既然是滚动容器就会有橡皮筋效果,那么只要容器不是滚动容器就不会触发橡皮筋效果了。
是否感觉上面这段话是无意义的?非也。
禁用 body 的橡皮筋效果
直接将 body 设置为非滚动容器, 此时 body 就没有橡皮筋效果了。但 body 的子元素如果包含滚动容器,那么那个滚动容器的橡皮筋效果依然存在
css
body {
overflow: hidden;
}
禁用非 body 的橡皮筋效果
知道了橡皮筋效果的触发条件,那我们使条件不满足不就可以了。
方式一: 通过阻止默认行为,来禁止滚动,从而达到禁用橡皮筋效果的目的
缺陷:
- 较为复杂
- 当滚动条初始停留在接近顶部的地方。如: scrollTop 为 5px 的位置,此时往下滑动很长一段距离,还是会有橡皮筋效果
WARNING
方式一有一点点缺陷
ts
/**
* 判断是否滚动到了最底部
*/
function isScrolledBottom(scrollHeight: number, clientHeight: number, scrollTop: number) {
return Math.abs(scrollHeight - clientHeight - scrollTop) < 1
}
/**
* 判断滚动容器是否滚动到了最底部
*/
function validateElementIsScrolledBottom(element: HTMLElement) {
const { scrollHeight, clientHeight, scrollTop } = element
return isScrolledBottom(scrollHeight, clientHeight, scrollTop)
}
interface TouchEventXPJ extends TouchEvent {
/**
* 是否应该支持滚动
*/
isNeedScroll: boolean
}
function disableEffectXPJ(needScrollDom: HTMLElement) {
// needScrollDom.addEventListener('touchmove', function (e: TouchEvent) {
// const evt = e as TouchEventXPJ
// evt.isNeedScroll = true
// })
let startY = 0
document.body.addEventListener(
'touchstart',
e => {
startY = e.touches[0].pageY
},
{ passive: false }
)
document.body.addEventListener(
'touchmove',
function (evt: TouchEvent) {
const e = evt as TouchEventXPJ
if (!e.isNeedScroll) {
// 无需支持滚动,则通过阻止默认行为,达到阻止滚动的目的
e.preventDefault()
} else {
//需要滑动的区域
const { touches } = e
const moveY = touches[0].pageY
const { scrollTop } = el
/**
* 手指是否在向下滑动
*/
const isPositive = moveY > startY
/**
* 滚动条是否滚动到了最底部
*/
const isScrolledEnd = validateElementIsScrolledBottom(el)
if ((scrollTop <= 0 && isPositive) || (isScrolledEnd && !isPositive)) {
// 已经滚动到了顶部手指还在向下滑动,或者已经滚动到了底部手指还在向上滑动,则阻止默认事件
e.preventDefault()
}
}
},
{ passive: false }
) //passive防止阻止默认事件不生效
}
方式二:将滚动容器在允许滚动和禁止滚动之间动态切换
ts
function onScroll(evt: UIEvent) {
const scrollDom = evt.target as HTMLElement
if (!scrollDom) return
const { scrollTop } = scrollDom
if (scrollTop <= 0) {
// 已经滑动到了顶部,则将容器变为非滚动容器,用于避免出现橡皮筋效果
scrollDom.style.overflow = 'hidden'
setTimeout(() => {
// 再将容器恢复为允许滚动
scrollDom.style.overflow = 'auto'
})
}
}
其他补充
IOS 的 safari 滚动卡顿?
试试将如下 css 添加到滚动容器中
css
.scrollDom {
/* 该属性用于解决IOS滚动卡顿,
如果滚动容器的层级之上有漂浮的绝对定位元素,
那么需要对绝对定位元素设置z-index值,
否则可能会导致滚动容器的内容显示再绝对定位元素的上层 */
-webkit-overflow-scrolling: touch;
}
-webkit-overflow-scrolling :控制元素在移动设备上是否使用滚动回弹效果。 auto :使用普通滚动, 当手指从触摸屏上移开,滚动会立即停止。 touch :使用具有回弹效果的滚动, 当手指从触摸屏上移开,内容会继续保持一段时间的滚动效果,继续滚动的速度和持续的时间和滚动手势的强烈程度成正比。
需要注意的事项:
通过动态添加内容撑开容器,结果根本不能滑动。 在 safari 上,点击其他区域,再在滚动区域滑动,滚动条无法滚动。 在 safari 上,使用了-webkit-overflow-scrolling:touch 之后,页面偶尔会卡住不动。
解决问题的方法:
第一步:将使用-webkit-overflow-scrolling
的元素的定位取消或手动改成 position:static;
第二步:将使用-webkit-overflow-scrolling 的元素添加一个子元素,
设置子元素的高度为 height:101%,或者为了精确一些写成
height:calc(100vh + 1px), 注意这个加号前后都要有空格.
参考资料
vue 结合 preventDefault()和页面滚动高度计算,解决 ios 橡皮筋效果
H5 下拉刷新(解决与 android 微信端下拉冲突、与页面滚动冲突)