Fix iOS Safari double-tap zoom issue on score modification buttons
Problem:
- Rapid tapping on +0.001/-0.001 buttons triggered page zoom on iOS Safari
- Previous solutions (viewport meta, touch-action: manipulation) were ineffective
Solution implemented:
1. Enhanced global touch event handling in index.html:
- Added comprehensive gesture event prevention (gesturestart/change/end)
- Improved touchend debouncing with stopPropagation
- Added specific CSS rules for button elements with touch-action: none
2. Modified button interaction in modify-score.vue:
- Replaced @click events with @touchstart/@touchend handlers
- Added preventDefault and stopPropagation on touch events
- Implemented 100ms debounce to prevent rapid successive touches
- Added pointer-events: none to child elements to ensure touch targets
- Changed touch-action from 'manipulation' to 'none' for complete control
Technical details:
- touch-action: none completely disables browser touch gestures
- Event handlers use { passive: false } to allow preventDefault
- Debounce mechanism prevents accidental double-triggers
- Child elements have pointer-events: none to ensure parent handles all touches
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
36
index.html
36
index.html
@@ -12,22 +12,52 @@
|
|||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 针对按钮元素禁用所有触摸动作 */
|
||||||
|
button, .control-btn, [class*="btn"] {
|
||||||
|
touch-action: none !important;
|
||||||
|
-webkit-user-select: none !important;
|
||||||
|
user-select: none !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
// 禁用 iOS Safari 双击缩放
|
// 更强力的 iOS Safari 双击缩放禁用方案
|
||||||
(function() {
|
(function() {
|
||||||
var lastTouchEnd = 0;
|
var lastTouchEnd = 0;
|
||||||
|
var lastTouchTarget = null;
|
||||||
|
|
||||||
|
// 阻止快速连续触摸导致的缩放
|
||||||
document.addEventListener('touchend', function(event) {
|
document.addEventListener('touchend', function(event) {
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
if (now - lastTouchEnd <= 300) {
|
var timeSinceLastTouch = now - lastTouchEnd;
|
||||||
|
|
||||||
|
// 如果两次触摸间隔小于 300ms,阻止默认行为
|
||||||
|
if (timeSinceLastTouch <= 300 && timeSinceLastTouch > 0) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
lastTouchEnd = now;
|
lastTouchEnd = now;
|
||||||
|
lastTouchTarget = event.target;
|
||||||
}, { passive: false });
|
}, { passive: false });
|
||||||
|
|
||||||
// 禁用双击缩放
|
// 阻止双击事件
|
||||||
document.addEventListener('dblclick', function(event) {
|
document.addEventListener('dblclick', function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}, { passive: false });
|
||||||
|
|
||||||
|
// 阻止手势缩放
|
||||||
|
document.addEventListener('gesturestart', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}, { passive: false });
|
||||||
|
|
||||||
|
document.addEventListener('gesturechange', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}, { passive: false });
|
||||||
|
|
||||||
|
document.addEventListener('gestureend', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
}, { passive: false });
|
}, { passive: false });
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -50,7 +50,11 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="score-control">
|
<view class="score-control">
|
||||||
<view class="control-btn decrease" @touchstart.prevent="decreaseScore" @click.prevent="decreaseScore">
|
<view
|
||||||
|
class="control-btn decrease"
|
||||||
|
@touchstart="handleTouchStart"
|
||||||
|
@touchend="handleDecreaseTouch"
|
||||||
|
>
|
||||||
<text class="btn-symbol">-</text>
|
<text class="btn-symbol">-</text>
|
||||||
<text class="btn-value">-0.001</text>
|
<text class="btn-value">-0.001</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -60,7 +64,11 @@
|
|||||||
<text class="no-modify-text">可不改</text>
|
<text class="no-modify-text">可不改</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="control-btn increase" @touchstart.prevent="increaseScore" @click.prevent="increaseScore">
|
<view
|
||||||
|
class="control-btn increase"
|
||||||
|
@touchstart="handleTouchStart"
|
||||||
|
@touchend="handleIncreaseTouch"
|
||||||
|
>
|
||||||
<text class="btn-symbol">+</text>
|
<text class="btn-symbol">+</text>
|
||||||
<text class="btn-value">+0.001</text>
|
<text class="btn-value">+0.001</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -114,7 +122,9 @@ export default {
|
|||||||
originalScore: 8.000,
|
originalScore: 8.000,
|
||||||
note: '',
|
note: '',
|
||||||
minScore: 5.0,
|
minScore: 5.0,
|
||||||
maxScore: 10.0
|
maxScore: 10.0,
|
||||||
|
lastTouchTime: 0,
|
||||||
|
touchDebounceDelay: 100
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -199,6 +209,44 @@ export default {
|
|||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleTouchStart(e) {
|
||||||
|
// 阻止默认行为,防止触发双击缩放
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDecreaseTouch(e) {
|
||||||
|
// 阻止默认行为和事件冒泡
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
// 防抖处理
|
||||||
|
const now = Date.now()
|
||||||
|
if (now - this.lastTouchTime < this.touchDebounceDelay) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.lastTouchTime = now
|
||||||
|
|
||||||
|
// 执行减分逻辑
|
||||||
|
this.decreaseScore()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleIncreaseTouch(e) {
|
||||||
|
// 阻止默认行为和事件冒泡
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
// 防抖处理
|
||||||
|
const now = Date.now()
|
||||||
|
if (now - this.lastTouchTime < this.touchDebounceDelay) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.lastTouchTime = now
|
||||||
|
|
||||||
|
// 执行加分逻辑
|
||||||
|
this.increaseScore()
|
||||||
|
},
|
||||||
|
|
||||||
decreaseScore() {
|
decreaseScore() {
|
||||||
if (this.currentScore > this.minScore) {
|
if (this.currentScore > this.minScore) {
|
||||||
this.currentScore = parseFloat((this.currentScore - 0.001).toFixed(3))
|
this.currentScore = parseFloat((this.currentScore - 0.001).toFixed(3))
|
||||||
@@ -459,10 +507,11 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.control-btn {
|
.control-btn {
|
||||||
touch-action: manipulation;
|
touch-action: none;
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
width: 140rpx;
|
width: 140rpx;
|
||||||
height: 140rpx;
|
height: 140rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -471,6 +520,7 @@ export default {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: #F5F5F5;
|
background-color: #F5F5F5;
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-btn.decrease {
|
.control-btn.decrease {
|
||||||
@@ -484,6 +534,7 @@ export default {
|
|||||||
.btn-symbol {
|
.btn-symbol {
|
||||||
font-size: 48rpx;
|
font-size: 48rpx;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-btn.decrease .btn-symbol {
|
.control-btn.decrease .btn-symbol {
|
||||||
@@ -497,6 +548,7 @@ export default {
|
|||||||
.btn-value {
|
.btn-value {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
margin-top: 8rpx;
|
margin-top: 8rpx;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-btn.decrease .btn-value {
|
.control-btn.decrease .btn-value {
|
||||||
|
|||||||
Reference in New Issue
Block a user