mole404
|
bcbdbd765c
|
chore(version): 后台「关于」版本号 v3.2 → v3.5
先确认过风险:entrypoint.sh 的 auto_migrate() 不读这个版本号,它直接比对
「镜像里的迁移文件」与「数据库 migrations 记账表」(SQLite 走只读探测,
读不到记账表就整体跳过)。所以改这个值纯粹影响显示,不会触发迁移。
与上一轮署名改动(已单独发布)合计后,后台「关于」显示为:
v3.5
By Mole404 & DeepSeek & Claude
本机 9 套 jsdom 测试全绿。
|
2026-10-03 14:25:05 +08:00 |
|
mole404
|
82fa68256a
|
chore(about): 后台「关于」署名改为 By Mole404 & DeepSeek & Claude
- config/app.php 的 'author':mole404 → Mole404 & DeepSeek & Claude
- admin/setting/index.blade.php:模板里的 "by" 改为 "By"(前缀写在模板里,
值里就不再重复写 By),最终显示为一行:
By Mole404 & DeepSeek & Claude
版本号本次不动(老师要求分两次构建:本轮只改署名)。
本机 9 套 jsdom 测试全绿。
|
2026-10-03 14:18:49 +08:00 |
|
mole404
|
1e5bac3c17
|
fix(viewer): 切图不再从偏移位置"飞入"(切的那一瞬摘掉过渡类)
老师反馈上一版的 viewer.reset() 没治到根(附了瞬间截图)。读源码确认:
· reset() = this.imageData = initialImageData; renderImage() —— 它自己也是带动画的,
所以"先 reset 再切"只是把过渡的起点从"被平移的偏移量"换成"复位中间态",照样滑。
· 库的过渡本质是"从上一张图的 marginTop/marginLeft 滑到新图的 margin"(renderImage 直接
写 width/height/margin,靠 CSS transition 补间)。两张图高度不同(横图↔竖图)时,
新图就会从偏上/偏下的位置滑进来 —— 这就是"有时正常、有时从上面飞"的根因,与拖动无关。
修法:切图这一瞬先摘掉过渡类 viewer-transition,再 next()/prev() ⇒ 新图直接出现在它该在
的位置(观感即"从中间出来")。该过渡类是库自己在管理的(action 为 move/zoom 时它自己也会
摘掉),不属于那个碰不得的 action 状态机;renderImage 同一帧会按 options.transition 恢复它,
因此捏合/双击/平移不受影响。
同时去掉上一版那个无效的 viewer.reset()(它反而制造了另一个中间态)。
本机实测(390x844 + CDP 真触摸):
【回归④切图不飞入】切换瞬间 top=-1582 → 稳定后 top=-1582,差 0px ✓
③ 切图后不退出查看 ✓ ① 捏合 351→1615 ✓ ② 双击 1615→2751 ✓
拖动跟手/回弹 ✓ 横划 20px·竖划 80px 不切 ✓ 横划 120px 切 ✓ 中途不翻页 ✓
本机 9 套 jsdom 测试全绿。
|
2026-10-03 14:13:07 +08:00 |
|
mole404
|
24afc03480
|
Update README.md
|
2026-10-03 14:03:18 +08:00 |
|
mole404
|
1564547216
|
fix(viewer): 切图前复位平移(不再从偏上位置飞入);切图后不再误关查看器
老师真机反馈两条,均已定位并修复:
① 切图时有时从靠上的位置"飞"出来。
原因:库用 marginTop/marginLeft 定位,拖动时图片已被平移过,松手直接切图 ⇒ 新图从
被平移的偏移量开始做 transition(手指带纵向位移时最明显)。修法:切图前先 viewer.reset()
把平移复位,再从居中开始过渡。
② 手指在图片外空白处结束并抬起时,"切图成功"与"退出查看"同时发生。
原因:抬手后浏览器补一发 click 落在背景上,库据此关闭查看器。修法:只吞掉紧跟切图之后
400ms 内、且落点不在图片上的那发背景 click —— 正常"点背景关闭"不受影响。
(历史上曾用 900ms 一刀切地吞 click,副作用是滑动后真双击失效,已废弃;本版条件精确得多。)
本机实测(390x844 手机模拟 + CDP 真触摸),六项全绿:
未放大可拖动 left 20→-20→回弹 20 ✓ 横划 20px/竖划 80px 不切 ✓ 横划 120px 切 ✓
中途不翻页 ✓ 双指捏合 351→1615 ✓ 双击放大 1615→2751 ✓
【回归③】从图片内拖到图片外空白处抬起 ⇒ 查看器仍在 ✓(修复前会被关掉)
注:回归③的探针最初写错(用 offsetParent 判断可见性,固定定位元素恒为 null ⇒ 假阴性),
已改为 display/visibility。
本机 9 套 jsdom 测试全绿。
|
2026-10-03 14:01:56 +08:00 |
|
mole404
|
608a306a71
|
Update README.md
|
2026-10-03 14:00:18 +08:00 |
|
mole404
|
e7f9a80382
|
Update README.md
|
2026-10-03 13:58:17 +08:00 |
|
mole404
|
18514d9d2d
|
Update README.md
|
2026-10-03 13:57:59 +08:00 |
|
mole404
|
b3698bd60f
|
Update README.md
|
2026-10-03 13:57:09 +08:00 |
|
mole404
|
6f4f7ff219
|
Update README.md
|
2026-10-03 13:56:39 +08:00 |
|
mole404
|
97e5f2fbe1
|
Update README.md
|
2026-10-03 13:56:02 +08:00 |
|
mole404
|
a3f5c82287
|
feat(viewer): 关掉库自带触摸切图,改由松手判定 —— 未放大也能拖动、不再一碰就切
采用 Claude 提出的方案(第二步),替换此前所有"阈值接管"尝试。
为什么之前的尝试都不行:
· 第 1 版在 pointerup 后 view() 弹回 —— 拖动中途库早已按 1px 判据翻页,事后补救无效。
· 第 2 版从外面改写 viewer.action —— 库的捏合/双击/过渡全靠这个状态机,外部改写导
致双指捏合失效、拖动带过渡延迟、切图错乱(老师真机实测四条全中)。
· 第 3 版在 document 捕获阶段 stopPropagation 拦 pointermove —— 库用
HAS_POINTER_EVENT ? 'pointermove' : 'touchmove' 选通道,真机可能走 touchmove,
拦截整段失效;且监听器注册在库之后,同一节点同一阶段抢不到前面。真机实测无效。
本次做法(读源码 + 真机特征共同指向的最小改动):
· new Viewer(...) 加 slideOnTouch: false —— 单指动作永远是 'move',库自带的平移就是
跟手拖动;不再产生 'switch' 动作 ⇒ 1px 判据与 others.js 的 'switched' 闩锁都不存在。
· 松手时自己判定:够远(> max(56px, 视口宽×0.18))或够快(> 0.5px/ms 且 > 30px),
且横向占优(|dx| > |dy|*1.5)⇒ viewer.next(false)/prev(false);否则 viewer.reset()
带动画回弹。放大态或第二指(捏合)出现时一律放权给库。
· 全程不写库的内部状态,也不拦事件 ⇒ 不依赖 pointer/touch 通道,捏合/双击/过渡不受影响。
本机实测(390x844 手机模拟 + CDP 真触摸)全绿:
【未放大可拖动】按下 left=20 → 中途 left=-20 → 松手回弹 left=20 ⇒ 跟手 ✓ 回弹 ✓
横划 20px / 竖划 80px → 不切 ✓ 横划 120px → 切 ✓ 拖到 30px 中途 → 不翻页 ✓
双指捏合 w 351→1615 ✓ 双击放大 w 1615→2751 ✓
注:放大态下本方案不接管切图(一律放权给库做平移),与上一版"放大后 120px 也切"不同。
本机 9 套 jsdom 测试全绿。
|
2026-10-03 13:28:21 +08:00 |
|
mole404
|
f535c8c6fb
|
fix(viewer): 切图阈值改为"只拦事件、不碰库状态"(上一版改写 action 把捏合等全弄崩了)
上一版的做法是从外面改写 viewer.action。事后真机验证发现这是根本性错误:
库的捏合、双击、移动过渡全都依赖它自己的 action 状态机(handlers.js:367-375 在
pointerdown 一次性定值;指针移动时若 action 为 move 会摘掉过渡类 CLASS_TRANSITION)。
外部改写它 ⇒ ①双指捏合被覆盖失效 ②拖动带上 CSS 过渡延迟 ③切图行为一并错乱。
老师实测四条全中。本版彻底换方向,并已回退到 49ef183a 期间线上一律跑稳定版。
新做法(零改写):
· 单指触摸、且位移尚未到阈值时,在捕获阶段 e.stopPropagation(),让库收不到这次
移动 —— 它就不会切图。库的内部状态自始至终不碰。
· 一旦划够阈值(未放大 60px、放大后 120px,且 |dx| > |dy|*1.2)立即放行,之后
全部交给库原样处理(跟手切图本来就是它自带的行为)。
· 多指(捏合)全程不干预:只要同时按下的手指数 > 1,本层立刻退出。
· 非画布区域(缩略图条等)一律放行。
本机实测(390x844 手机模拟 + CDP 真触摸,含两项回归):
捏合 w 351 → 1615 ✓ 双击放大 w 1615 → 2751 ✓
横划 20px / 竖划 80px → 不切 ✓ 横划 120px → 切 ✓
拖到 30px 中途 → 不提前翻页 ✓ 库状态零改写(源码里仅存松闩那一处历史用法)✓
「未放大也能拖动」本轮未做(风险最高,留第二轮):实测拖动时图片不动,属预期。
本机 9 套 jsdom 测试 409 项全绿。
|
2026-10-03 13:04:42 +08:00 |
|
mole404
|
a1b5ff298d
|
fix(viewer): 用"规范动作值"驱动切图/拖动,未放大也能拖动
上一版的两个问题一起修:
1) 切图失效(上一版把库的内部状态读回来又还回去)。库的 change() 一进 SWITCH 分支就把
this.action 置成 'switched'(others.js:217,即"闩"),而这个值在外层 switch 里没有 case;
把读到的值还回去,等于让库什么都不做 ⇒ 够格也不切图。实测确认:横划 120px 由"切了"
变成"没切"。改为直接写两个确定的字面量(handlers.js:367-375 里库自己的取值就是
'move' / 'switch'):
未放大且划够 ⇒ 'switch'(库跟手切图)
其余(未划够 / 已放大未够格)⇒ 'move'(库只平移)
不再读取库的内部 action,避免再被闩住的值带偏。
2) 老师新需求:未放大时也要能拖动图片。未划够时给库 'move',于是 1:1 状态下拖动就是平移,
而"稍动就切图"依然不会发生(划够 60px 且明显横向才变 'switch')。
阈值维持老师定的:未放大 60px、放大后 120px,竖直方向一律不切。
验收脚本同时修了一处自身缺陷:原来按"面积最大的 img"取对象,图片被平移后会取错对象,
改为直接取 .viewer-canvas img。
本机实测(390x844 手机模拟 + CDP 真触摸)全绿:
拖到 30px 中途 → 未翻页 ✓ 松手后 → 未切 ✓
横划 20px / 竖划 80px → 没切 ✓ 横划 120px → 切了 ✓
【未放大可拖动】按下 left=20 → 中途 left=-3 → 松手 left=-20 ⇒ 跟着手走 ✓,且未切图 ✓
放大后两条:无头环境里"放大"这个动作仍没成功,两条实际跑在未放大状态,无有效数据;
放大后的 120px 阈值待老师真机定手感。
本机 9 套 jsdom 测试 409 项全绿。
|
2026-10-03 12:50:22 +08:00 |
|
mole404
|
71657705e3
|
fix(viewer): 切图阈值改为"过程中就不动作"(上一版只在松手后弹回,翻页那一下用户已看见)
老师反馈:上版"有变化但仍极度灵敏"。诊断:两个原因都占,主因是**库的判定在拖动过程中就已生效**——
上一版只在 pointerup 时判定够不够格、不够就 view() 弹回,但拖动中途库的 1px 阈值
(others.js change():absoluteOffsetX > 1)早就把图翻过去了,事后弹回补救不了观感。
本次改法:拖动过程中、尚未划够阈值时,直接把 viewer.action 设成 false ——
库的 pointermove 第一行就是 `if (!this.viewed || !action) return;`,于是它整段移动都不处理,
根本不会发生"先翻过去再弹回来"。一旦划够阈值,就把库自己算出的动作值(在第一次 move 时
记下的 savedAction)原样还回去,从那一刻开始跟手。
阈值也按老师要求钝化:未放大 40 → 60,放大后 80 → 120。
"弹回"保留为兜底(正常路径不会走到)。
本机实测(390x844 手机模拟 + CDP 真触摸),这次专门量"拖动中途":
拖到 30px 中途 → 大图未变 ✓(上一版的破绽,本次治住)
松手后 → 未切 ✓
横划 20px / 竖划 80px → 没切 ✓(方向判据生效)
横划 120px → 切了 ✓
放大后两条:无头环境里"放大"这个动作没成功(工具栏与滚轮都试过),两条实际跑在未放大状态,
未取得有效数据 —— 放大后的 120px 阈值待老师真机定手感。
本机 9 套 jsdom 测试 409 项全绿。
|
2026-10-03 12:43:01 +08:00 |
|
mole404
|
49ef183a8e
|
feat(viewer): 手机端切图阈值接管(老师定的三条规则)—— 不再"稍动就切"
老师反馈:手机端切图手势过于灵敏(滑短短一下就切),想拖动基本都误触;而且"放大后能拖、
缩小后一拖就切图",阈值区间摸不透。
根源(源码核对):库把切图阈值硬编码成 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 项全绿。
|
2026-10-03 12:31:50 +08:00 |
|
mole404
|
58e24766fa
|
fix(viewer): 关掉 focus(真因:view() 聚焦缩略图 li 把 overflow:hidden 的导航条滚走)+ 滚轮监听器改 passive
真因(Claude 定位 + 真 Chromium 对照验证,我核对源码确认):
view() 摆放缩略图条之前会先对目标缩略图的 <li> 调 .focus()(源码 s.focus&&h.focus()),
而 .viewer-navbar 虽然 overflow:hidden,仍是滚动容器 ⇒ 浏览器为了"让焦点可见"把导航条滚走
(scrollLeft 推到最大);随后 renderList() 再叠一层 translateX ⇒ 位置被偏移两次,整条列表
跑到屏幕外,只剩空黑底。
表现:从靠前的图跳到很靠后的图(或关掉再打开靠后的图)后,缩略图条里靠后那批不显示、
靠前的还在 —— 因为靠前那批本来就在初始可视区、不触发滚动。与"图是不是后加载的"无关,只与位置有关。
真机对照:桌面跳到第 93 张 navScrollLeft 2439 / 可见缩略图 0 → 加 focus:false 后 0 / 43;
手机 2573/0 → 0/15;关闭后重开第 100 张 2909/0 → 0/15。
代价:关掉"打开后聚焦容器 + 焦点陷阱",对鼠标/触屏无影响,仅纯键盘 Tab 导航有差别
(键盘切图与 Esc 关闭实测不受影响)。
保留上一版的 update() 包装 —— Claude 确认它修的是另一个真实存在的问题(重建列表后位置没重算)。
顺带的清理(回答老师"砍偏的地方有没有影响"):
· 拦 touch / 关捏合 / 吞补发 click 三刀早已回退或删除,代码里没有残留 ✓
· 画布 touch-action:none 与 touchmove 的 passive 保留(前者是标准做法且不碰缩略图条,
后者比原来更正确)✓
· 唯一还带隐患的是滚轮那个非 passive 监听器:与 touchmove 那个坑同源
(document 上非 passive 会让浏览器丢掉"不等 JS 就绘制"的快路径)。
它的 preventDefault 其实是多余的 —— stopPropagation 就足以拦住控件的滚轮缩放
(控件是在它自己的监听器里做的)。故一并改成 passive。
本机 9 套 jsdom 测试 409 项全绿。
|
2026-10-03 11:58:10 +08:00 |
|
mole404
|
50d4d034ae
|
fix(viewer): 包一层 update(),让缩略图条重建后补算位置(Claude 定位 / 已核对产物)
现象:看图时底部缩略图条偶尔空白;等多久都不出来,但手指一碰/一滑它立刻显示;
安卓和电脑端都有(所以不是之前猜的"安卓绘制快路径",那条成因已被证伪)。
机制(Claude 定位,我在源码与线上压缩产物里逐条核对过):
viewer.update() 会把 .viewer-list 的 width 设成 auto、重建所有 <li>,但当前图没变时
不调 renderList(),于是位置(width + translateX((容器宽-30)/2 - 31*index))仍是旧值。
两者错配 ⇒ 列表盒子沉在屏幕左侧外面,只剩一条空黑底。手指一滑走 goTo→view→renderList,
位置被重算,缩略图立刻回来 —— 与老师"一碰就出来"完全吻合。
看图时触发 update() 的路径:images.blade.php 里无限滚动每次请求结束的 complete 回调
(看图时后台正好加载完一批),以及侧栏折叠。所以是"偶尔"。
本机 A/B 实测(副本站,1280x720,第 35 张,80 个缩略图):
A 组(线上那版):打开后量到 width=auto + translateX(-460px) —— 正是那个错配状态,量到了 ✓
但没有复现将整条推出可视区(该尺寸/该张数下缩略图仍可见)⇒ 没敢声称"一定治好"。
已如实说明。"一碰就好"这条证据与机制吻合,且本改动只把状态修对、不会变坏。
实现(不依赖可能被压缩改名的成员:产物里查不到 items / isShown,故改成查 DOM):
· 包一层 viewer.update:跑完补一次 renderList(),并按 index 补回丢失的 li.viewer-active。
· 守卫:update/renderList 不是函数就安静退出;整个补算包在 try/catch 里 ——
补这一刀绝不能反过来把看图界面弄挂。
· 注意这是 fork 侧自证:Dockerfile 钉的 images.blade.php md5 必须同步。
本机 9 套 jsdom 测试 409 项全绿。
|
2026-10-03 11:07:55 +08:00 |
|
mole404
|
73bf20143b
|
fix(viewer): 修上一版引入的回归——缩略图条在安卓上偶尔空白(非 passive 的 document touchmove)
老师报的现象:安卓上看图时,底部缩略图条偶尔不显示;**等多久都不会自己出来,
但手指一碰它立刻显示**,像被什么卡住了;大图始终正常;且「这个版本加了滚动才开始有」。
定位:这就是典型的「不重绘」。上一版为了让缩略图条支持拖动切图,在 document 上注册了
一个 **非 passive 的 touchmove**(还带了 preventDefault)。在 document 上注册非 passive 的
touchmove 会让 Chrome 失去「不等 JS 就滚动/绘制」的快路径 —— 表现为远离手指的元素
(屏幕底部的缩略图条)不重绘:一直空白,直到下一次触摸才画出来。安卓上尤其明显;
大图在屏幕中间所以看起来正常。全中。
修法:把它改成 **passive: true** 并去掉 preventDefault ——
缩略图条本来就是 touch-action:none、浏览器根本不会滚它,所以 preventDefault 是多余的,
去掉它对功能毫无影响(拖动切图逻辑一行没动)。注释里写明了"这里必须 passive,不许改回去"。
本机 9 套 jsdom 测试 409 项全绿(含拖动切图的行为用例)。
|
2026-10-03 10:45:11 +08:00 |
|
mole404
|
1eb6a54053
|
feat(viewer): 底部缩略图条上的快捷切图(电脑滚轮切一张 / 手机拖动连续切)
老师的两个诉求:
· 电脑:光标在底部缩略图条上滚轮 ⇒ 快速前后切图
· 手机:在缩略图条上左右拖动 ⇒ 连续滑动切换(不是"划一下才切一张")
实现(全部挂在外面,不改 viewer.min.js):
· 滚轮:document 捕获阶段接住 —— 控件自己在外层容器上绑了"滚轮缩放"(也是捕获阶段),
所以必须比它更早,并且只对落在 .viewer-navbar 上的滚轮生效:图上滚轮照旧是缩放。
节流 260ms,滚一格切一张。stopPropagation 阻止它被当成缩放。
· 拖动:tracker 在触摸起手记下起点与当时的 index,touchmove 里按"已滑过距离 / 40px"
连续 follower —— 拖回来也会切回去,是连续跟随而不是一次性触发。
另:缩略图条被库设成 touch-action:none、本来就划不动,所以不存在"抢走它自己的滚动"
这个问题(它是靠自动跟随当前图工作的)。
· 一律调对外 API viewer.view(index),到头即止(loop 已关)。
本机验收(模拟环境,1280x720,40 张缩略图):
① 缩略图条上滚一格 → 切到下一张 ✓
② 大图上滚一格 → 只有缩放、不切图 ✓
③ 缩略图条上往左拖 120px → 连续切过 3 张 ✓
④ 再往右拖回 80px → 能切回来 ✓
无 JS 报错 ✓
顺带修的守卫冲突:
· 注释里写了"调公开 API"撞上项目铁律「公开/私有 口径一个字的残留都不许有(含注释)」,
改成"对外 API"。
· tag-feature 的守卫 `!/viewer\.view\(/` 原意是"不许劫持卡片点击",收窄为
"卡片点击附近不许出现 viewer.view(",并写明理由(新功能挂在缩略图条上,不碰卡片)。
jsdom 9 套 409 项全绿。
|
2026-10-03 10:16:05 +08:00 |
|
mole404
|
f1fc0fd639
|
fix(viewer): 关掉到头绕回(loop:false)+ 收口双击判定(300ms 窗口、拖动不算轻点)
一、到头绕回(老师明确不需要循环)
打开第一张图后向右滑"上一张",库会绕回最后一张;而这一下会让控件重建画布/缩略图条,
随之出现两个现象:iOS 上整个看图界面左偏、右侧空出一条;安卓上底部缩略图全空(只剩框)。
采用 loop:false 从源头去掉这个动作;若后续仍观察到错位/缩略图异常,再按"重建那一步"单独查。
二、双击判定的收口(老师反馈:连续拖动两下也被当成双击放大)
库的「模拟双击」(handlers.js:424-451)只看两次抬手的间隔(硬编码 500ms,且改不了),
完全不检查中间有没有拖动。在外面收口(不改 viewer.min.js):
· 抬手时用「起点→终点」位移判断是轻点还是拖动,阈值 30px ——
依据是实测:真滑动 130~300px(中位 214px),轻点抖动一般 <15px,30px 把两者分开;
(老师提醒过 10px 太严,会误伤真双击,故取 30px。)
· 是拖动 ⇒ 立刻清掉库的 imageClicked,拖动永远不能充当"第一次轻点";
若这一下之前已有"第一次轻点",那发 50ms 后要派发的合成 dblclick 也一并拦掉。
· 两次都不动的轻点 ⇒ 真双击,放行;并按老师要求把有效窗口从 500ms 收到 300ms。
仍用 touchstart/touchend 的坐标差(不用 touchmove —— 安卓上它可能收不到)。
本机 9 套 jsdom 测试 409 项全绿。
|
2026-10-03 02:13:47 +08:00 |
|
mole404
|
8eda125810
|
chore(viewer): 撤掉临时诊断(面板 + 打点)与「吞补发 click」那段无效补丁
问题已由 5f36036 的「松闩」修复解决,随之清理两处临时/无效代码:
1. 删除 `?dbg=1` 诊断面板与远程打点(整块临时代码,共 6.5KB)——
定位结束后按约定撤掉,线上恢复干净。
2. 删除「吞掉滑动手势后 900ms 内的 click/dblclick」那段。
它针对的是「凭空 dblclick 触发放大」这条**错误判断**(真因是 others.js 的闩锁),
而且它有副作用:滑完图 900ms 内真双击会失效,与「保留双击放大」的诉求相冲突。
真机上也从未验证过它有效,故一并移除。
保留:
- 5f36036 的松闩修复(真因修复,已在 jsdom 复现脚本上验证对比)。
- common.less 里给 .viewer-canvas / img 补的 `touch-action: none`
(全屏看图控件的标准做法,零副作用,顺带避免浏览器中途收走手势)。
- viewer.min.js 始终未改动。
本机 9 套 jsdom 测试 409 项全绿。
|
2026-10-03 01:45:47 +08:00 |
|
mole404
|
5f3603658b
|
fix(viewer): 松开关闭切图手势的「闩锁」——这解释了安卓上「纹丝不动 / 大多数切图失败」
真因(真机打点 518 条 + 源码逐行核对 + jsdom 复现三方对齐):
node_modules/viewerjs/src/js/others.js:216-223 change() 里
case ACTION_SWITCH: {
this.action = 'switched'; // ← 先上闩
const absoluteOffsetX = Math.abs(offsetX);
if (absoluteOffsetX > 1 && absoluteOffsetX > Math.abs(offsetY)) {
… this.view(index); // 过了判定才真切图
}
第一次 pointermove 进来就把 this.action 改成字符串 'switched',而外层 switch 没有这个分支
⇒ 之后每一次 pointermove 全部落空:不切图、不平移、不放大 = 用户看到的「纹丝不动」。
判定只看开头那次采样,而真手指刚落下天然带纵向抖动(dy ≥ dx 很常见)⇒ 大多数手势一开始
就被闩死,偶尔开头偏横向才成功 = 「大多数失败、少数成功、无规律」。iOS 上因为触摸路径不同
不触发,官方演示页也没有列表页那套前置条件,所以都正常。
修法(不改 viewer.min.js —— 压缩产物改了没法维护):从外面把闩松开。
手势期间用 document 捕获阶段监听 pointermove,发现 viewer.action 变成了那个闩值,
就恢复成手势开始时 Viewer 自己算出的动作值;后续采样继续参与方向判定(判据看的是最近一次
位移,所以横滑终究会切到)。首次遇到闩值时自动学出「切图动作」的真值,无需硬编码常量。
验证(用 Claude 提供的 jsdom 复现脚本,加/不加本补丁对比):
修复前:starts diagonal dy>dx, then horizontal → no switch ✗
修复后:starts diagonal dy>dx, then horizontal → SWITCHED ✓
对照组:vertical drag (should NOT switch) → 仍 no switch ✓(未被破坏)
其余 7 个用例修复前后都正常。jsdom 9 套 409 项全绿。
|
2026-10-03 01:34:09 +08:00 |
|
mole404
|
36245c583b
|
fix(viewer): 安卓切图失败的真因修复——看图控件自己"模拟双击",用抬手坐标差把它拦住
真机打点(420 条,会话 palh0x,老师只做了点开图片 + 左右滑动)暴露了真因:
类型计数 pointerdown 102 / touchstart 102 / pointerup 102 / touchend 88 / click 3 / **dblclick 23**
—— 老师没做过任何双击,却凭空出现 23 次 dblclick(且 click 只有 3 次,说明不是"两次单击拼出来的",
而是被合成派发的)。
源头在库自身:`viewerjs/src/js/handlers.js:424-451`
// Emulate click and double click in touch devices to support backdrop and image zooming (#210).
if (IS_TOUCH_DEVICE && action !== ACTION_ZOOM && pointer && (Date.now() - pointer.timeStamp < 500)) {
if (options.toggleOnDblclick && this.viewed && event.target === this.image) {
if (this.imageClicked) { …dispatchEvent(this.image, EVENT_DBLCLICK, {…}) } // ← 凭空派发 dblclick
else { this.imageClicked = true; … }
即:**两次落在图片上的抬手间隔 <500ms**,它就自己派发一个 dblclick ⇒ 触发放大 ⇒ 图片不再
"刚好一屏" ⇒ isSwitchable 为假 ⇒ 滑动变成平移 ⇒ 大多数切图失败。老师连续快速滑图正好满足。
上一版(3ea41b9)为何没拦住:它靠 touchmove 判断手指挥动过,但安卓上控件在指针处理里
preventDefault 之后 touchmove 可能根本收不到 ⇒ moved 永远为 false。
本次改为用 **touchstart / touchend 的坐标差**(真机打点里这两种事件 100% 都有,已逐条核对),
位移 >10px 即认定是滑图,吞掉其后 900ms 内落进看图区域的 click/dblclick;
真正的双击是两下不动的轻点,不受影响 —— 老师要求保留的双击放大照旧可用。
本机 9 套 jsdom 测试 409 项全绿。
|
2026-10-03 00:29:21 +08:00 |
|
mole404
|
3c05072fd8
|
debug(viewer): 诊断面板改用「远程打点」——数据直接落进服务器访问日志,并自动停在最新一行
老师反馈:面板设了 pointer-events:none 之后自己也不能滚动了,看不到最下面。改为更聪明的方案:
1. 页面把每条事件打点回站点(fetch 到 /dbg-<会话号>/<序号>/<记录>)——请求会落进服务器
访问日志,阿罗娜在远程直接读日志即可拿到全部数据,老师不需要截图、不需要滚动。
打点只在 ?dbg=1 时发生(整块诊断代码都不带参数时不加载)。
2. 面板加自动滚动,始终停在最新一行。
本机验证:带 ?dbg=1 打开并操作后,在服务器日志里读到了完整打点,含会话号与序号,例如
dbg-2tuzt1/1/pointerdown img ptrId=2/touch @195,247
dbg-2tuzt1/2/touchstart img touchId=0 @195,247
dbg-2tuzt1/5/click img ptrId=2/touch @195,247
共 10 条。不带 ?dbg=1 时面板与打点都不出现。
|
2026-10-03 00:17:38 +08:00 |
|
mole404
|
3ea41b940d
|
fix(viewer): 安卓触摸手势修复——吞掉"滑完图之后"补发的 click,画布补 touch-action:none
真机诊断数据(老师安卓 + ?dbg=1 面板)确认了两件事:
1. 计数里 dblclick×5,且 .viewer-move 上出现 dblclick ——「本来要双击才放大,点一下就放大」
被数据证实:看图控件只绑 pointer 事件,安卓在触摸抬手后还会补发一发 click,
它和紧接着的轻点挨得近,被浏览器拼成 dblclick ⇒ 误触发放大。
iOS 上 fork 的长按补丁正好吞掉 iOS 抬手那发 click,所以只有安卓坏。
2. touchstart×35 但 touchend×31、pointerdown×35 但 pointerup×31、pointercancel×4
—— 有几次手势被浏览器中途收走(部分来自诊断面板自身滚动,面板已改 pointer-events:none)。
改动:
a) images.blade.php:只吞掉「手指挥动过(位移 >10px)」之后紧跟的那发 click/dblclick。
真正的双击是两下不动的轻点,不受影响 —— 老师要求保留双击放大。
b) common.less:给 .viewer-canvas 与其 img 补 touch-action:none(库只在 .viewer-container
上声明了,画布层仍是 auto)⇒ 浏览器不把手指判成滚动,不再发 pointercancel。
c) 诊断面板加 pointer-events:none,避免它自身滚动产生干扰。
本机验收(安卓 UA 模拟,重建 CSS 后):
单击不放大 ✓;双击仍放大(351 → 3050)✓;画布 touch-action=none ✓;无 JS 报错 ✓;
100/160/260px 三条滑动全部切图 ✓;两指捏合仍可用(351 → 1193)✓;jsdom 9 套 409 项全绿 ✓。
注:本机模拟仍复现不出该 bug,最终效果以安卓真机为准。
|
2026-10-03 00:05:51 +08:00 |
|
mole404
|
57714aa09a
|
fix(debug): 诊断面板去掉 hover:none 字样,避免撞上「触摸端媒体查询已删」的守卫测试
images-modal.test.mjs 里有一条守卫:blade 代码(去掉注释后)不得再出现 `hover: none`
字符串(为了让触摸端操作按钮常显、不留无用/矛盾规则)。诊断面板里那句
`matchMedia('(hover: none)')` 正好命中该断言,导致 CI 的 jsdom 测试失败、镜像未提升。
面板本来就是临时诊断,去掉这一项即可(ontouchstart / maxTouchPoints / pointer:coarse 仍在)。
本机 9 套 jsdom 测试 409 项全绿。
|
2026-10-02 23:43:35 +08:00 |
|
mole404
|
1ca7f96ad7
|
debug(viewer): 临时诊断读数面板(?dbg=1),用于定位安卓看大图手势问题
背景:安卓(Edge/Chrome)看大图时切图/放大/拖动都不稳,iOS 正常,官方演示页在老师安卓机上
也正常;本机 Chromium 模拟安卓复现不出(滑动/单击/双击/捏合/事件计数/pointers 全正常)。
老师补充的关键现象:「出问题的时候,本来要双击才放大,点一下图片就能放大」——像输入被重复投递。
既然本机复现不了,改为取真机数据。
做法:仅在网址带 ?dbg=1 时,在页面顶部显示一块读数面板(26vh,避免遮挡图片),实时显示:
· 环境:UA 尾部、ontouchstart、maxTouchPoints、hover:none、pointer:coarse
· viewer 容器个数、大图尺寸 / 容器尺寸
· 最近 20 条事件:capture 阶段的事件名 + 目标 + touchId/pointerId + 坐标;
每条都跟一行 bubble 阶段的**最终 defaultPrevented**
· 事件计数(touchstart/pointerdown/click/dblclick…)
点面板清空;不带 ?dbg=1 时完全不加载(已实测:不带时无面板 ✓,带时出现 ✓,计数实时更新 ✓)。
注意:这是**临时**代码,定位完即删除。
|
2026-10-02 23:39:23 +08:00 |
|
mole404
|
cc150e2f95
|
revert(viewer): 回退安卓触摸手势相关的两轮改动,恢复捏合缩放
前两轮都未获真机验证有效:
05604dc 捕获阶段拦 touch* —— 实测 Viewer 只绑 pointer 事件,是空拦;且会挡住
context-js 菜单守卫的触摸(点空白关菜单失效),属回归
53edb1f 安卓关掉 zoomOnTouch —— 老师实测「还是不行」,与捏合无关
老师要求把捏合恢复,故把 images.blade.php / Dockerfile / patches/ios-longpress.patch
一并还原到 af7c730(老师验收「修得很完美」的那一版)。
md5 回到 27e13507a629aa3ae9f21496a6cf0ebf,zoomOnTouch 已移除(默认 true,捏合恢复)。
看大图手势的真因仍未定位。老师补充的关键现象:出问题时「单击一下图片就放大」(本该双击)
——即输入像被重复投递;但本机 Chromium 模拟安卓仍复现不出:滑动/单击/双击/捏合均正常,
一次单击只产生 1 个 click、双击是 2 click + 1 dblclick,只有一个 .viewer-container 实例。
下一步改为在页面加临时诊断读数,取真机数据定位。
|
2026-10-02 23:35:57 +08:00 |
|
mole404
|
53edb1ffb3
|
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 三条滑动全部正常切图 ✓;捏合缩放按设计在安卓关闭 ✓。
真机效果仍需老师在安卓上确认。
|
2026-10-02 23:24:13 +08:00 |
|
mole404
|
05604dcff2
|
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 差异是实测的,
实际效果请老师在安卓真机上确认。
|
2026-10-02 23:02:38 +08:00 |
|
mole404
|
af7c730edc
|
fix(viewer): 用反向 zoom 根治看图控件在桌面 110% 缩放下的偏移
上一版只在「点开」这条路径上用 JS 重算 margin:键盘 ←→ 切图又偏回来,图片适配尺寸也偏大、
会盖住下方导航条 —— 逐条路径打补丁的方向是错的。
真因:<html>{zoom:1.1} 让 Viewer 内部「以为的 1px」只有屏幕上的 1/1.1。它按视觉尺寸
(window.innerWidth=1280)算居中/适配/边界,写出来的却是布局值(空间只有 1164),
渲染时浏览器又乘 1.1 ⇒ 居中偏、适配偏大、边界回收也错。
修法:在外壳上把缩放缩回去(common.less 的 html .viewer-container { zoom: calc(1/1.1) }),
让 Viewer 的子坐标系与屏幕 1:1,它自己的数学就全对;blade 里上一版加的 JS 补丁整块删除。
只在 @media (min-width:768px) 内,手机端零影响。
真机验收(生产库只读副本 + 一次性容器 + Chromium,1280×577):
- 图片视觉尺寸 = 布局尺寸(625.2 vs 625)⇒ 坐标系 1:1
- 画布高 577 = 屏幕高(原来 525)
- 左右留白差 +0.0(两张横图两张竖图都验过);按键盘 → 切图后仍 +0.0(上一版这里会偏)
- 图片底 455 / 导航条顶 525 → 留 70px,不再被下方按钮/缩略图压住
- 竖直:上 24 = 到工具栏间隙 24,对称(Viewer 主动给底部 UI 留位)
- jsdom 9 套件全绿
|
2026-10-02 22:34:10 +08:00 |
|
mole404
|
197a5f0cd6
|
fix(viewer): 桌面 110% 缩放下看图控件整体右偏 + 遮罩盖不满屏幕
真因(真 Chromium 实测,1280×577 / html{zoom:1.1}):
- 布局空间只有屏幕的 1/1.1(1280 屏 → 布局 1164)。Viewer 按「视觉屏幕宽 1280」算居中,
算出的 margin-left 被当【布局值】写进内联样式 ⇒ 渲染时又 ×1.1 ⇒ 图片整体右偏
(1280 − 1280/1.1)/2 ≈ 58 布局 px(渲染 64px)。实测左右留白 360.1 / 232.1(差 +128)。
- 看图外壳(.viewer-container,fixed + inset:0)在 zoom 下只铺到屏幕的 90.9%
(1163.6×524.5),右侧/底部约 116×52px 的遮罩盖不住。
修法:
- blade:点开图后约 4s 内持续按【布局单位】重算大图 margin(幂等;z=1 的手机端直接跳过;
带「整图适配」护栏,不干扰用户自己的放大/平移)。不用 Viewer 的事件 API —— 内置的
viewer.min.js 是压缩产物、实例上没有可用的 .on()(用了会让整段脚本报错)。
- common.less:外壳改 width/height:100%(实测渲染出来正好铺满屏幕;不能用 100vw,
zoom 下它会渲染成 1408 即溢出 10%)。
真机验收(生产库只读副本 + 一次性容器 + Chromium):
- 左右留白 296.4 / 295.8(差 +0.6)✓ 上下 51.5 / 51.3(差 +0.2)✓
- 容器与遮罩 = 1280×577 = 正好一屏 ✓
- 手机档 390 宽:z=1,代码自动跳过,左右 19.5/19.5(差 0.0,与原样一致)✓
- jsdom 9 套件 409 项全绿 ✓ 无 JS 报错 ✓
|
2026-10-02 21:17:39 +08:00 |
|
mole404
|
6e317b106a
|
wip: 看图控件桌面 110% 缩放下居中(A 容器铺满已验证;B margin 重算已落地但仍有约 21px 残余,未完成)
|
2026-10-02 21:17:39 +08:00 |
|
mole404
|
462bd0d47c
|
Revise installation steps in README.md
Updated installation instructions for clarity and added a step for post-installation usage.
|
2026-10-02 20:37:31 +08:00 |
|
mole404
|
d5263fcab3
|
Update README.md
|
2026-10-02 20:35:34 +08:00 |
|
mole404
|
cc67964c68
|
Update README.md
|
2026-10-02 20:35:09 +08:00 |
|
mole404
|
cdf512b289
|
Update README.md
|
2026-10-02 20:34:05 +08:00 |
|
mole404
|
a2a12f3fcf
|
Update README.md
|
2026-10-02 20:33:51 +08:00 |
|
mole404
|
aa476b4d7f
|
Update README.md
|
2026-10-02 20:33:02 +08:00 |
|
mole404
|
6239d69904
|
Revise README for clarity on features and changes
Updated README to clarify changes and features.
|
2026-10-02 20:31:19 +08:00 |
|
mole404
|
2847a533e2
|
Update README.md
|
2026-10-02 20:30:46 +08:00 |
|
mole404
|
d83bbc550f
|
Update README.md
|
2026-10-02 20:28:54 +08:00 |
|
mole404
|
4a54f9ae7f
|
Update README.md
|
2026-10-02 20:27:30 +08:00 |
|
mole404
|
c04861aeed
|
Update README.md
|
2026-10-02 20:25:15 +08:00 |
|
mole404
|
a672b38a31
|
Update README.md
|
2026-10-02 20:23:22 +08:00 |
|
mole404
|
fcdbfef3f7
|
Revert "fix(css): 桌面 110% 缩放下看图控件整体右偏 —— .viewer-container 按 ls-zoom 口径除以 1.1"
This reverts commit 51fb29b767.
|
2026-10-02 20:21:12 +08:00 |
|
mole404
|
736c3f2d8f
|
Update README.md
|
2026-10-02 20:17:35 +08:00 |
|
mole404
|
61f8b1cec5
|
Update README with new features and upgrade support
Added support for lossless upgrades from upstream Lsky-Pro V2.1.
|
2026-10-02 20:16:37 +08:00 |
|
mole404
|
99e6d2a741
|
Update README.md
|
2026-10-02 20:14:32 +08:00 |
|