受控输入框:按字形限长与注入
把前面拆过的概念合进一个真实输入组件,三件事最容易出错:限长该数用户感知的「一个字符」而非 .length;文本里可能混入 看不见的控制字符,既要能受控注入演示,也要能被察觉;同一个 emoji 在各平台字形不一,要一致显示就得换成自带的 SVG。文本框本身交给
@vega/smart-input 接管输入法、撤销与拦截,本页只描述一个 formatter。
1 · 工作台
编辑区用 contenteditable 加富渲染而非纯 <textarea>,因为后者渲染不了内联图片。每个 twemoji 收录的 emoji 在编辑区里直接用 SVG 显示,取不到资源时回退系统字体;为保住光标与计数,SVG 只作覆盖层,底层仍是真实码点。formatter 的 normalize 把超过 24
个字形的部分截断——按字素簇计数,🤦🏼♂️ 只算一个,截断也不会切碎 ZWJ 序列。
右侧两条路把字符插到光标处:常用不可见字符的快捷 chip,以及自定义码点输入(可一次填多个,如 U+202E U+2322,合法时下方实时给出渲染结果)。下方是按 Unicode 分组的 emoji 选择器,可切肤色与渲染方式。
2 · 两个要害
警示 · 限长按 str.length 会出两种错:一个 😀 占 2、🤦🏼♂️ 占 7,用户「打了几个字」与系统「数出几格」对不上;截断点还可能落在代理对中间,产出半个乱码字符。按字素簇计数并在字形边界截断才与用户感知一致。另一头是
U+202E:注入它之后后文的显示顺序整个反转,文件名 invoicegpj.exe 看着像 invoiceexe.jpg,这就是 Trojan Source。控制字符看不见却真实存在,受控输入既该能注入演示,更该能审计揪出。
注入控制字符后计数器会立刻变化:字形数与隐形字符数双双上涨,而肉眼看不出多了任何东西——「一个都没多、限额却被悄悄吃掉」这件事,在计数器上是看得见的。
建议 · twemoji 是一套开源 emoji 图形,为每个 emoji 提供 SVG 与 PNG。把页面里的 emoji 换成它,所有用户看到的就是同一个字形,不再受设备 emoji 字体版本影响;本页按码点从 CDN 取 SVG,某个序列未收录时自动回退系统字形。要判断本机字体到底支持到哪一代,见 emoji 渲染检测。
3 · 参考文献
- Unicode. UAX #29: Unicode Text Segmentation. 字素簇边界的规范,限长与截断的依据。unicode.org
- Unicode. UAX #9: Unicode Bidirectional Algorithm.
U+202E等方向控制字符的语义,Trojan Source 的机理。unicode.org - jdecked. twemoji. 本页所用的开源 emoji 图形集,原 Twitter 的 Twemoji,现由社区维护。github.com
- MDN. Intl.Segmenter. 按字素簇切分的原生接口,本页计数与截断都建立在它之上。developer.mozilla.org