浏览器手写输入 (Browser Handwriting Input)
结论
纯网页端能手写,不用 WKWebView 桥。 iPad Safari 从 iOS 13 起就支持 Apple Pencil 的 Pointer Events(pointerType === 'pen',带 pressure 压力 / tiltX/tiltY 倾斜)。在 canvas 上画线即可。WKWebView 桥或原生 app 只有要"原生级"体验时才需要。
相关项目:浏览器内 Agent 服务 (InPage Agent) 的手写画布需求。
完整研发演进史与算法路径详见:数字墨迹与笔刷引擎演进史 (Digital Ink & Brush Engine Evolution)。
手笔手势冲突与平移引擎踩坑详见:批注模式触控笔书写触发页面滚动导致笔画拉丝偏移 (Stylus Annotation Triggers Viewport Pan Causing Stroke Drift)。
实现要点
- canvas 元素设
touch-action: none—— 否则滚动会吃掉笔画;但在可滚动文档(如 PDF)中叠加批注时,需配合内置虚拟平移引擎由手指接管平移。 - 监听
pointerdown / pointermove / pointerup;移动事件用getCoalescedEvents()拿细颗粒度坐标序列。 - 每笔记为 points 数组(pressure 字段),喂给 perfect-freehand 渲染平滑压力线条。
- 落盘:存为 SVG 或点列(JSON),放 IndexedDB。
掌误抑制与手笔分离: 在画布监听事件中严格区分 pointerType =<mark> 'pen'(落墨、preventDefault 锁定视口)与 pointerType </mark>= 'touch'(严禁落墨,手指驱动虚拟平移滚动容器)。
开源选型(2026-09 GitHub CLI 实查)
| 层 | 项目 | 星 | 说明 |
|---|---|---|---|
| 笔画渲染(核心) | steveruizok/perfect-freehand | 5730 | MIT;压力感应平滑线条,tldraw 同款 |
| 完整画布 | tldraw/tldraw | 50k | MIT 类;React 无限画布 |
| 完整画布 | excalidraw/excalidraw | 132k | MIT;手绘风白板 |
| 整体参考(最接近) | penecho/penecho | 2368 | JS;手写画布 + 内置 agent + MCP;AGPL-3.0,抄代码要谨慎 |
| iPad 原生参考 | brianharms/penpal | — | Swift;iPad 手写连 Claude API,回复也手写在同一画布 |
| 手写识别(墨迹→文字) | huggingface/transformers.js | 16k | 本地 WASM 跑 TrOCR,开源不上传;偏重,可后置 |
什么时候才真的需要原生/WKWebView 桥
- 想要 Apple Scribble 系统手写键盘(系统能力,不暴露给 web JS)。
- 更强的掌误抑制 / 原生墨迹识别(Ink)体验。
- 否则:画线、手写笔记、agent 画布,纯浏览器完全够。
"GoodNotes 级" = 三个硬指标(2026-09 调研)
| 指标 | 纯 web 能做到吗 | 怎么做 / 参考 |
|---|---|---|
| 逼真墨迹质感 | 能,自写或调和算法 | 1. SignaturePad 算法 (Square 三次贝塞尔 + 速度插值):连笔书写极流畅,自带笔锋自然收窄,最接近钢笔质感。 2. GoodNotes 模拟引擎 (向心样条 + Hermite S 曲线压感 + 速度阻尼):双层增量渲染,支持钢笔/圆珠笔/秀丽笔/荧光笔。 3. Rnote 笔刷模型 (向心 Catmull-Rom + 低通滤波 + 拐角曲率抑制):解决笔画拐折处的突变与膨胀。 4. perfect-freehand:多边形 Ribbons,开启真实硬件压感时质感偏马克笔/白板笔。 |
| 极低延迟(~16ms) | 逼近,需特定优化组合 | 1. canvas.getContext('2d', { desynchronized: true }) 绕过合成器双缓冲。2. 双层画布 (Backing + Active):当前笔划只在顶层局部画增量段,零全量重绘开销。 3. e.getCoalescedEvents():iPad Pro 120Hz/240Hz 采样全保留,消除折线段感。 |
| 掌误抑制 + 系统识别 | 纯 Web 可做到笔/手分离 | 1. Web 层开启 Pen-Only 模式:e.pointerType === 'pen' 触发绘制,touch 忽略画线,有效解决手掌贴屏误画。2. Apple Scribble 原生系统文字识别需原生层;Web 端文字识别可用 TrOCR / 云 OCR。 |
实测对比实验室 (Web Handwriting Lab)
已在平台部署五种引擎并排体验实验室:https://6767.chat/write
- 涵盖:GoodNotes Ink 引擎、SignaturePad 算法、Rnote 笔刷模型、Perfect-Freehand、原生折线基准。
- 包含实时 HUD:采样率(ProMotion 120Hz 状态)、原生压感值、渲染耗时、米字练习格底纹。
GoodNotes 核心 4 维手感参数数学建模 (实测基准: 75 / 50 / 33 / 0)
笔尖锐度 (Tip Sharpness, 75%):
- 作用:控制落笔初始接触与提笔出锋时的收细锥度。
- 算法:在笔画抬起或末段加速时引入出锋锥形衰减 $w_{\text{tip}} = w \cdot (1 - 0.75 \times 0.75)$,消除圆头钝感,使中文的“撇、捺、勾、悬针竖”自然显现尖锐笔锋。
压力敏感度 (Pressure Sensitivity, 50%):
- 作用:映射 Apple Pencil 硬件压感 $p \in [0, 1]$ 到线条粗细。
- 算法:通过非线性 Hermite / 幂次曲线调和 $w_p = \text{base} \cdot [(1 - 0.8S) + 1.6S \cdot p^{0.9}]$,中度压力下既有轻按发力感,又不会暴胀。
扁平度 (Flatness, 33%):
- 作用:模拟真实钢笔切角笔尖(如平尖/书法尖),创造“横细竖粗”的书法结构。
- 算法:笔尖设为椭圆截面(倾斜角 $\phi \approx -40^\circ$),沿运笔方向 $\theta = \operatorname{atan2}(dy, dx)$ 投影有效线宽:
$$W(\theta) = 2 \sqrt{a^2 \sin^2(\theta - \phi) + b^2 \cos^2(\theta - \phi)}$$
扁平度 33% 对应短半轴压缩约 21%,在汉字横平竖直时产生自然的粗细层次。
画笔稳定度 (Stabilization, 0%):
- 作用:0 代表零粘滞、零拖拽延迟。
- 算法:完全跳过拖尾弹簧/阻尼延时滤波,配合
desynchronized: true与getCoalescedEvents(),笔迹 1:1 紧咬 Pencil 笔尖,实现原生的指尖随动感。
核心手感发现:笔画纠偏机制与拉力收敛 (Laplacian Curve Tension)
在 iPad 玻璃表面书写草书、连笔或花体时,若完全按照原生硬件点拟合样条,线条容易显得“花乱、散漫”(肉眼可见的玻璃打滑与生理手颤)。
GoodNotes 之所以即使在“稳定=0”时线条依然极其收敛、紧绷、有骨力,核心在于几何拉力纠偏机制 (Tension Rectification):
拉普拉斯曲线张力算子 (Laplacian Curve Tension):
- 仅对平滑曲线和连笔弧度段生效(相邻转折角 $\Delta\theta < 68^\circ$)。
- 将连续三点中间点 $P_1$ 沿弦中点 $\frac{P_0 + P_2}{2}$ 方向施加内收拉力 $P_1 \leftarrow P_1 + k_{\text{rect}} \cdot (\text{mid} - P_1)$。
- 效果:消除不规则的微抖,使圆弧、笔圈、连笔呈现如同绷紧橡皮筋般的紧凑内敛感;而在遇到折笔、顿笔($\Delta\theta \ge 70^\circ$)时拉力衰减至零,完整保全锋利转角。
非对称压感滞后回线 (Asymmetric Pressure Hysteresis):
- 落笔按压时快速响应($\alpha_{\text{down}} \approx 0.85$),行笔和收笔时平滑衰减($\alpha_{\text{up}} \approx 0.45$)。
- 消除屏幕电容感应的高频微跳,使笔画宽窄过度极为润泽。
真实压感数据集捕获与回放套件 (Dataset Studio):
- 支持逐帧捕获 Apple Pencil 原生硬件微秒级时间戳、坐标、真实压感($p$)与倾斜度($tiltX, tiltY$)。
- 导出为标准 JSON 数据集,支持在实验室中随时载入并通过不同纠偏/张力参数实时重演回放,用于算法调优拟合。
关键突破:非线性双端有界压感模型 (Dual-Bounded Non-Linear Pressure Transfer Functions)
在真实书写体验中,笔画粗细与 Apple Pencil 硬件压感 $p \in [0, 1]$ 绝非线性关系。人类手指对触觉阻力和按压力的心理感知严格遵循韦伯-费希纳定律 (Weber-Fechner Law) 与史蒂文斯幂律 (Stevens' Power Law):
- 轻写起点高 (High Baseline Floor):极轻笔尖接触($p \to 0$ 或 $0.05 \sim 0.15$)必须有坚实的基础保底粗细($50\% \sim 60\%$ 基准线宽),否则线条会如蛛丝般纤细、脆弱、打滑。
- 重写饱和封顶 (Ceiling Saturation):深重下压($p > 0.5$)必须有平滑的渐进封顶($120\% \sim 135\%$),防止墨迹过度肥大失控。
- 笔锋敏感区 (Working Dynamic Zone):汉字顿笔、收笔的粗细变化主要集中在 $p \in [0.15, 0.45]$ 的微力区间。
公式结构:
$$W(p) = \text{base} \cdot \left[ 1.0 + 2S \cdot \left( F + (C - F) \cdot g(p) - 1.0 \right) \right]$$
其中 $F$ 为保底粗细比(默认 55%),$C$ 为封顶饱和比(默认 130%),$S$ 为压力敏感度(默认 50%),$g(p) \in [0, 1]$ 为五类非线性传递函数模型:
| 模型名称 | 数学映射 $g(p)$ | 核心手感特征 | 适用场景 |
|---|---|---|---|
| 对数压缩 (Logarithmic) | $\frac{\ln(1 + \beta p)}{\ln(1 + \beta)}$ ($\beta \approx 8$) | 触觉感知定律,轻起步快,中段饱满,重压平滑贴顶 | GoodNotes 经典手感,日常中文字符流畅书写 |
| Sigmoid S型阻尼 | $\frac{1}{1 + e^{-k(p - p_0)}}$ ($p_0 = 0.28$) | 中心拐点两侧收敛,中度运笔极舒展,深压物理阻尼软锁 | 行草连笔、快写,防止大弧度笔画暴肥 |
| 双曲正切弹性 (Tanh) | $\frac{\tanh(\alpha p)}{\tanh(\alpha)}$ ($\alpha \approx 3$) | 纯物理弹簧-阻尼器模型,下压如同按入厚实纸垫,有回弹骨力 | 秀丽笔、书法顿笔 |
| 幂律软饱和 (Power Gamma) | $p^\gamma$ ($\gamma \approx 0.38$) | 近零斜率极陡,起步瞬时有肉感,平滑凹面压缩 | 圆珠笔、钢笔素描 |
| 分段双斜率膝点 (Piecewise Knee) | $p \le 0.32$ 快速爬升;$p > 0.32$ 强阻尼平缓 | 双折线拐点,出锋提笔与顿笔按压层次对比鲜明 | 硬笔书法、正楷、方正字形 |
实测定案结论:多维混合调和律(终选对数压缩模型 Logarithmic)
“所有单一变量都不能带来质变,手感是多种物理与感知变量协同混合而成的。”
实测验证证明,GoodNotes 的原生手感并非来自单一神奇参数,而是由五个相互制约、相互补偿的维度构成的动态平衡体:
- 对数压感压缩 (Logarithmic, 终选):提供了“轻写高起点(55% 保底)与重压软封顶(130% 饱和)”的基石,使起笔不飘、重压不肥。
- 扁平切角笔尖 (Flatness 33% @ -40°):打破了圆管线条的单调,使汉字在运笔转折时天然自带“横细竖粗”的刚骨。
- 拉普拉斯曲线张力纠偏 (Laplacian Tension 50%):消除玻璃表面的打滑虚浮感,把连笔和圆弧紧绷收敛,又在转折尖角处自动释放。
- 出锋与笔尖锐度 (Tip Sharpness 75%):在提笔末端以二次曲线锥形收窄,使汉字的“勾、撇、捺、悬针”拥有尖锐锋芒。
- 动态速度变细与非对称滤波:快速挥洒时线宽自然变细,压感下压即刻响应、提笔润泽衰减。
最终黄金手感定案配置清单 (Final Golden Baseline Configuration)
export const GOODNOTES_GOLDEN_BASELINE_CONFIG = {
engine: 'goodnotes',
penType: 'fountain', // 钢笔笔型
size: 3.5, // 基准字号线宽: 3.5px
tipSharpness: 0.66, // 笔尖出锋锐度: 66%
pressureSensitivity: 0.66, // 硬件压力敏感: 66%
flatness: 0.66, // 扁平切角: 66% (产生鲜明横细竖粗骨架)
nibAngle: -0.6981, // 笔身切角: -40°
stabilization: 1.20, // 画笔稳定: 120 (0-200 连续微调,对应 66 舒适黄金档,0.85+平滑映射到高阻尼)
rectification: 1.00, // 几何纠偏张力: 100% (圆弧草书完全收敛,折笔尖角不钝)
wobbleFilter: 0.60, // 玻璃高频微颤抑制: 60%
// 非线性压感模型 (Logarithmic Weber-Fechner 对数起步)
pressureModel: 'logarithmic',
pressureFloor: 0.20, // 轻写起步保底: 20% (轻下笔清脆有骨)
pressureCeiling: 1.00, // 重压饱和封顶: 100% (深重按压严密锁定,绝不肥厚失控)
pressureGain: 1.80, // 对数曲率增益: 1.8x (微力区快速过渡到饱满)
// 直线抗抖与消除节瘤波纹 (第4格满血去波纹配置)
headingFilter: 0.85, // 笔向切角惯性滤波: 85% (彻底锁死椭圆自转闪烁)
resampleStep: 2.2, // 等距重采样降噪步长: 2.2px (滤除电容屏0.2px亚像素抖动)
railTension: 0.70, // 导轨趋势张力: 70% (直线自适应引力走直,折角瞬间释放)
widthViscosity: 0.75, // 粗细速度粘滞阻尼: 75% (抹平加减速顿挫脉冲)
lateralDrag: 0.65 // 侧向位移摩擦阻尼: 65% (模拟纸张侧向行进阻力)
};提笔抖动与突然变细的根因与像素级保真 (Zero-Jitter Active Canvas Commit)
- 提笔抖动根因:提笔
pointerup阶段若盲目调用全笔重绘(redrawCanvas),会导致当前笔划被拉普拉斯全量平滑重新拟合,破坏实时增量贝塞尔曲线的自然连接。 - 提笔变细根因:全笔重绘时若缺失笔型线宽增益乘数(如钢笔笔尖的
1.15x墨水延展),会导致整条线条在落笔与提笔瞬间粗细突跳。 - 终极解决方案:采用 1:1 位图直接转移(
mainCtx.drawImage(activeCanvas, 0, 0)),屏幕上运笔时眼睛看到什么,提笔就位图锁定什么,绝对零抖动、零跳跃、零变细。
工作台极简交互范式:四档微调 (Stepped Fine-Tuning: 0 / 33 / 66 / 100)
在全参数收敛锁定后,工作台交互彻底从“三十余个连续细分滑动条”重构为“极简画布 + 四档离散步长切换按钮”:
- 核心参数按四档步长离散化:
0% (关)/33% (轻度)/66% (默认舒适黄金档)/100% (强力/满档)。 - 画笔稳定度支持
0 ~ 200放大区间,默认快捷键66严格锁定120舒适黄金档,并附带内联滑块支持无级连续微调。 - 用户无需在毫米级滑块上反复对齐,直接点击分段 Toggle 按钮即可即时微调体验。
- 原始全参数实验室归档为独立路由
/write/archive.html,主路由/write/与极简工作台/write/simplify默认启用黄金配置。
直线抗抖与消除节瘤波纹 (Straight Stroke Anti-Wobble & De-Tamping)
1. 现象与物理/算法根因剖析 (Symptoms & Root Causes)
在 iPad 光滑玻璃表面快速下拉画竖笔或直线时,极易产生一连串肉眼可见的“粗细波纹 / 节瘤串珠 (Tamps / Camps)”,而 GoodNotes 在画直线时却异常顺直且带有明显的“玻璃阻尼感”。
通过捕获并实测分析真实 12 笔抖动直线数据集(ds_1789866707643_f94ed7,共 3,604 个物理采样点,平均点距仅 0.27px),发现节瘤波纹并非单纯是手抖,而是三种物理与几何现象的复合共振:
电容屏高频亚像素微颤 (Subpixel Capacitive Noise):
- 屏幕采样率极高(单笔 148px 产生了 544 个采样点,点距小于 0.3px),手指与笔尖微颤会导致坐标横向微小左右晃动(实测单笔横向折返达 54 次)。
切向角翻滚放大效应 (Nib Angle Twisting & Flickering,核心根因):
- 拟真书法引擎为了实现“横细竖粗”,笔尖采用了扁平椭圆截面(Flatness 33% @ -40°),瞬间线宽直接取决于切向角 $\theta = \operatorname{atan2}(dy, dx)$。
- 当纵向运笔出现极微小的横向扰动($dx$ 在微米级波动)时,$\operatorname{atan2}$ 会在 $45^\circ \sim 135^\circ$ 之间急剧翻转跳跃(实测单笔角跳 $>30^\circ$ 出现 259 次!)。
- 后果:笔尖椭圆在零点几毫米内疯狂来回旋转,把原本只有 0.3px 的横向微颤放大为数十倍的视觉粗细突变,形成如同“串珠/节瘤”般的锯齿波纹。
速度微顿引起的线宽脉冲 (Velocity Stutter):
- 手部克服玻璃静摩擦力时的微小顿挫导致瞬时速度忽快忽慢,使速度变细函数产生连续的粗细膨胀与收缩。
2. 五维调直与去波纹消除算法 (5-Dimension De-Tamp Pipeline)
针对以上机理,在 GoodNotes 引擎管线中引入五维协同滤波机制:
[原生硬件点列]
│
▼
[1. 间距降噪重采样 (Resample Step)] ────► 滤除 <2.2px 的电容屏高频亚像素抖动 (噪点消除率 87%)
│
▼
[2. 笔向切角惯性向量滤波 (Heading Filter)] ──► 二维方向向量 EMA 阻尼,彻底锁死切向角,消除切角旋转闪烁
│
▼
[3. 侧向位移阻尼 (Lateral Drag)] ───────► 沿局部正交法向量分解,压缩垂直行进方向的横向打滑
│
▼
[4. 导轨趋势张力 (Straight Rail Tension)] ─► 识别直线走势(<25°),自适应将微摆向局部弦轴心靠拢
│
▼
[5. 粗细粘滞阻尼 (Width Viscosity)] ────► 一阶惯性低通平抑速度突变,彻底抹平粗细节瘤
│
▼
[输出顺直且具阻尼的拟真笔迹]笔向切角向量滤波 (Heading Filter, 推荐 85%):
- 不使用离散标量角度,而是维护归一化的二维行进单位向量 $(\hat{u}_x, \hat{u}_y)$:
$$\vec{v}_{\text{head}} \leftarrow \vec{v}_{\text{head}} \cdot \alpha_{\text{head}} + \Delta\vec{p} \cdot (1 - \alpha_{\text{head}})$$ - 切角平滑过度,角度跳跃从 259 次降低到 18 次以下,笔尖椭圆截面保持稳定指向,彻底消除波纹节瘤。
- 不使用离散标量角度,而是维护归一化的二维行进单位向量 $(\hat{u}_x, \hat{u}_y)$:
等距降噪重采样 (Resample Step, 推荐 2.2px):
- 沿笔划弧长设定步长门限 $d_{\text{step}} = 2.2\text{px}$,剔除亚像素微距离冗余点(544 点精简至 71 点),直接消除屏幕传感器的微观锯齿。
导轨趋势自适应引力 (Straight Rail Tension, 推荐 70%):
- 实时计算局部弦向量 $\vec{AB}$ 与当前点 $P$ 的夹角。当夹角 $< 25^\circ$ 时判定为直线段走笔,将 $P$ 点向弦投影点拉回:
$$P \leftarrow P + (\text{Proj}_{\vec{AB}}(P) - P) \cdot k_{\text{rail}}$$ - 当遇到折线(夹角 $\ge 25^\circ$)时引力瞬时衰减为 0,确保起笔顿笔与转折尖锐。
- 实时计算局部弦向量 $\vec{AB}$ 与当前点 $P$ 的夹角。当夹角 $< 25^\circ$ 时判定为直线段走笔,将 $P$ 点向弦投影点拉回:
粗细粘滞阻尼 (Width Viscosity, 推荐 75%):
- 线宽一阶低通滤波 $w_t = w_{t-1} \cdot (1 - \alpha_v) + w_{\text{target}} \cdot \alpha_v$,吸收运笔速度微颤。
侧向位移阻尼 (Lateral Drag, 推荐 65%):
- 分解运笔位移为切向分量 $d_\parallel$ 与法向分量 $d_\perp$,将法向横摆衰减 $d'_\perp = d_\perp \cdot (1 - 0.72 k_{\text{lat}})$,产生极具质感的“纸张侧向摩擦阻力”。
3. 直线抗抖专属工作台与 4 格矩阵对比 (Straight Stroke Studio)
已独立上线子路由工作台:https://6767.chat/write/straight.html
同屏 4 格对照:
- 格 1:原生抖动未滤波 (54 次横向折返、强烈波纹节瘤)
- 格 2:仅笔向惯性滤波 (切向角锁死、消除切角闪烁波纹)
- 格 3:笔向 + 间距降噪 (消除 0.2px 传感器高频锯齿串珠)
- 格 4:GoodNotes 满血调直 (导轨张力 + 侧阻 + 粗细粘滞,线条顺直收敛)
- 实时五维滑块:可直接在 iPad 屏幕上随写随调,支持一键载入实录 12 笔抖动直线数据与导出代码配置。
跨端手写踩坑与手势架构实战 (2026-09 生产落地总结)
1. Apple Pencil 2 偶发丢失捕获断笔陷阱 (WebKit Lost Pointer Capture Pitfall)
- 现象 (Symptoms):Apple Pencil Pro 在 iPadOS 上运笔平滑,但在 Apple Pencil 2(或部分低压感手写笔)快速连笔、手掌边缘微触屏幕时,笔画出现明显的断断续续、吞笔画或被切成碎断点。
根因分析 (Root Cause):
- W3C 指针捕获规范(Pointer Capture API)中,通过
setPointerCapture(pointerId)锁定事件源;但在 iOS WebKit 实现中,当手掌有轻微边缘触碰或手指手势引擎介入判断时,WebKit 会单方面向 canvas 抛出lostpointercapture事件,即使此时笔尖并未离开屏幕物理表面。 - 旧有手写实现若将
lostpointercapture监听视同“提笔收笔”(调用finalizeStroke并重置isDrawing = false),后续持续产生的物理pointermove采样点就会因校验不通过而被直接丢弃,导致书写被掐断。
- W3C 指针捕获规范(Pointer Capture API)中,通过
治理策略 (Resolution):
- 彻底移除
lostpointercapture强制收笔逻辑。 - 收笔提笔出口严格且仅收敛于两种确凿场景:真实提笔的
pointerup事件,以及系统级确凿中断的pointercancel事件。 - 在
pointermove中持续维护活跃指针,杜绝 WebKit 误报丢捕获带来的非预期截断。
- 彻底移除
2. 无限可写画卷手势系统与手指导航模式 (Infinite Canvas & Touch Gesture Architecture)
设计权衡 (Trade-off):
- 手写板传统模式往往直接允许手指落墨,导致手掌误触(Palm Rejection 偶发漏检)在纸面上留下大量杂点碎墨,且无法便捷移动与缩放画布。
两级手势状态机:
手势导航模式(默认推荐):
- 手指(
pointerType === 'touch')绝对不落墨。 - 单指/多指拖拽:驱动视口平移($panX \leftarrow panX + dx, panY \leftarrow panY + dy$);在嵌入文档(如 PDF)模式下则优雅平移文档滚动容器。
- 双指捏合(Pinch-to-Zoom):以双指几何中心点为锚点无极缩放(0.2x ~ 5.0x),并同步叠加中心点位移量($zoomAndPan$)。
- 仅触控笔书写:笔尖接触(
pointerType === 'pen')方可落墨。
- 手指(
允许手指书写模式(可选切换):
- 单指落墨书写。
- 视口平移与缩放转移至双指拖拽与双指捏合接管;一旦检测到 $\ge 2$ 指触碰,立即丢弃单指起笔的残墨(
cancelActiveStroke),实现无缝手笔分离。
世界坐标系统一 (World vs Screen Coordinates):
- 笔画采样点统一存储为不受平移缩放影响的世界真实坐标($x_{\text{world}} = (x_{\text{screen}} - panX) / zoom$)。
- 画布仅使用 Canvas 2D 变换矩阵(
dpr * zoom,panX * dpr)渲染,平移缩放具有零重绘计算开销与硬件抗锯齿优势。 - 导出图片(PNG)时自动根据世界坐标中所有笔画的实际包围盒(Bounding Box)进行智能自适应裁剪,输出 2x 高清位图。
3. 旧版 Android / 华为平板 WebView 现代 API 缺失与动态懒加载 (Legacy WebView Compatibility)
- 现象:现代浏览器(iPad Safari、桌面 Chrome)运行正常,但在华为平板等搭载 HarmonyOS 4.0 早期 Chromium M99 内核的设备上打开应用直接白屏。
根因:
- 现代 JavaScript 运行环境 API 缺失:Chromium <119 缺少
Promise.withResolvers,Chromium <126 缺少URL.parse,以及缺少structuredClone等,导致 SPA 框架在初始化执行顶层模块时抛出全局TypeError,整页停滞。 - 现代重型引擎首屏静态捆绑(Static Import):如
pdfjs-dist等现代 PDF 引擎体积大(>2.5MB)且包含现代高级语法,在首屏未经拆包直接加载时容易导致低内存/低版本内核直接崩溃。
- 现代 JavaScript 运行环境 API 缺失:Chromium <119 缺少
治理策略:
- 在 HTML 最早期注入内联 Polyfill 兜底与全局未捕获白屏诊断卡片,并在入口
main.ts最首行挂载 Polyfill 补丁垫片。 - 动态按需拆包(Dynamic Import):将 PDF 引擎从首屏关键路径中剥离,仅在用户实际打开 PDF 文件时才异步动态载入(
await import('./pdfViewer')),首屏关键 JS chunk 体积直接减小 500KB+,彻底杜绝首屏内核崩溃。
- 在 HTML 最早期注入内联 Polyfill 兜底与全局未捕获白屏诊断卡片,并在入口
边界
- 识别(墨迹→文本)比捕获难一个量级:on-device 用 transformers.js + TrOCR,或云 OCR;MVP 建议先只做捕获 + 展示。
- Android Chrome 同样支持 Pointer Events + 触控笔(S Pen 等)。
本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Frontend