移动端禁止页面缩放

一般情况

1
2
3
4
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
/>

iOS Safari

新版本的 Safari 上这种方法并不生效。从 iOS 10 开始, Safari 会忽略 user-scalablemin-scalemax-scale 设置,原因是为了更好的阅读体验。

With the release of iOS 10, we’ve made some minor adjustments to the behavior of our fast tapping, and an important change to a very common user interaction: pinch zooming.

参考:New Interaction Behaviors in iOS 10 | WebKit

preventDefault()

还有其他办法吗?“缩放”的操作一般是双指手势,可以监听 touch 事件,阻止其默认行为

1
2
3
document.addEventListener("touchmove", (e) => {
e.preventDefault();
});

但是!在 ios14.7 实测并不生效,原因:
EventTarget.addEventListener() - Web APIs | MDN

passive
A boolean value that, if true, indicates that the function specified by listener will never call preventDefault(). If a passive listener does call preventDefault(), the user agent will do nothing other than generate a console warning. See Improving scrolling performance with passive listeners to learn more.

some browsers (specifically, Chrome and Firefox) have changed the default value of the passive option to true for the touchstart and touchmove events on the document-level nodes Window, Document, and Document.body

所以,加上 passive: false 就生效了。

1
2
3
4
5
6
7
document.addEventListener(
"touchmove",
(e) => {
e.preventDefault();
},
{ passive: false }
);

但是,这样会同样禁止页面滚动,通常我们不希望这样,加个条件试试:

1
2
3
4
5
6
7
8
9
document.addEventListener(
"touchmove",
(e) => {
if (e.touches.length > 1) {
e.preventDefault();
}
},
{ passive: false }
);

实测发现,两指都接触屏幕后滑动没有问题,但是当一指先滑动再放另一指上去时,就又能缩放了。
实时打印 e.cancelable 属性发现,后一种情况下 cancelable 属性为 false,即无法阻止默认行为,所以此时调用 preventDefault 也就没有效果了。

除了监听 touch 事件, Safari 还有一个特有的 GestureEvent,可以省去上述例子中双指的判断逻辑,但是效果跟上面一样,无法 100% 禁止缩放。

1
2
3
document.addEventListener("gesturestart", (e) => {
e.preventDefault();
});

touch-action

touch-action - CSS(层叠样式表) | MDN
iOS 13 开始,Safari 了支持 touch-action 的 pan-xpan-y 值,可以限制用户的屏幕操作只能进行 x 或 y 方向上的滚动。

1
2
3
body {
touch-action: pan-x pan-y;
}