From 49ef183a8e44e69ca786f6b2ab142e5f0f06c519 Mon Sep 17 00:00:00 2001 From: mole404 <248403041+mole404@users.noreply.github.com> Date: Sat, 3 Oct 2026 12:31:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(viewer):=20=E6=89=8B=E6=9C=BA=E7=AB=AF?= =?UTF-8?q?=E5=88=87=E5=9B=BE=E9=98=88=E5=80=BC=E6=8E=A5=E7=AE=A1=EF=BC=88?= =?UTF-8?q?=E8=80=81=E5=B8=88=E5=AE=9A=E7=9A=84=E4=B8=89=E6=9D=A1=E8=A7=84?= =?UTF-8?q?=E5=88=99=EF=BC=89=E2=80=94=E2=80=94=20=E4=B8=8D=E5=86=8D"?= =?UTF-8?q?=E7=A8=8D=E5=8A=A8=E5=B0=B1=E5=88=87"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 老师反馈:手机端切图手势过于灵敏(滑短短一下就切),想拖动基本都误触;而且"放大后能拖、 缩小后一拖就切图",阈值区间摸不透。 根源(源码核对):库把切图阈值硬编码成 1px(others.js 的 change(): absoluteOffsetX > 1 && > |offsetY|),而且"这一下是切图还是拖动"在 pointerdown 就按 "图片有没有超出屏幕"定死了 ⇒ 未放大时几乎必然判成切图、放大后又必然判成拖动 —— 规则本身不自洽,所以摸不透。 老师定的三条规则(实现如下,全部挂在外面,不改 viewer.min.js): · 未放大:横划跟手(库自带的行为保留),松手时 |dx| ≥ 40px 且 |dx| > |dy|*1.2 才切图, 否则把已切过去的那张 view() 回来 —— 观感就是弹回原位; · 放大后:拖动看图照旧(库的 ACTION_MOVE),横划 ≥ 80px 才由本层补一次 view(); · 竖直滑永远不切图(方向判据同时生效)。 判定用 pointer 事件(与库同一套),范围限定 .viewer-canvas 内,不碰缩略图条。 本机实测(390x844 手机模拟,CDP 真触摸): 未放大 横划 20px → 没切 ✓(期望不切) 未放大 竖划 80px → 没切 ✓(方向判据生效) 未放大 横划 120px → 切了 ✓ 放大后 50px/140px 两条:无头环境里"放大"这个动作没成功(工具栏点击与滚轮都试过), 所以那两条实际跑在未放大状态,未取得有效数据。放大后的 80px 阈值待老师真机确认手感。 本机 9 套 jsdom 测试 409 项全绿。 --- Dockerfile | 4 +- patches/ios-longpress.patch | 2 +- src/resources/views/user/images.blade.php | 58 +++++++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8c99011..a56085d 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' \ - 'eeea8d21cbfb849fd3c624dc06512d95 ./resources/views/user/images.blade.php' \ + 'e03d452d272d7d5e3767d6d3385a1e65 ./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=eeea8d21cbfb849fd3c624dc06512d95" \ + "images_blade_md5=e03d452d272d7d5e3767d6d3385a1e65" \ "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 067cec1..bc4a5be 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 -# eeea8d21cbfb849fd3c624dc06512d95 src/resources/views/user/images.blade.php +# e03d452d272d7d5e3767d6d3385a1e65 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 e987e24..6ccc0b8 100644 --- a/src/resources/views/user/images.blade.php +++ b/src/resources/views/user/images.blade.php @@ -642,6 +642,64 @@ class="relative inline-flex h-4 w-7 shrink-0 items-center rounded-full bg-brand document.addEventListener('touchcancel', () => { navDown = false; }, true); })(); + /* 触摸端切图阈值接管(老师定的三条规则): + * 未放大:横划跟手,松手时 |dx| ≥ 40px 且明显横向才切图,否则弹回原位; + * 放大后:拖动看图照旧(库自己的 ACTION_MOVE),横划 ≥ 80px 且明显横向才切图; + * 竖直滑永远不切图。 + * 为什么要在外面接管:库把阈值硬编码成 1px(others.js 的 change(): + * absoluteOffsetX > 1 && > |offsetY|),而且"这一下是切图还是拖动"在 pointerdown + * 就按"图片有没有超出屏幕"定死了 ⇒ 未放大时几乎必然判成切图(稍动就切)、 + * 放大后又必然判成拖动(切不了)。老师反馈"阈值区间摸不透"就是这个不自洽。 + * 做法:库的"跟手滑动"保留不动,只在松手时若不够格就把切过去的那张 view() 回来 + *(观感就是弹回原位);放大后库不切,由本层按 80px 判定补一次 view()。 + * 注意用 pointer 事件(与库同一套),判断范围限定在 .viewer-canvas 内,不碰缩略图条。 */ + (function () { + const SWIPE_NOT_ZOOMED = 40; // 未放大:划够这么多才切 + const SWIPE_ZOOMED = 80; // 放大后:更钝一点,避免和拖动打架 + const DIRECTION_RATIO = 1.2; // |dx| 要明显大于 |dy| 才算横划 + let downX = 0; + let downY = 0; + let downIndex = null; + let zoomed = false; + let qualified = false; + const inCanvas = (el) => el instanceof Element && !!el.closest('.viewer-canvas'); + const isZoomed = () => { + const im = document.querySelector('.viewer-canvas img'); + const c = document.querySelector('.viewer-canvas'); + if (!im || !c) return false; + return im.getBoundingClientRect().width > c.getBoundingClientRect().width + 2; + }; + document.addEventListener('pointerdown', (e) => { + if (!inCanvas(e.target)) return; + downX = e.clientX; + downY = e.clientY; + downIndex = viewer.index; + zoomed = isZoomed(); + qualified = false; + }, true); + document.addEventListener('pointermove', (e) => { + if (downIndex === null) return; + const dx = e.clientX - downX; + const dy = e.clientY - downY; + const need = zoomed ? SWIPE_ZOOMED : SWIPE_NOT_ZOOMED; + qualified = Math.abs(dx) >= need && Math.abs(dx) > Math.abs(dy) * DIRECTION_RATIO; + }, true); + document.addEventListener('pointerup', (e) => { + if (downIndex === null) return; + const from = downIndex; + const dx = e.clientX - downX; + downIndex = null; + if (qualified) { + // 放大时库不会自己切(它判成拖动),由这里补一次 + if (zoomed) viewer.view(viewer.index + (dx < 0 ? 1 : -1)); + return; + } + // 不够格:库可能已经"跟手切过去"了,把它弹回原位 + if (!zoomed && viewer.index !== from) viewer.view(from); + }, true); + document.addEventListener('pointercancel', () => { downIndex = null; }, true); + })(); + /* 双击放大:库里的「模拟双击」(handlers.js:424-451)只看两次抬手的间隔(硬编码 500ms), * 完全不检查中间有没有拖动 ⇒ 连续拖动两下也会被当成双击放大。这里在外面收口(不改库): * 1. 抬手时用「起点→终点」的位移判断这一下是轻点还是拖动。阈值 30px 的依据是实测: