fix(viewer): 安卓上关掉「触摸捏合缩放」,绕开"单指被当成两指"的手势误判

现象:iOS 手机端手势完全正常;安卓(Edge/Chrome)看大图时切图极难触发、放大与拖动都不稳。
用户观感「单指被判定成双指」,与 Viewer 内部 `Object.keys(pointers).length > 1` 触发
ACTION_ZOOM 的表现一致。

已有的排查结论(都实测过):
  · 官方演示页在老师安卓机上正常 ⇒ 差异在我们自己的页面/环境,不是库本身在安卓上不能用
  · 本机 Chromium 模拟安卓复现不出来:两引擎、真手指速度、三种距离、hover:none、触摸+指针
    全量探针、touch-action 计算值链、pointercancel、页面滚动 —— 全部正常
  · 页面注册的 pointer 监听只有 Viewer 自己的(另一个是 Playwright 测试框架注入的)
  · Viewer 在触摸环境下绑的是 pointer 事件(实测 .viewer-canvas 上 pointerdown,
    document 上 pointermove/pointerup/pointercancel)
故不做无法验证的底层修补,改为**中和**这条误判路径:

    zoomOnTouch: !/Android/i.test(navigator.userAgent)

安卓上单指只可能被解释成平移/切图(放大仍可用工具栏 ± 与双击),iOS 行为完全不变。

同时撤掉上一版在捕获阶段拦 touch* 的代码:实测 Viewer 只绑 pointer 事件,那段是空拦;
且它会挡住 context-js 菜单守卫的触摸(点空白关菜单会失效),属回归,故删除。

本机验收(安卓 UA 模拟):100/160/260px 三条滑动全部正常切图 ✓;捏合缩放按设计在安卓关闭 ✓。
真机效果仍需老师在安卓上确认。
This commit is contained in:
mole404 committed 2026-10-02 23:24:13 +08:00
1 parent 05604dcff2
commit 53edb1ffb3
3 files changed
+14 -18

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' \
'cfaad16720f1646fda4426b832e12122 ./resources/views/user/images.blade.php' \
'25a2aac1ce72ddae3b99ce26cbb6df05 ./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=cfaad16720f1646fda4426b832e12122" \
"images_blade_md5=25a2aac1ce72ddae3b99ce26cbb6df05" \
"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
# cfaad16720f1646fda4426b832e12122 src/resources/views/user/images.blade.php
# 25a2aac1ce72ddae3b99ce26cbb6df05 src/resources/views/user/images.blade.php
#
# 上游原文件 md5(改动前,供比对):
# c8e57f6232848ca8277341ddf1a3a7a6 resources/js/context-js.js
+11 -15
View File
@@ -533,7 +533,11 @@ class="relative inline-flex h-4 w-7 shrink-0 items-center rounded-full bg-brand
+ escapeHtml(tag.name) + '</span>'
).join('');
};
const viewer = new Viewer(document.getElementById('images-grid'), {url: 'data-original'});
const viewer = new Viewer(document.getElementById('images-grid'), {
url: 'data-original',
// 仅安卓关闭"触摸捏合缩放":见上方注释(安卓上单指会被错记成两指)
zoomOnTouch: !/Android/i.test(navigator.userAgent),
});
/* 桌面 110% 缩放下看图控件(Viewer.js)的整体偏移:**不在这里用 JS 打补丁**。
* 真因:<html>{zoom:1.1} 让 Viewer 内部「以为的 1px」只有屏幕上的 1/1.1 ——
@@ -547,20 +551,12 @@ 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);
});
/* 安卓 Chromium 上看大图的手势会被误判:实测该环境下 Viewer 的手势状态里,
* 单指常被记成"两个指针"(用户侧的观感就是「单指被判成双指」),于是它的
* `Object.keys(pointers).length > 1` 成立、动作被派成 ACTION_ZOOM(捏合缩放),
* 表现为切图极难触发、放大与拖动都不稳。iOS 上不会出现,官方演示页在安卓上也正常,
* 说明是这台环境下的事件重复计数。处理:只在安卓关掉"触摸捏合缩放",
* 让单指只可能被解释成平移/切图(放大仍可用工具栏的 ± 与双击)。 */
$photos.justifiedGallery(gridConfigs);
let albumsInfinite = null;