diff --git a/Dockerfile b/Dockerfile index 1eb71b4..5d2c7d4 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' \ - '69e2e947c24af2795bee2e6323723145 ./resources/views/user/images.blade.php' \ + '15878ee87c65d46137249dde8e40931f ./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=69e2e947c24af2795bee2e6323723145" \ + "images_blade_md5=15878ee87c65d46137249dde8e40931f" \ "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 604ff46..32158a2 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 -# 69e2e947c24af2795bee2e6323723145 src/resources/views/user/images.blade.php +# 15878ee87c65d46137249dde8e40931f 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 e70156b..bca9ad6 100644 --- a/src/resources/views/user/images.blade.php +++ b/src/resources/views/user/images.blade.php @@ -644,7 +644,24 @@ class="relative inline-flex h-4 w-7 shrink-0 items-center rounded-full bg-brand document.addEventListener('touchcancel', () => { navDown = false; }, true); })(); - /* 触摸端切图判定(接管 slideOnTouch 后的配套逻辑): + /* 切图动画起点固定为"屏幕中心"(Claude 方案 A)。 + * 库每切一张都新建 :先写成屏幕中心的点(宽高 0 + margin 居中),读完自然尺寸 + * 再加 viewer-transition 写最终宽高与 margin,靠 CSS 过渡补间。CSS 过渡的起点是 + * 浏览器"上一次真正渲染过的样式" —— 中间那帧有没有渲染出来取决于图片缓存/网速/ + * 主线程忙不忙,于是表现为"有时从中间长出来、有时从屏幕上方飞进来"。 + * 这里在 initImage 之前强制回流一次,把"中心点"提交为已渲染样式 ⇒ 起点永远是中心。 + * 覆盖手指滑动、缩略图条、键盘与底部按钮等所有切图路径(都在 view() 里)。 + * 注:早先那版在 pointerup 里摘旧图的 viewer-transition 是无效的 —— view() 新建的是 + * 另一个 ,且 load() 会按 options.transition 给新图重新加类。 */ + if (viewer && typeof viewer.initImage === 'function') { + const rawInitImage = viewer.initImage.bind(viewer); + viewer.initImage = function (cb) { + if (viewer.image) void viewer.image.offsetWidth; // 强制回流,提交"中心点" + return rawInitImage(cb); + }; + } + + /* 触摸端切图判定(接管 slideOnTouch 后的配套逻辑): * 库里 slideOnTouch:false ⇒ 单指动作永远是 'move',库自己的平移就是跟手拖动, * 不再出现 'switch' 动作 ⇒ 1px 判据与"闩锁"都不存在。切图还是回弹由松手时决定。 * 为什么不拦事件:库用 HAS_POINTER_EVENT ? 'pointermove' : 'touchmove' 选通道, @@ -680,12 +697,6 @@ class="relative inline-flex h-4 w-7 shrink-0 items-center rounded-full bg-brand const far = Math.abs(dx) > Math.max(FAR_MIN, window.innerWidth * FAR_RATIO); const fast = Math.abs(dx) / dt > FAST_SPEED && Math.abs(dx) > 30; if ((far || fast) && Math.abs(dx) > Math.abs(dy) * 1.5) { - /* 切图不要"飞入":库的过渡是"从上一张图的 margin 滑到新图的 margin", - * 两张图高度不同(横图↔竖图)时,新图就会从偏上/偏下的位置滑进来。 - * 这一段临时摘掉过渡类 ⇒ 新图直接出现在它该在的位置(= "从中间出来")。 - * 过渡类是库自己在管的(action 为 move/zoom 时它自己也会摘),不属于 - * 那个碰不得的 action 状态机,且同一帧即由 renderImage 恢复。 */ - viewer.image.classList.remove('viewer-transition'); if (dx < 0) viewer.next(false); else viewer.prev(false); guardClick = Date.now(); // 标记:刚刚切过图 } else if (dx !== 0 || dy !== 0) {