const MOBILE_POINTER_QUERY = '(pointer: coarse)'; const QUICK_TAP_INTERVAL_MS = 300; function shouldLockMobileViewportZoom() { if (typeof window === 'undefined' || typeof navigator === 'undefined') { return false; } return ( navigator.maxTouchPoints > 1 || window.matchMedia(MOBILE_POINTER_QUERY).matches ); } /** * 中文注释:主站在移动端采用游戏式固定画布,不能让浏览器把整页缩放。 * 单指滚动、点击仍交给页面自身处理,只拦截多指捏合和快速双击触发的页面级缩放。 */ export function lockMobileViewportZoom() { if ( typeof document === 'undefined' || !shouldLockMobileViewportZoom() || document.documentElement.dataset.mobileViewportZoomLocked === 'true' ) { return; } document.documentElement.dataset.mobileViewportZoomLocked = 'true'; const nonPassiveOptions: AddEventListenerOptions = { passive: false }; let lastTouchEndAt = 0; const preventMultiTouchZoom = (event: TouchEvent) => { if (event.touches.length > 1) { event.preventDefault(); } }; const preventBrowserGestureZoom = (event: Event) => { event.preventDefault(); }; const preventQuickTapZoom = (event: TouchEvent) => { const now = Date.now(); if (now - lastTouchEndAt < QUICK_TAP_INTERVAL_MS) { event.preventDefault(); } lastTouchEndAt = now; }; document.addEventListener('touchmove', preventMultiTouchZoom, nonPassiveOptions); document.addEventListener('touchend', preventQuickTapZoom, nonPassiveOptions); document.addEventListener('gesturestart', preventBrowserGestureZoom, nonPassiveOptions); document.addEventListener('gesturechange', preventBrowserGestureZoom, nonPassiveOptions); document.addEventListener('gestureend', preventBrowserGestureZoom, nonPassiveOptions); }