跳到正文
W Winse Blog
editor dev 1 min read

富文本编辑器开发学习笔记:光标之Carota实现

在学习和开发的一段时间后,越来越来觉得:看代码和写代码一样,都要带着目标目的。要求更高一点的话,甚至乎要在自己脑子里面把逻辑跑通。如果只是盲目的翻代码,代码量稍微一大,就会迷失在代码的海洋里了。

看代码也是对自己的一个训练,不能漫无目的!思考是一种能力,需要锻炼。boss说的:把代码记在脑子里面,躺在床上也能去推敲推演。在脑子想清楚,代码能在脑子里面跑起来,这样你的思路才是清晰的,想清楚想明白才能少走弯路。

说回主题,我们思考:什么是光标,文本编辑器的光标的作用是啥?

光标是屏幕上闪烁的小竖线。光标呈现了当前位置的样式,更重要的是告诉我们即将输入内容的样式。 光标是文本编辑器中非常重要组件,正确的绘制出光标是验证整个逻辑是否正确的关键。

实现一个光标,需要做哪些功能:

  • 屏幕坐标和索引的转换。用于获取光标的高度和位置;

  • 光标的闪烁;

  • Affinity(亲和性),光标是在字符左边,还是右边?尤其在行末时,差别就凸显出来了。

# 测量

在上一篇文章中简单过了下文字的测量,它是绘制的基础。每个字符都有基于基线(baseline)的上升(ascent)和下降(descent)。只有在获取每个字的TextMetrics后,才能在不同字体的混排时,依据一个统一标准去绘制(基于基线,顶端或底部)。

测量工作是在创建Box初始化时完成的。

# 转换

编辑器的模型中保存的是字符串的 ordinal 索引。

当我们在编辑器中点击时,需要把 屏幕的xy坐标 转换为 字符串索引。而绘制的时刻,就要把 索引 转换为 屏幕xy坐标,才能正确的绘制光标。

getCaretCoords(ordinal: number): Rect | null 从 索引位置 转 屏幕坐标:

byCoordinate(x: number, y: number): Node | null 从 屏幕坐标 转成 索引位置:

这里虽然是返回的Node,但返回的对象是 PositionedChar 相当于就是 oridinal 一样的(Carota没有考虑多字节字符串)。

其中,PositionedChar 的 byCoordinate 逻辑表明:当光标落在字符串中心点的左侧,返回当前字符;否则返回下一个字符。这就是光标的Affinity更亲近哪个字符,可以参考Flutter的TextSelection。

# 渲染

在理解了坐标转换后,光标的绘制 drawSelection 就比较简单了:

  1. 从 selection选取 获取索引位置;

  2. 调用 getCaretCoords 得到屏幕坐标;

  3. 然后用canvas绘制光标的矩形。

光标的闪烁则是通过 Editor 中一个定时器实现,每200ms触发一次 carotaEditorSharedTimer 事件。update 函数通过 toggleCaret 来更新光标显示状态。

# 小结

坐标转换是编辑器的核心代码之一,涉及到交互和渲染。所以,先单独写一篇关于光标的文章来讲讲它是怎么实现的。

当然,现在来看会觉得Carota很多实现不是很优,比如:光标现在流行的都是用div加css动画来实现。

但我们阅读代码先把人家的实现读懂。下一篇我们学习它的交互功能。


这是一个关于富文本编辑器的专题。在文本编辑器开发过程中,我从 boss 那里学到很多,但也意识到自己还有诸多的不足。希望通过研究开源项目开阔视野、拓展思路。如果你也在探索,不妨一起交流学习共同进步。

富文本编辑器专栏

源代码地址:https://gitcode.com/winseII/carota-ts

在 GitHub 上讨论

欢迎通过 GitHub Issue 留言或反馈。每条讨论都会关联到对应文章的源文件路径。

2025-08-18-富文本编辑器开发学习笔记:光标之Carota实现.md

Related posts