导言
目前富文本编辑器的实现主要有两种技术方案:一个是利用contenteditable属性直接对html元素进行编辑,如draft.js;另一种是代理textarea + 自定义div + 模拟光标实现。对于类似"word"的经典富文本编辑器,一般会采用以上两种技术方案之一,而不会考虑用canvas实现。
事实上,官方最佳实践中已经特别声明了不推荐用canvas实现编辑器,详见https://www.w3.org/TR/2dconte...
不推荐的原因包括光标位置维护、键盘移动的实现、以及没有原生文本输入处理等等。
既然如此,为何还要用canvas制作文本编辑器呢?这是因为对一些特殊的创作来说,canvas能更好的实现展示需求。比如艺术字效果的渲染,以及文本、背景动画等。
基于这点想法,便有了“简诗”这个自娱自乐的小项目。
简诗是为短诗文创作而开发的文本编辑器,主要面向中文写作。中文最特别之处便在于其笔画,所以在开发之初,我便想对文字进行处理之时,一定要把汉字进行笔画分割,以便实现更多有趣的效果的。
项目中文字由WebGL进行渲染。基本思路是先根据用户选择的字体,将文字写在离屏canvas上,然后利用getImageData api获取文字像素数据,进行连通域查询、分割、边缘查找及三角化后,由WebGL进行渲染。
(注:这种处理方式的好处是对任意系统支持的字体都可以实现艺术效果,而无需额外的字体开发。目前项目中没有引入字体文件,用到的字体都是Mac内置的字体,Mac用户如发现其中有的字体系统没有默认安装,只需到“字体册”中安装一下即可)
这一系列过程会单开一篇文章来写,本文主要描述canvas编辑器核心的实现。
实现效果
预览地址:https://moyuer1992.github.io/...
源码地址:https://github.com/moyuer1992...
技术关键点
文字键入(代理输入框)
用canvas实现编辑器最关键的一点就是如何监听键盘文字输入,如果通过键盘事件自己处理,英文尚可,中文肯定是不可行的。所以还是需要使用原生textarea做一层代理。
代理textarea输入框是不可见的。这里需特别注意下,若用display: none隐藏输入框,则无法触发focus事件,所以输入框需要利用z-index来做隐藏。
当用户点击canvas时,程序控制触发textarea的focus事件,继而用户输入时,也自然触发了textarea的input事件:
var pos = this._convertWindowPosToCanvas(e.clientX, e.clientY);
if (pos.x !== -1 && pos.y !== -1) {
this.focus(pos.x, pos.y);
} else {
this.blur();
}
focus (x, y) {
var pos = this.findPosfromMap(x, y);
this.selection.update(pos.row, pos.col);
this.updateCursor();
this.$input.focus();
this.$cursor.css('visibility', 'v