Files
martial-admin-mini/index.html
DevOps 56c1320e40 Fix iOS Safari double-tap zoom issue with comprehensive solution
Implemented multiple layers of protection to prevent iOS Safari from zooming when users quickly tap the score adjustment buttons:

1. Enhanced touch event handling in modify-score.vue:
   - Changed from touchend to touchstart for immediate response
   - Added .stop.prevent modifiers to all touch events (touchstart, touchmove, touchend, touchcancel)
   - Added noop() handlers to absorb unwanted events
   - Replaced time-based debouncing with isProcessing flag using requestAnimationFrame
   - Ensured all child elements have pointer-events: none

2. Comprehensive index.html protection:
   - Added iOS-specific meta tags (apple-mobile-web-app-capable, format-detection)
   - Enhanced CSS with touch-action: pan-y for scrolling while preventing zoom
   - Implemented 7-layer JavaScript protection:
     * Layer 1: Intercept rapid touchstart events with counter
     * Layer 2: Block touchend events within 300ms
     * Layer 3: Completely disable dblclick events
     * Layer 4: Prevent gesture events (gesturestart/change/end)
     * Layer 5: Use Pointer Events API for additional blocking
     * Layer 6: Filter rapid click events
     * Layer 7: Add capture-phase listeners to buttons
   - All event listeners use { passive: false, capture: true } for maximum control

This multi-layered approach addresses the root cause: iOS Safari triggers zoom at the browser level before JavaScript can normally intercept it. By using capture phase and preventing events at multiple stages, we ensure the zoom behavior is blocked.

Generated with Claude Code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-24 01:13:55 +08:00

170 lines
5.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, viewport-fit=cover">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="format-detection" content="telephone=no">
<title>武术评分系统</title>
<link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />
<style>
* {
touch-action: pan-y !important;
-webkit-touch-callout: none !important;
-webkit-tap-highlight-color: transparent !important;
-webkit-user-select: none !important;
user-select: none !important;
}
/* 针对按钮元素完全禁用触摸动作 */
button, .control-btn, [class*="btn"], [class*="control"] {
touch-action: none !important;
-webkit-user-select: none !important;
user-select: none !important;
-webkit-touch-callout: none !important;
}
/* 允许输入框正常交互 */
input, textarea {
touch-action: manipulation !important;
-webkit-user-select: text !important;
user-select: text !important;
}
</style>
<script>
// 终极 iOS Safari 双击缩放禁用方案
(function() {
'use strict';
var lastTouchEnd = 0;
var lastTouchStart = 0;
var touchCount = 0;
var resetTimer = null;
// 方案1: 拦截所有快速连续的触摸事件
document.addEventListener('touchstart', function(event) {
var now = Date.now();
// 重置计数器
if (resetTimer) {
clearTimeout(resetTimer);
}
// 如果距离上次触摸结束小于300ms增加计数
if (now - lastTouchEnd < 300) {
touchCount++;
// 如果是第二次或更多次快速触摸,阻止默认行为
if (touchCount >= 1) {
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
}
} else {
touchCount = 0;
}
lastTouchStart = now;
// 500ms后重置计数器
resetTimer = setTimeout(function() {
touchCount = 0;
}, 500);
}, { passive: false, capture: true });
// 方案2: 拦截touchend事件
document.addEventListener('touchend', function(event) {
var now = Date.now();
var timeSinceLastTouch = now - lastTouchEnd;
// 如果两次触摸间隔小于300ms阻止默认行为
if (timeSinceLastTouch <= 300 && timeSinceLastTouch > 0) {
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
}
lastTouchEnd = now;
}, { passive: false, capture: true });
// 方案3: 完全禁用双击事件
document.addEventListener('dblclick', function(event) {
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
return false;
}, { passive: false, capture: true });
// 方案4: 禁用手势缩放
document.addEventListener('gesturestart', function(event) {
event.preventDefault();
event.stopPropagation();
}, { passive: false, capture: true });
document.addEventListener('gesturechange', function(event) {
event.preventDefault();
event.stopPropagation();
}, { passive: false, capture: true });
document.addEventListener('gestureend', function(event) {
event.preventDefault();
event.stopPropagation();
}, { passive: false, capture: true });
// 方案5: 使用 Pointer Events 拦截
if (window.PointerEvent) {
var lastPointerUp = 0;
document.addEventListener('pointerup', function(event) {
var now = Date.now();
if (now - lastPointerUp < 300) {
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
}
lastPointerUp = now;
}, { passive: false, capture: true });
}
// 方案6: 监听 click 事件,过滤掉快速连续的点击
var lastClickTime = 0;
document.addEventListener('click', function(event) {
var now = Date.now();
var timeSinceLastClick = now - lastClickTime;
// 如果距离上次点击小于300ms可能是双击导致的阻止
if (timeSinceLastClick < 300 && timeSinceLastClick > 0) {
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
return false;
}
lastClickTime = now;
}, { passive: false, capture: true });
// 方案7: 禁用特定元素的默认行为
document.addEventListener('DOMContentLoaded', function() {
// 为所有按钮添加额外的事件监听
var buttons = document.querySelectorAll('button, .control-btn, [class*="btn"]');
buttons.forEach(function(btn) {
btn.addEventListener('touchstart', function(e) {
e.stopPropagation();
}, { passive: false, capture: true });
});
});
console.log('iOS Safari 双击缩放防护已启用');
})();
</script>
</head>
<body>
<noscript>
<strong>请开启JavaScript运行本应用</strong>
</noscript>
<div id="app"></div>
</body>
</html>