Files
ArtPlayer/refactor/changes/2026-09-11-CORE-17-input.md
T

89 lines
17 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.
# CORE-17 键盘、手势、焦点与全局事件
状态:完成。以下阶段记录保留当时的进度;当前交付结论以末尾最终验收为准。
先核实公开键盘注册、焦点及跨 document 行为,保留回调 this、参数、顺序、可变 keys 数组和链式返回。后续处理原生监听器注册/释放、全局 document/window 重绑、点击/悬停/移动/触摸,以及 resize/view/RAF 调度归属。
源码观察:Hotkey 的输入判断固定读取全局 document.activeElement,重复 init 会重复订阅;Events 的重绑先删除旧监听,失败期间可能保留部分新监听;触摸只处理结束、缺少取消。尚未复现的点不登记为已确认缺陷。BASE-PERF-01 的 pending resize 已在 CORE-04 修正,本任务继续回归,不重复宣称新修复。
## 第一批:Hotkey
hotkey.ts 保留 art/keys 自有字段、init/add/remove 原型方法、keys 的普通对象原型与数组身份、链式返回、回调 this=art 和原同步事件顺序。input/hotkey-defaults.ts 管理稳定的默认回调;keyboard-focus.ts 判断事件 document 与编辑区域;hotkey-types.ts 描述最小泛型宿主。保持回调数组的旧实时遍历语义,注册/删除中途修改数组仍直接影响本次派发;不借本次迁移静默改成快照模型。
发布/候选初始基线 24 项中,15 通过、候选目标 9 失败,复现 iframe 与 Shadow DOM 输入被快捷键拦截,以及重复 init 触发两次。迁移后 24 项通过。随后补充已发布包的 prototype-named key 复现:constructor/toString/__proto__ 的注册会因继承属性报错;候选保持普通 keys 原型,用自身属性注册消除碰撞。分别登记 BASE-DOM-16/17 与 BASE-LIFE-23,当前均保持 open,等待完整 CORE-17 安装包出口。
键盘判断优先读取实际事件 document,回退到 player 当前 ownerDocument,并检查深层 shadow focus/composed target;input/textarea/select、继承或 plaintext 编辑区、IME 组合输入不触发快捷键,仍保留通用 keydown 通知。修饰键规则保持。实例 scope 清理订阅,销毁中回调不再继续后续回调或 hotkey/keydown 通知;同步回调错误仍保留身份。重复 init 复用默认回调,并可恢复显式移除的默认键;移动端默认不初始化,原手动 init 路径保留。
新增 9 项 Node 测试(与已有播放测试合跑 23 项),覆盖公开形态、默认与自定义键、重复初始化/恢复、回调上下文与数组修改、销毁、错误、修饰键/IME 和空 activeElement。类型用例保留泛型扩展与 this,拒绝错误事件/接收者类型。浏览器扩展首轮 45 项通过;最后补入移动 UA 的旧手动 init 行为,并与显示模块组合回归。未新增依赖,test:unit 加入键盘用例。
Events 注册/重绑、点击/手势/悬停以及 view/resize/RAF 仍未完成;本阶段不标记整个 CORE-17 完成,也不作完成任务提交。
本批最终 CI 278 项(249 单元、4 工程、25 基线)、168 个生产 TS 文件和五组消费者严格检查通过;键盘与显示模式组合 351 项三浏览器通过,含键盘 51 项。核心三个格式和 i18n 构建通过,docs/compiled 一致;独立重建候选并逐项核对浏览器 manifest,见 [键盘阶段证据](../baselines/hotkey-validation.json)。这不是全量源码浏览器或最终安装 UMD/legacy 验收。回退本批时恢复 hotkey.js,删除 input/hotkey 与 keyboard-focus 模块和对应测试,再通过仓库脚本重建;保留基线缺陷记录。
## 第二批:原生监听器与全局重绑
events/listener-registry.ts 管理原生监听器注册、capture 快照、数组失败回滚、abort 和清理;events/index.js 仍保留旧门面、bound proxy/hover、destroyEvents Set 与初始化顺序。返回的 disposer 直接执行后移除记录,重复执行无副作用。移除失败警告并保留记录供后续 remove/destroy 重试,其他监听器继续清理;自定义 target 永久拒绝移除仍属于明确能力边界。
保留原回调交给原生 EventTarget,因此 this、event 身份、外部 removeEventListener、原生 once/重复注册语义保持。once 触发后,记录在显式 disposer 或实例销毁时清除;不为了提前清记录包装用户回调。相同回调的重复注册不做引用计数。abort 清理记录,预先 aborted 的信号不注册。测试还发现本次迁移曾误拒绝 null options:旧包三浏览器通过、初始候选三浏览器失败,已恢复原生默认行为并保留回归用例。
原生注册初始基线 24 项中旧包 12 通过、候选目标 12 失败;迁移后配合生命周期 54 项通过。补入 null、once/abort 后,与键盘/生命周期合跑 117 项通过。新增 9 项 Node 测试覆盖原生身份、可变选项、对象监听器、取消、注册后抛错、销毁中注册、清理失败重试、手动 destroy 后复用和重复注册。确认 BASE-LIFE-24,保持 open 等待整个 CORE-17 安装包出口。
events/globalInit.ts 与 global-types.ts 拆出全局监听组及最小宿主。先注册完整新组,成功后再切换活动组并释放旧组;失败回滚新组,保留旧组可用。暂存组不转发事件,代次检查确保嵌套重绑由更新调用生效,销毁中断的注册不会晚到激活。保留原事件名称/身份、void 返回、document/window 独立回退规则(单传 document 不隐式更换 window)及成功重绑后不再接收旧目标事件。
实际 iframe 的初始基线 12 项中 9 通过、候选失败回滚目标 3 失败,证实旧实现失败后失去旧 document 并保留部分新监听。迁移后与键盘合跑 63 项通过;再补浏览器嵌套重绑/销毁检查。5 项 Node 测试覆盖 ownerDocument 变化、window 注册失败、暂存期间事件、嵌套重绑与销毁。确认 BASE-LIFE-25,保持 open;两组共 14 项 Node 测试通过。未新增依赖,已把两组测试接入 test:unit。
以上键盘阶段指纹只对应第一批历史源码,不能代表第二批。第二批最终 CI 292 项(263 单元、4 工程、25 基线)、171 个生产 TS 和五组消费者严格检查通过;键盘/原生监听器/全局重绑/生命周期组合 135 项三浏览器通过,无重试、跳过或页面错误。核心三个格式和 i18n 已重建,docs/compiled 字节一致;独立重建并逐项核对候选 manifest,见 [监听器及重绑阶段证据](../baselines/input-listeners-validation.json)。这是源码阶段的局部组合回归,不是最终安装包全量验收。
点击、悬停、移动、手势和 resize/view/RAF 仍待实施。回退本批时恢复 events/index.js/globalInit.js,删除新的 listener-registry/globalInit/global-types TS 与测试,并用仓库脚本重建。整个 CORE-17 仍为 doing,不作完成任务提交。
## 第三批:点击、焦点、悬停与移动
clickInit.ts、hoverInit.ts、moveInit.ts 迁移为 TS,pointer-types.ts 定义最小宿主/监听接口,pointer-focus.ts 独立管理文档焦点订阅。保留原始 Event、类名变化先于 hover 通知、isInput 仅识别 INPUT 的旧语义、单/双击闭区间阈值、第三次点击重置、动态静态配置与嵌套同步派发。Emitter 的 this 仍由显式 ctx 参数决定,不能套用 hotkey 的 this=art 约定。销毁移除自有焦点订阅,并阻止失效的点击/悬停/移动回调继续修改状态。
确认 BASE-LIFE-26:桌面 click 或移动 UA dblclick 回调销毁实例后,旧实现仍调用原生媒体 play。候选在用户事件回调返回后检查实例生命周期,同时在处理入口拦截已关闭实例;单元测试也验证销毁时不再进入 fullscreen 默认动作。该修正不改变正常单击播放、双击行为、用户异常身份及内部播放拒绝处理。
最初浏览器夹具错误地替换不可重定义的 toggle 属性,导致 18 项失败;修正为观察底层 video.play 后,暴露出默认 Emitter ctx 的错误测试假设。保留了两次失败报告,不把这些计为产品缺陷;第二次报告中另有 6 项候选销毁目标失败,均复现上述真实问题。最终测试显式传 ctx,保留旧不可配置属性不变。修复后 24 项三浏览器契约通过,再新增实际鼠标点击、播放/暂停、焦点和 hover 的 6 项浏览器验证通过。新增 8 项单元测试通过,包括阈值、嵌套派发、销毁、异常与自有订阅清理,已接入 test:unit,未新增依赖。
最终 CI 300 项(271 单元、4 工程、25 基线)、176 个生产 TS 与五组消费者严格检查通过。首次组合浏览器为 164 通过、1 项 WebKit 键盘定位失败;该测试设置初始 currentTime 后未等待定位完成,现补充 paused/seeking 前置条件,并断言方向键确实进入 hotkey。保留失败记录,修正测试后组合 165 项三浏览器通过,无重试/跳过/页面错误,含指针交互 30 项。核心三个格式与 i18n 重建通过,独立重建候选并核对每项 manifest、源码及 docs/compiled 指纹,见 [指针阶段证据](../baselines/pointer-validation.json)。
前两批证据仅代表各自历史源码。本批是局部源码组合回归,手势、resize/view/RAF 与 Events 门面还待迁移,整个 CORE-17 安装包出口未完成。回退本批时恢复三个 Init.js,删除 pointer-focus/pointer-types 和对应测试,通过仓库脚本重建。保持 doing,不作完成任务提交。
## 第四批:触摸手势
gestureInit.ts 负责移动端安装与选项分支;gesture-controller.ts 管理触点、源身份、方向快照和结束/取消/锁定订阅;gesture-direction.ts 保留原两像素阈值与对角边界;gesture-types.ts 描述最小宿主和当前手势。gesture=false 仍支持进度条触摸,isLive 不安装;普通视频拖动保留 TOUCH_MOVE_RATIO,进度条拖动保留全尺寸倍率,旋转时保留纵向计算。
初始 36 项三浏览器基线:21 通过、15 个候选修正目标失败。复现 touchcancel、锁定、多指、触点 identifier 改变后继续沿用旧拖动,以及 seek 回调销毁后继续发出 setBar。分别登记 BASE-LIFE-27/28。候选取消失效手势,并在用户回调后检查手势/实例/源身份;进度条触摸也不会在 setBar 回调销毁或启动新手势后继续旧 seek。失败起始回调保留异常身份并取消未完成手势。
control/progress/position.ts 改为最小宿主,并增加可选的 active 回调,供触摸调用在 setBar 与 seek 之间检查生命周期;原双参数桌面调用默认行为保持,纳入既有组件回归。新增 document:touchcancel 转发及同名公开 Event 声明,旧事件/类型保持。原生目标也监听取消,因此全局 document 被重绑后仍可释放自身手势。源切换通过既有 source 身份取消,避免依赖延迟到达的媒体事件。
12 项单元测试通过,覆盖轴/阈值、绝对/相对顺序、锁定/取消/多指/换指、源/旋转改变、无效尺寸、销毁、重入、错误和订阅清理。类型用例校验 TouchEvent 输入和新增取消事件。首轮手势+重绑浏览器为 52 通过、2 项 WebKit 用例停在触摸执行前的初始定位,最终媒体仍接近 0,未进入手势断言;保留失败记录。准备步骤显式播放/暂停以初始化解码器后,54 项通过;另外独立覆盖首次播放前的 seek 请求契约。扩展进度条绝对定位/销毁和原事件顺序后,手势 54 项三浏览器通过。
本批最终 CI 312 项(283 单元、4 工程、25 基线)、180 个生产 TS 与五组消费者严格检查通过;输入/生命周期/组件组合 303 项三浏览器通过,含 54 项手势与 84 项组件回归,无重试/跳过/页面错误。核心三个格式和 i18n 已重建,独立重建候选核对全部浏览器 manifest,源码/产物/文档副本指纹一致;公开声明差异逐字核对,仅增加 document:touchcancel。见 [手势阶段证据](../baselines/gesture-validation.json)。
浏览器触摸载荷和 Android UA 是受控输入,使用实际 DOM、媒体及定位;不将其宣称为实体手机触摸、滚动仲裁或系统取消的验收。未新增依赖,单元用例加入 test:unit。剩余调度/Events 门面和整个 CORE-17 安装包出口仍待完成;任务保持 doing。回退本批时恢复 gestureInit.js 与 position.ts,删除新的 gesture 模块和测试,撤销新增取消转发/声明后按仓库脚本重建。
## 第五批:调度与 Events 门面
resizeInit.ts 保留合并触发、动态 RESIZE_TIME、标准状态 autoSize、宽高比恢复与 notice 清理,并在重入销毁后停止后续写入。方向事件按 owner window 的 addEventListener 能力注册,不再依赖 onchange 已被赋值。viewInit.ts 拥有首发节流的复位定时器和订阅,保留构造时 SCROLL_TIME、动态 SCROLL_GAP、无尾随通知及原同步嵌套时序;viewport.ts 按 container 当前 ownerDocument 计算,但不更改原边界/容差公式。公共 throttle/debounce/isInViewport 不改。
updateInit.ts 保留 USE_RAF 开关、首次 playing 时立即通知、暂停不发 raf 和单个待执行帧;实例销毁或 raf 回调内销毁不再安排新帧,显式 destroy 通知仍可取消活跃实例的待执行帧。subscriptions.ts 以明确事件映射管理内部订阅并在实例关闭时移除;scheduling-types.ts 定义最小宿主。
events/index.ts 完成门面迁移,types.ts 组合初始化器宿主;保留 destroyEvents/proxy/hover/bindGlobalEvents 自有字段、原型方法、bound proxy/hover、注册返回和初始化顺序。bindGlobalEvents 使用 declare,不在构造开始时生成额外字段。strict 检查暴露的泛型推断问题通过显式事件映射修正,没有用 any 绕过;类型用例验证注册重载、回调 payload 和订阅边界。
初始 24 项浏览器基线为 15 通过、9 个候选目标失败。确认方向 onchange=null 时无 resize、销毁残留 scroll 节流定时器、跨 iframe 使用错误 viewport;登记 BASE-LIFE-29/30、BASE-DOM-18。首轮迁移后与生命周期组合 51 通过、3 个 viewport 用例仍失败,检查发现测试 top=150 恰好处于旧 50 像素容差边界;改为 top=250,保留闭区间规则并加入单元边界检查,不把容差规则改掉来迎合测试。补入实际 RAF/播放/销毁后,60 项三浏览器组合通过。9 项新增单元测试通过并接入 test:unit。
源码阶段 CI 321 项(292 单元、4 工程、25 基线)、188 个生产 TS 文件与五组消费者严格检查通过。实际 core/chapter tarball 安装后 27 项运行时检查、五组类型零诊断。核心三个格式及 i18n 已重建;最终安装 UMD/legacy 完整浏览器矩阵仍在验收,未将本任务标记完成。此前阶段报告仅记录当时源码,最终结论将以完整安装包指纹为准。
## 最终验收
- Hotkey、Events 门面及全部输入初始化器已迁移 TS,拆出键盘焦点/默认键、原生监听器、全局监听组、指针焦点、手势控制/方向、可见性和调度宿主。核心 183 个与 chapter 5 个生产 TS 文件严格检查通过;主播放器入口的剩余整合继续由后续任务处理。
- yarn ci:check:321 项通过(292 单元、4 工程、25 基线)。实际 core/chapter tarball 仓库外安装后 27 项运行时、五组类型零诊断。
- 最终安装 UMD 和 legacy 各完成 1119 项全量及 24 项补充三浏览器检查,分别对同一安装产物累计验证 1143 项;其中输入专项合计 243 项,既有显示模式 300 项;无重试、跳过或未处理页面错误。保留了早期失败记录与修正原因,未把早期阶段指纹混作最终证据。
- [最终证据](../baselines/input-validation.json)核对全部当前核心源码与打包快照、核心和 chapter 实际浏览器产物、全部包内文件、三个分发格式与 docs/compiled、22 个语言文件和独立分发许可。
- 保留公开字段/原型、方法绑定、回调数组和 native listener 身份、Emitter 显式 ctx、普通点击/触摸/调度时序。公开声明仅增加 document:touchcancel,其余内容与父提交逐字核对一致;五组旧/新 TS 消费者仍通过。
- 实际鼠标/键盘播放、定位、iframe 焦点/可见性和 RAF 已验证。触摸载荷、Android UA、IME、方向事件等包含受控路径;实体 iOS/Android 手势、滚动仲裁、系统取消、旋转和安全区仍交 REL-03/REVIEW-02。
- 保留原生 once/重复注册语义;once 记录在显式 disposer 或 owner 清理时释放。永久拒绝移除的自定义 target 只能警告并保留可重试记录,不承诺强制恢复外部副作用。
- 无新增依赖、锁文件或版本修改。六个新增 Node 测试文件已接入 test:unit,并补充类型与浏览器用例;既有公共类型差异仍由 CORE-21 协调。回退时恢复输入入口/初始化器与进度位置辅助函数,删除新增输入模块和用例,撤销新增取消事件,再通过仓库脚本重建,保留基线记录。
关闭本任务十一项输入/生命周期差异;BASE-PERF-01 的既有 resize 修复仅作回归验证,不重复计为新修复。控件 Tab 可达性 BASE-DOM-01 继续由 CORE-23 完成,未在本任务关闭。当前 214 项中 53 完成、161 待办,下一项 CORE-18 内置插件和提示模块。本任务独立本地提交,不推送或发布;全生态迁移和发布复盘继续按完整计划推进。