问题描述: - 用户在 iOS Safari 上快速点击加分/减分按钮时触发页面缩放 - 影响用户体验,导致操作困难 解决方案: 1. 全局事件拦截(index.html) - 拦截 touchstart/touchend 事件,检测快速连续触摸(<350ms) - 完全禁用 dblclick 和 gesture 事件 - 使用 MutationObserver 动态监听 DOM 变化 - 添加 CSS 强制禁用缩放 2. 组件级优化(modify-score.vue) - 使用 touchstart/touchend 替代 click 事件 - 添加 300ms 防抖机制,忽略快速连续触摸 - 实现长按连续加减分功能(500ms 后每 100ms 触发一次) - H5 平台条件编译,添加原生事件监听器 - 清理定时器,防止内存泄漏 3. UniApp 特性应用 - 使用条件编译 #ifdef H5 针对 H5 平台特殊处理 - 利用 $nextTick 确保 DOM 渲染完成后添加事件监听 - 保持跨平台兼容性(小程序、App 不受影响) 技术要点: - touch-action: none 禁用触摸动作 - event.preventDefault() 阻止默认行为 - capture: true 在捕获阶段拦截事件 - passive: false 允许调用 preventDefault() 测试建议: - 在 iOS Safari 上快速点击按钮,验证不再缩放 - 测试长按功能是否正常工作 - 验证其他平台(微信小程序、Android)不受影响 🤖 Generated with Claude Code Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
239 lines
8.4 KiB
HTML
239 lines
8.4 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<!-- 关键:使用最严格的 viewport 设置 -->
|
||
<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"],
|
||
.decrease,
|
||
.increase {
|
||
touch-action: none !important;
|
||
-webkit-user-select: none !important;
|
||
user-select: none !important;
|
||
-webkit-touch-callout: none !important;
|
||
pointer-events: auto !important;
|
||
}
|
||
|
||
/* 允许输入框正常交互 */
|
||
input, textarea {
|
||
touch-action: manipulation !important;
|
||
-webkit-user-select: text !important;
|
||
user-select: text !important;
|
||
}
|
||
|
||
/* 防止页面整体缩放 */
|
||
html, body {
|
||
touch-action: pan-y !important;
|
||
-ms-touch-action: pan-y !important;
|
||
}
|
||
</style>
|
||
<script>
|
||
// UniApp H5 专用:iOS Safari 双击缩放终极解决方案
|
||
(function() {
|
||
'use strict';
|
||
|
||
var lastTouchEnd = 0;
|
||
var touchStartTime = 0;
|
||
var touchCount = 0;
|
||
var resetTimer = null;
|
||
var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
||
|
||
console.log('iOS 检测:', isIOS);
|
||
console.log('User Agent:', navigator.userAgent);
|
||
|
||
// 方案1: 全局拦截 touchstart - 最高优先级
|
||
document.addEventListener('touchstart', function(event) {
|
||
var now = Date.now();
|
||
touchStartTime = now;
|
||
|
||
// 清除重置计时器
|
||
if (resetTimer) {
|
||
clearTimeout(resetTimer);
|
||
}
|
||
|
||
// 检查是否是快速连续触摸
|
||
var timeSinceLastTouch = now - lastTouchEnd;
|
||
|
||
if (timeSinceLastTouch < 350) {
|
||
touchCount++;
|
||
|
||
// 如果是第二次或更多次快速触摸,立即阻止
|
||
if (touchCount >= 1) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
event.stopImmediatePropagation();
|
||
console.log('阻止快速连续触摸', touchCount, timeSinceLastTouch);
|
||
return false;
|
||
}
|
||
} else {
|
||
touchCount = 0;
|
||
}
|
||
|
||
// 600ms 后重置计数器
|
||
resetTimer = setTimeout(function() {
|
||
touchCount = 0;
|
||
}, 600);
|
||
|
||
}, { passive: false, capture: true });
|
||
|
||
// 方案2: 全局拦截 touchend
|
||
document.addEventListener('touchend', function(event) {
|
||
var now = Date.now();
|
||
var touchDuration = now - touchStartTime;
|
||
var timeSinceLastTouch = now - lastTouchEnd;
|
||
|
||
// 如果触摸时间很短(<150ms)且距离上次触摸很近(<350ms),很可能是双击
|
||
if (touchDuration < 150 && timeSinceLastTouch < 350 && timeSinceLastTouch > 0) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
event.stopImmediatePropagation();
|
||
console.log('阻止疑似双击', touchDuration, timeSinceLastTouch);
|
||
return false;
|
||
}
|
||
|
||
lastTouchEnd = now;
|
||
}, { passive: false, capture: true });
|
||
|
||
// 方案3: 完全禁用 dblclick 事件
|
||
document.addEventListener('dblclick', function(event) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
event.stopImmediatePropagation();
|
||
console.log('阻止 dblclick 事件');
|
||
return false;
|
||
}, { passive: false, capture: true });
|
||
|
||
// 方案4: 禁用手势缩放
|
||
document.addEventListener('gesturestart', function(event) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
console.log('阻止 gesturestart');
|
||
}, { 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: 监听 click 事件,过滤快速连续点击
|
||
var lastClickTime = 0;
|
||
document.addEventListener('click', function(event) {
|
||
var now = Date.now();
|
||
var timeSinceLastClick = now - lastClickTime;
|
||
|
||
// 如果距离上次点击小于350ms,阻止
|
||
if (timeSinceLastClick < 350 && timeSinceLastClick > 0) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
event.stopImmediatePropagation();
|
||
console.log('阻止快速连续点击', timeSinceLastClick);
|
||
return false;
|
||
}
|
||
|
||
lastClickTime = now;
|
||
}, { passive: false, capture: true });
|
||
|
||
// 方案6: 针对按钮元素的特殊处理
|
||
function addButtonProtection() {
|
||
var selectors = [
|
||
'.control-btn',
|
||
'.control-btn.decrease',
|
||
'.control-btn.increase',
|
||
'button',
|
||
'[class*="btn"]'
|
||
];
|
||
|
||
selectors.forEach(function(selector) {
|
||
var elements = document.querySelectorAll(selector);
|
||
elements.forEach(function(element) {
|
||
// 移除所有现有的事件监听器(通过克隆节点)
|
||
var newElement = element.cloneNode(true);
|
||
element.parentNode.replaceChild(newElement, element);
|
||
|
||
// 添加新的保护性事件监听器
|
||
['touchstart', 'touchend', 'touchmove', 'click', 'dblclick'].forEach(function(eventType) {
|
||
newElement.addEventListener(eventType, function(e) {
|
||
if (eventType === 'dblclick') {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
e.stopImmediatePropagation();
|
||
return false;
|
||
}
|
||
}, { passive: false, capture: true });
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
// DOM 加载完成后添加按钮保护
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
setTimeout(addButtonProtection, 100);
|
||
// 使用 MutationObserver 监听 DOM 变化
|
||
var observer = new MutationObserver(function(mutations) {
|
||
addButtonProtection();
|
||
});
|
||
observer.observe(document.body, {
|
||
childList: true,
|
||
subtree: true
|
||
});
|
||
});
|
||
} else {
|
||
setTimeout(addButtonProtection, 100);
|
||
}
|
||
|
||
// 方案7: 使用 CSS 强制禁用缩放
|
||
var style = document.createElement('style');
|
||
style.innerHTML = `
|
||
* {
|
||
touch-action: pan-y !important;
|
||
}
|
||
.control-btn,
|
||
.control-btn *,
|
||
button,
|
||
button * {
|
||
touch-action: none !important;
|
||
-webkit-user-select: none !important;
|
||
user-select: none !important;
|
||
-webkit-touch-callout: none !important;
|
||
}
|
||
`;
|
||
document.head.appendChild(style);
|
||
|
||
console.log('iOS Safari 双击缩放防护已启用 - UniApp H5 专用版本');
|
||
})();
|
||
</script>
|
||
</head>
|
||
<body>
|
||
<noscript>
|
||
<strong>请开启JavaScript运行本应用</strong>
|
||
</noscript>
|
||
<div id="app"></div>
|
||
</body>
|
||
</html>
|