Files
terminal-lab/pxterm/mobile-scroll-optimization.md
2026-03-03 21:19:52 +08:00

38 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 移动端 Xterm.js 滚动优化总结 (Mobile Scroll Optimization)
## 1. 之前的问题现象 (The Problem)
在移动端(特别是 iOS Safari/WebKit`xterm.js` 终端界面中,用户上下滑动时经常会遇到 **卡顿****突然冻结**。具体的表现是:手指在屏幕上持续滑动,但屏幕内容不再发生滚动。
## 2. 根本原因 (Root Cause)
通过在 `window` 级别捕获全局手指移动日志,我们发现了导致“屏幕结冰”的确切原因:
1. **浏览器原生手势劫持 (Native Gesture Hijacking)**移动端浏览器OS 级别)默认会接管屏幕上的 `touch` 事件,用于判断是否需要进行页面滚动、双击放大或边缘返回等原生手势。
2. **`touchmove` 事件被吞噬**`xterm.js` 处于 DOM 渲染模式时,内部生成了大量的 `span``div` 节点。当手指在这些复杂的层叠节点上滑动时,系统的手势识别引擎一旦认定这是系统的原生滚动行为,就会**强制中断并停止派发 JS 层的 `touchmove` 事件**(或者派发 `touchcancel`),导致我们原有的滚动计算逻辑中途得不到坐标更新,画面即刻冻结。
## 3. 演进与精细化方案 (Evolution & Refined Solution)
单纯使用“防劫持猛药”能解决滚动断流问题,但会导致一个严重的副作用:**iOS 原生的长按文本选择功能(放大镜与高亮选区)会失效。**
为此,我们抛弃了全局霸道的 `touch-action: none` 与过早的 `preventDefault`转而使用“动态拦截Dynamic Interception”组合拳
### A. 撤销 CSS 暴力禁用,依赖 JS 动态阻止
不要在终端交互区强加 `touch-action: none !important;`。必须保留浏览器默认的触摸能力,否则底层 OS 根本不会触发“长按开启选区”的计时器。
### B. Touch Start 阶段:坚决放行 (Allow to Timer)
`touchstart` 甚至 `pointerdown` 阶段,**绝对不再调用 `preventDefault()`**。
- **作用**:确保手指刚摸到屏幕时,原生浏览器可以“观察”这次触屏是否可能变成一次“长按唤醒放大镜和选区”的操作。
### C. Touch Move 阶段:精准拦截与驱动 (Precise Interception)
这是最核心的一步:在持续跟手的 `touchmove` 事件中,依靠 JS 来判断到底该阻止系统,还是让渡给系统。
- **如果已经有系统选区 (`hasActiveNativeSelectionInTerminal() === true`)**
说明用户正在拖拽选区的把手。此时代码**不拦截**,顺从原生的触摸动作,全权交由系统处理文本选取。
- **如果没有选区 (`false`)**
说明用户大概率在普通滑动。此时马上执行 `event.preventDefault()`**强行掐断系统的原生滚动尝试**,以防止 `touchmove` 被系统没收,并将 `dy` 直接交给我们的“自定义JS滚动”。
### D. 自定义动量滚动 (Custom Momentum JS Scroll)
依赖于不被吞噬的连续 move 事件,我们实现了高性能的滚动接管:
1. **跟手滑动**:实时计算两次触屏事件的 `dy` (delta Y),直接映射 `viewportScroller.scrollTop -= dy`实现完全的1:1跟手。
2. **惯性衰减 (Momentum)**:记录滑动撒手 (`touchend`) 瞬间的最后速度,将其转入 `requestAnimationFrame` 驱动的动画循环中进行指数级衰减,精美模拟原本仅有 OS 层才能给到的平滑阻尼滚动感受。
## 4. 最终达成的效果 (Final Results)
凭借这种高度克制且动态的干扰策略,我们实现了“鱼与熊掌兼得”:
1. **完美原生选区**:长按仍能顺畅唤起 iOS 文本放大镜与蓝色选框。
2. **不断流的跟手滚动**:只要没有进入选区模式,手指在密集的字符网格上狂滑也不会触发浏览器原生手势劫持,配合 60fps 的 JS 帧动画达到了极致丝滑。
3. **软键盘完整兼容**:没有使用全局焦点的强行剥夺,不阻碍日常的点击输入与键盘显隐。