fix(viewer): 安卓 Chromium 上看图控件触摸判定错乱 —— 拦掉重复的 touch* 事件流

现象:iOS 手机端手势完全正常,安卓(Edge/Chrome)看大图时切图极难触发、放大与拖动也常出问题。

真因(真 Chromium 实测的事件计数与 id):一次单指滑动同时收到 pointerdown ×1 与 touchstart ×1,
两者用的是**不同的 id 体系**(pointerdown: pointerId=5 / touchstart: touch.identifier=0)。
Viewer 的 pointerdown 里:

    if (options.zoomOnTouch && options.zoomable && Object.keys(pointers).length > 1) action = ACTION_ZOOM;

于是同一根手指被记成两个键 ⇒ 被判定成「两根手指捏合」⇒ 动作误派成 ZOOM,切图/平移都不对。
iOS 上两套 id 恰好同值(WebKit 的 pointerId 就是 touch identifier)⇒ 只有一个键 ⇒ 一切正常,
这正解释了为什么只有安卓坏。

修法:在看图容器那层用**捕获阶段**把 touch* 停掉,只留 pointer 一套(捏合缩放照旧走 pointer 路径)。
只影响 .viewer-container 内部,页面其它地方的触摸不受影响。

验收(本机模拟):一刀切图仍正常 ✓;两指捏合仍可用(图片宽 351 → 1193)✓;jsdom 9 套件全绿 ✓。
注:本机模拟复现不出该 bug(模拟器不是真安卓),但上面的事件计数与 id 差异是实测的,
实际效果请老师在安卓真机上确认。
This commit is contained in:
mole404 committed 2026-10-02 23:02:38 +08:00
1 parent af7c730edc
commit 05604dcff2
3 files changed
+17 -3

No files matched your search

+2 -2
View File
@@ -165,7 +165,7 @@ RUN php -r "file_exists('.env') || copy('.env.example', '.env');" \
RUN printf '%s\n' \
'c0e513ec8e93fd81b34b3c6de5cf5eb8 ./public/js/context-js/context-js.js' \
'c0e513ec8e93fd81b34b3c6de5cf5eb8 ./resources/js/context-js.js' \
'27e13507a629aa3ae9f21496a6cf0ebf ./resources/views/user/images.blade.php' \
'cfaad16720f1646fda4426b832e12122 ./resources/views/user/images.blade.php' \
| md5sum -c - \
&& grep -q "assetVersion('js/context-js/context-js.js')" ./resources/views/user/images.blade.php \
&& grep -q 'isIOSWebKit' ./public/js/context-js/context-js.js \
@@ -208,7 +208,7 @@ RUN APP_SRC_MD5=$(find app config routes -type f -print0 2>/dev/null | sort -z |
"fork_sha=${FORK_SHA}" \
"lsky_commit=${LSKY_COMMIT}" \
"context_js_md5=c0e513ec8e93fd81b34b3c6de5cf5eb8" \
"images_blade_md5=27e13507a629aa3ae9f21496a6cf0ebf" \
"images_blade_md5=cfaad16720f1646fda4426b832e12122" \
"app_src_md5=${APP_SRC_MD5}" \
"app_js_md5=${APP_JS_MD5}" \
"app_css_md5=${APP_CSS_MD5}" \
+1 -1
View File
@@ -52,7 +52,7 @@
# 三个文件各自的 md5(与 Dockerfile「自证 2」里的期望值一致):
# c0e513ec8e93fd81b34b3c6de5cf5eb8 src/resources/js/context-js.js
# c0e513ec8e93fd81b34b3c6de5cf5eb8 src/public/js/context-js/context-js.js
# 27e13507a629aa3ae9f21496a6cf0ebf src/resources/views/user/images.blade.php
# cfaad16720f1646fda4426b832e12122 src/resources/views/user/images.blade.php
#
# 上游原文件 md5(改动前,供比对):
# c8e57f6232848ca8277341ddf1a3a7a6 resources/js/context-js.js
+14
View File
@@ -547,6 +547,20 @@ class="relative inline-flex h-4 w-7 shrink-0 items-center rounded-full bg-brand
* (教训:第三方控件遇到根节点 zoom,就把它那层缩回去,别逐条路径打补丁。) */
/* 安卓 Chromium 专属问题:它会**同时**派发 touch* 和 pointer* 两套事件,而 Viewer 把
* 同一个手指记成两个键(touch.identifier vs pointerId)——实测 pointerId=5 / touchId=0
* 不同值。于是它内部 `Object.keys(pointers).length > 1` 成立,把动作误判成 ACTION_ZOOM
* (以为是两根手指捏合)⇒ 手机上切图/放大/拖动全都不稳。iOS 上两套 id 恰好同值,所以没事。
* 修法:在看图容器这层用**捕获阶段**把 touch* 拦下,不让它到 Viewer —— 只留 pointer 一套,
* 捏合缩放照旧走 pointer 路径。只影响看图控件内部,页面其它地方的触摸不受影响。 */
['touchstart', 'touchmove', 'touchend', 'touchcancel'].forEach(type => {
document.addEventListener(type, (e) => {
if (e.target instanceof Element && e.target.closest('.viewer-container')) {
e.stopPropagation();
}
}, true);
});
$photos.justifiedGallery(gridConfigs);
let albumsInfinite = null;