diff --git a/Dockerfile b/Dockerfile index 8ed8ce5..d4ac800 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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' \ - 'b40ba5e968e70e7f6d5c9e00c03e25e1 ./resources/views/user/images.blade.php' \ + 'e0b5b669408e8a5f3dee7f332ad21e97 ./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=b40ba5e968e70e7f6d5c9e00c03e25e1" \ + "images_blade_md5=e0b5b669408e8a5f3dee7f332ad21e97" \ "app_src_md5=${APP_SRC_MD5}" \ "app_js_md5=${APP_JS_MD5}" \ "app_css_md5=${APP_CSS_MD5}" \ diff --git a/patches/ios-longpress.patch b/patches/ios-longpress.patch index ec0f818..f2cb98b 100644 --- a/patches/ios-longpress.patch +++ b/patches/ios-longpress.patch @@ -52,7 +52,7 @@ # 三个文件各自的 md5(与 Dockerfile「自证 2」里的期望值一致): # c0e513ec8e93fd81b34b3c6de5cf5eb8 src/resources/js/context-js.js # c0e513ec8e93fd81b34b3c6de5cf5eb8 src/public/js/context-js/context-js.js -# b40ba5e968e70e7f6d5c9e00c03e25e1 src/resources/views/user/images.blade.php +# e0b5b669408e8a5f3dee7f332ad21e97 src/resources/views/user/images.blade.php # # 上游原文件 md5(改动前,供比对): # c8e57f6232848ca8277341ddf1a3a7a6 resources/js/context-js.js diff --git a/src/resources/views/user/images.blade.php b/src/resources/views/user/images.blade.php index 6b70233..afeddba 100644 --- a/src/resources/views/user/images.blade.php +++ b/src/resources/views/user/images.blade.php @@ -535,6 +535,42 @@ class="relative inline-flex h-4 w-7 shrink-0 items-center rounded-full bg-brand }; const viewer = new Viewer(document.getElementById('images-grid'), {url: 'data-original'}); + /* 修 Viewer.js 的「闩锁」bug(真机打点 + 源码逐行核对 + jsdom 复现确认): + * others.js:216-223 change() 里 + * case ACTION_SWITCH: { + * this.action = 'switched'; // ← 第一次 pointermove 就先上闩 + * if (|offsetX| > 1 && |offsetX| > |offsetY|) { … this.view(index) } + * 上闩之后 action 成了字符串 'switched',而外层 switch 没有这个分支 ⇒ 之后 + * 每一次 pointermove 全部落空:不切图、不平移、不放大 =「纹丝不动」。 + * 判据又只看开头那次采样,真手指刚落下天然带纵向抖动(dy ≥ dx)⇒ 大多数手势 + * 一开始就被闩死,偶尔开头偏横向才成功 —— 这就是「大多数失败、少数成功、无规律」。 + * 从外面把闩松开:手势期间发现 action 变成那个闩值,就恢复成刚才那个动作值, + * 后续采样继续参与判定(切图判据看的是"最近一次位移",所以横滑一定能切到)。 + * 不改 viewer.min.js(压缩产物,改了没法维护)。 */ + (function () { + let inViewer = false; + let lastAction = null; + let switchAction = null; + const isViewer = (el) => el instanceof Element && !!el.closest('.viewer-container'); + document.addEventListener('pointerdown', (e) => { + inViewer = isViewer(e.target); + lastAction = null; + }, true); + document.addEventListener('pointermove', (e) => { + if (!inViewer) return; + const a = viewer.action; + if (a === 'switched') { + // 学到切图动作的真值后,把闩松开 + if (!switchAction && lastAction) switchAction = lastAction; + if (switchAction && viewer.action !== switchAction) viewer.action = switchAction; + } else if (a) { + lastAction = a; // 手势开始时 Viewer 刚算出来的动作值 + } + }, true); + document.addEventListener('pointerup', () => { inViewer = false; }, true); + document.addEventListener('pointercancel', () => { inViewer = false; }, true); + })(); + /* 安卓 Chromium:看图控件自己有"模拟双击"逻辑(viewerjs handlers.js:424-451)—— * 两次落在**图片上**的抬手间隔 <500ms,它就 setTimeout 后自己派发一个 dblclick, * 于是放大。真机打点证实:老师连续滑图时凭空出现 dblclick×23 ⇒ 一放大,图片就不再 diff --git a/test/repro-swipe-latch.cjs b/test/repro-swipe-latch.cjs new file mode 100644 index 0000000..1320bb6 --- /dev/null +++ b/test/repro-swipe-latch.cjs @@ -0,0 +1,43 @@ +const {JSDOM}=require('jsdom'); +const fs=require('fs'); +function run(file, moves, label){ + const dom=new JSDOM('


