Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(reader): 修复 Android 选区跨页自动翻页的连续翻页与漏翻问题
问题背景
Android 真机上,阅读器在“长按选区并跨页拖动”的场景下,自动翻页行为不稳定,主要有两类异常:
在第 1 页拖到页尾后,会正常翻到第 2 页;但进入第 2 页后,即使选区实际上还没有重新触达第 2 页页尾,也可能继续自动翻到第 3、4 页,导致中间页无法停留。
该问题主要出现在 Android 真机触摸手柄路径中,模拟器和鼠标路径不容易稳定复现。
我也是在自己手机触摸屏上使用过程中才发现了这个bug,在模拟器和鼠标路径不容易稳定复现。这可能也是导致这个bug没被发现的原因
修改问题之前:
f4a4915eddc93364fee22f1d1268ea92.mp4
修改问题之后:
97e11d29528325cd3596ebf46c06cace.mp4
原因分析
原有实现的问题不在“是否判断到了页尾”,而在于跨页时的状态管理不正确。
1. 翻页触发没有严格绑定“当前页”
原逻辑在一次拖动过程中,会沿用上一个页面的部分触发状态。
这意味着:第 1 页触发翻页后,进入第 2 页时,没有完全基于“第 2 页当前边界 + 当前选区状态”重新开始判断,导致上一页的触发结果影响了下一页。
结果就是:
2. “本页已触发”标记写入时机过早
原逻辑在
next-page-scheduled阶段就把当前页视为“已触发”。但从“调度翻页”到“真正执行翻页”之间存在一个等待窗口(约 1 秒),这段时间里用户仍可能继续调整选区。
如果这 1 秒内发生了以下情况:
那么逻辑上这次翻页其实并没有真正发生,但当前页已经被提前标记为“触发过”。
后续即使用户重新把选区拖到当前页末尾,也会被错误地跳过,表现为“明明到页尾了却不翻页”。
3. 翻页后新页状态存在短暂滞后
在
view.next()执行后,选区变化和lastLocation更新并不是完全同步的。也就是说,页面视觉上已经翻到了下一页,但内部用于判定“当前页边界”的位置数据可能稍后才更新。
这会导致一种典型漏判:
解决方案
这次修复的核心思路是:把自动翻页改成严格的逐页触发状态机。
1. 改为逐页判定,不再复用跨页触发结果
为每一页生成独立的
pageKey,并在一次选区会话中按页维护状态。自动翻页只能基于“当前页”的边界进行判断,翻到新页后,后续是否继续翻页必须重新按新页计算,不能继承上一页的触发结果。
这样做的目的,是把“第 N 页是否允许翻页”完全限定在“第 N 页自己的状态”上,避免连续失控翻页。
2. 统一翻页触发条件
当前页触发自动翻页必须同时满足两个条件:
compareBoundaryPoints(Range.END_TO_END, lastLocation.range) >= 0selectionBottom >= 0.9也就是说,只有“内容边界触底”和“视觉位置触底”同时成立,才认为当前页真正进入可翻页状态。
这样做的目的,是避免仅凭某一个条件成立就过早触发翻页。
3. 调整“已触发”标记的写入时机
保留“每页只触发一次”的门控机制,但将
triggeredPages.add(pageKey)的时机从“调度翻页时”改为“真正执行翻页时”。也就是说:
next-page-scheduled只表示“准备翻”next-page-trigger才表示“真的翻了”这样做的目的,是确保:
这一步是修复“偶发不翻页”的关键。
4. 离开页尾时取消 pending,回到底部允许重新调度
如果当前页已经进入等待翻页状态,但用户在等待窗口内把选区移出了页尾条件,那么立即取消 pending 状态。
这样做的目的,是让系统真实反映“用户当前是否仍然在请求翻页”,而不是机械执行之前的定时动作。
5. 翻页后增加短时重检,处理新页定位滞后
在执行
view.next()后,增加有限次数的短时重检,主动触发后续判定,直到:这样做的目的,是覆盖
lastLocation更新滞后带来的窗口期,避免“新页其实已经满足条件,但判定还没跟上”的漏翻问题。修改内容
本次改动集中在以下文件:
assets/foliate-js/src/book.jsassets/foliate-js/dist/bundle.js主要代码调整包括:
pageKey,将触发判断绑定到当前页修复后的行为
修复后,自动翻页遵循以下规则:
验证结果
已通过以下验证:
构建验证
flutter build apk --debug通过真机手动回归
影响范围与风险