一般来说,我们在PC端使用的微软Excel即是一个很强大的本地文档软件,而所谓“在线”,就是把这些功能移植到浏览器端,当然也包括移动web,小程序,Android或者iOS应用中,和本地最大的不同是在线文档除了基本的文档编辑操作之外,还加入了“协同”功能,能够同时多人编辑,这也就更能体现出在线文档的优势。
目前来说,比较流行的在线文档包括了Google Doc,钉钉文档,飞书文档,腾讯文档以及石墨文档等等,这些文档包括了Word,Excel,PPT等常用的模块,本文主要就Excel模块即Sheet文档进行技术解析。
正如上文所述,协同是在线文档的一大特色,其主要功能场景如下:
为了实现这些功能场景,就需要解决以下的技术问题:
处理冲突问题,常用的算法是OT算法,全称是Operation Transformation,是在线协作系统中经常使用的操作合并算法。其核心是一种操作合并指导思想,在不同的应用场景下有不同实现,这里不过多解释,感兴趣的同学可以看下ot.js。关于协同这块,由于涉及到很多WebSocket和后端的交互,我们主要讲一下纯前端的在线编辑交互这块。
对于Sheet文档的在线编辑,可以看作是一个传统的前端单页应用,其包括了较为复杂的DOM交互,鼠标事件交互,样式,滚动处理等等,一般来说会采用纯DOM或者DOM和Canvas并配合Vue,React结合数据状态管理库Redux或者Vue来实现。
以DOM+Canvas+React+Redux为例子,其主要实现思路如下:
export type CellAttrs = { x: number // x坐标 y: number // y坐标 width: number // 宽度 height: number // 高度度 value?: string // 值 ownKey: string // key fill?: string // 背景颜色 borderStyle?: BorderStyle // 边框属性 fontWeight?: string | boolean // 加粗 textColor?: string // 字体颜色 verticalAlign?: string // 垂直居中 align?: string // 水平居中 fontFamily?: string // 字体 fontSize?: number // 字号 fontItalic?: string | boolean // 斜体 textDecoration?: string | boolean // 下划线 ... }
其页面结构如下图所示:
这些层,除了Canvas之外,一般设为pointer-events:none
并且z-index
高于Canvas。
首先,使用Canvas一般是来渲染最底层的单元格,相对于DOM来实现,主要有以下好处:
借助react-konva库,可以很方便的将React组件和Canvas结合起来,实现最原子的Cell操作。
这层主要实现的鼠标选择交互相关的UI展示,如下所示蓝色区域:
其包括了选择区域(透明蓝色背景)和当前激活单元格Cell(实现蓝色框)两部分,相关技术点主要有:
这层主要包括的是双击单个Cell单元格时,实时渲染出编辑器的逻辑,如下图所示:
简单来说,由一个文本输入框<textarea>
实现,相关技术点主要有:
<textarea>
输入文字时,需要实时修改其width
和height
,来适配不同内容的展示。<textarea>
之外,还可以有<select>
,<datepicker>
等等复杂的编辑器。这层主要目的是为了模拟出Canvas的真实宽度和高度,从而通过overflow:auto
来模拟出滚动条及其相关交互,从而使Canvas看起来是可以滚动的,其原理如下图:
这些层主要包括了一些其他逻辑,主要有:
合并单元格相对于原子的修改Cell属性操作会比较复杂一些,这里可以将合并单元格看作是一个样式加属性调整的操作,相关技术点主要有:
key值的属性,如下所示:
export type CellAttrs = { ... isMerge:[first:key,last:key] // 左上角和右下角的坐标key }
由于修改列或者行的Cell的大小会影响之后的所有Cell位置,所以这是一个很消耗性能的操作,在mousemove
时,可以采用节流_.throttle
函数来减少一些高频操作,或者直接在mouseup
的那一刻去做修改操作。
对于Sheet文档来说,最重要的技术点还是对Cell数据的操作,如何管理好Cell,使其变换出不同的效果和样式是实现好Sheet功能的关键,而React以及Redux和Canvas则是提供了数据->界面
的工具。快开始你的在线文档之旅吧。