基于 Canvas 实现的高性能 Excel è¡¨æ ¼å¼•æ“Žç»„ä»¶ OpenHarmonySheet。
由于大部分�?ç«¯é¡¹ç›®æ¸²æŸ“å±‚æ˜¯ä½¿ç”¨æ¡†æž¶æ ¹ï¿½?ï¿½æŽ’ç‰ˆæ¨¡åž‹æ ‘ç»“æž„ï¿½?å±‚æ¸²æŸ“çš„ï¼Œæ•´æ£µæ¸²æŸ“æ ‘ä¹Ÿæ˜¯ä¸ŽæŽ’ç‰ˆæ¨¡åž‹æ ‘ä¸€ä¸€å¯¹åº”ã€‚å› æ¤ï¼Œæ•´ä¸ªæ¸²æŸ“的节点也�?�常多。项目较大时,性能会�?�到较大的影�?。
为了�??�?�渲染性能,�??供更优质的编辑体验从 DOM 更�?��? Canvas 渲染,方便开�?�者构建�?�?端大型在线文档项目,在国内外实现类似引擎的公�?�仅仅�?ï¿½æœ‰å‡ å®¶ï¼Œå¦‚ï¼šè…¾è®¯æ–‡æ¡£ï¼Œé‡‘å±±æ–‡æ¡£å’Œè°·æŒæ–‡æ¡£ç‰ã€‚
在项目ä¸å¼•å…¥ <Sheet></Sheet> 组件�?��?�,使用方法如下:
<element name="Sheet" src="../../components/index.hml"></element>
<Sheet
sheet="{{sheet}}"
@sheet-show="sheetShow"
@sheet-hide="sheetHide"
@click-cell-start="clickCellStart"
@click-cell-end="clickCellEnd"
@click-cell-longpress="clickCellLongpress"
@change="change"
></Sheet>- sheet è¡¨æ ¼æ•°ï¿½?�
- @sheet-show è¡¨æ ¼æ˜¾ç¤º
- @sheet-hide è¡¨æ ¼ï¿½?�?
- @click-cell-start �?ï¿½å…ƒæ ¼ç‚¹å‡»ï¿½?
- @click-cell-end �?ï¿½å…ƒæ ¼ç‚¹å‡»ï¿½?�
- @click-cell-longpress é•¿æŒ‰è¡¨æ ¼
- @change 修改�?ï¿½å…ƒæ ¼æ•°ï¿½?�
比如,我们在示例ä¸ï¿½?�以监�?� 长按 事件,当用户 长按 的时候弹出 对�?框,示例代�?如下:
clickCellLongpress(evt) {
prompt.showDialog({
buttons: [{
text: '测试',
color: '#666666',
}],
});
}以上所有的接�?�都会返回一个详细的 sheet 对象,里�?��?�有以下信�?�:
- el è¡¨æ ¼çš„èŠ‚ç‚¹
- textarea �?ï¿½å…ƒæ ¼è¾“å…¥æ¡†èŠ‚ç‚¹
- viewport �?ï¿½å…ƒæ ¼é«˜äº®é€‰æ¡†
- table �?ï¿½å…ƒæ ¼ï¿½?作对象
sheetShow(sheet) {
this.el = sheet.detail.el;
this.textarea = sheet.detail.textarea;
this.viewport = sheet.detail.viewport;
this.table = sheet.detail.table;
}渲染引擎�?è£…å¥½äº†å¸¸ç”¨çš„è¡¨æ ¼æ•°ï¿½?��?ä½œç‰æŽ¥ï¿½?�。
this.table.xxx
ç”¨äºŽå¸®åŠ©ä½ ï¿½?作�?ï¿½å…ƒæ ¼çš„æ‰€æœ‰æ•°ï¿½?ï¿½å’Œæ ¼ï¿½?,也�?å¤§æ–¹ä¾¿ä½ è‡ªå®šä¹‰ä¸€ä¸ªåŠŸèƒ½å®Œæ•´çš„å·¥å…·ï¿½?:
this.viewport.xxx
ç”¨äºŽå¸®åŠ©ä½ ï¿½?作�?ï¿½å…ƒæ ¼ä¸Šå±‚çš„é«˜äº®é€‰æ¡†ã€‚
this.textarea.xxx
this.textarea 是对鸿蒙的原生 <textarea> 组件的�?装接�?ï¿½ï¼Œç”¨äºŽå¸®åŠ©ä½ æŽ¥ï¿½?�用户在界�?�ä¸çš„输入,然�?��?�?� this.table.xx 将数�?�层的数�?ï¿½æ¸²æŸ“åˆ°è¡¨æ ¼æ¸²æŸ“å±‚ï¼Œè¿™é‡Œçš„è¾“å…¥éœ€ï¿½?çœŸæœºè°ƒè¯•ï¼Œå› ä¸ºçœŸæœºæœ‰è‡ªå¸¦è¾“å…¥æ³•ï¼Œå®žæµ‹ Previewer æ— æ•ˆã€‚
import Table from "./sheet/";
this.el = this.$refs.canvas;
this.table = Table.create(this.el, 850, 800).render();viewport 用于创建和控制�?ï¿½å…ƒæ ¼é«˜äº®é€‰æ¡†ï¼Œç»˜åˆ¶åœ¨ï¿½?ï¿½å…ƒæ ¼ä¸Šå±‚ï¼Œè¾“å…¥æ¡†ä¸‹å±‚ï¼Œæ”¯ï¿½?列选择,行选择和范围选择。
this.viewport = new Viewport(this.table).render();åœ¨ä»»ä½•æƒ…å†µï¼Œä½ éƒ½ï¿½?�以使用 .cell 方法全局更新任一�?置的数�?�。
this.table.cell((ri, ci) => `${ri}-${ci}`).render();åœ¨è¡¨æ ¼ä¸è¿™æ˜¯ä¸€ä¸ªå¸¸ç”¨çš„æ–¹æ³•,我们�?�以打碎局部�?ï¿½å…ƒæ ¼ï¿½?��?�并�?作。
this.table.merges(["G9:H11", "B9:D11"]).render();�?ï¿½ä»¥è®¾ç½®ä½ çš„åˆ—è¡¨è¡Œå¤´å’Œå…¶é«˜åº¦ã€‚
this.table.colHeader({ height: 50, rows: 2 }).render();�?äº›æƒ…å†µï¼Œæˆ‘ä»¬åœ¨æŸ¥é˜…è¡¨æ ¼çš„æ—¶å€™ï¼Œæˆ‘ä»¬ï¿½?�能需�?固定�?些行和�?些列的�?ï¿½å…ƒæ ¼ï¿½?��??é«˜è¡¨æ ¼é˜…è¯»æ€§ï¼Œæ¤æ—¶ .freeze 就�?�以派上用场。
this.table.freeze("C6").render();一般�?�?�冻结区域使用,让冻结区域以外的选区�?�以�?�滚动�?作。
this.table.scrollRows(2).scrollCols(1).render();�?ï¿½ç‰¹æ®Šæƒ…å†µä½ ï¿½?需�?花费时间去�?作�?ï¿½å…ƒæ ¼é€‰æ¡†ï¼Œæ£å¸¸æƒ…况选框接�?ï¿½ä½ ï¿½?ï¿½å…ƒæ ¼çš„ç›¸å¯¹ï¿½?置�?�绘制。
const range = this.viewport.range(
evt.changedTouches[0].localX,
evt.changedTouches[0].localY
);
this.table.selection(range);
this.viewport.render(this.table.$draw);�?ï¿½å…ƒæ ¼ï¼Œè¡Œå’Œåˆ—è¡¨æ ¼ç»“æž„å¦‚ä¸‹ï¼š
| col 列 | col 列 | |
| row 行 | cell �?ï¿½å…ƒæ ¼ | cell �?ï¿½å…ƒæ ¼ |
| row 行 | cell �?ï¿½å…ƒæ ¼ | cell �?ï¿½å…ƒæ ¼ |
我们�?�以使用以下方法更新�?ï¿½å…ƒæ ¼ç¬¬äºŒè¡Œç¬¬äºŒåˆ—çš„æ•°ï¿½?�为 8848,颜色为红色:
this.table
.cell((ri, ci) => {
if (ri === 2 && ci === 2) {
return {
text: "8848",
style: {
color: "red",
},
};
}
return this.sheet?.[ri]?.[ci] || "";
})
.render();å½“ç„¶ä½ ï¿½?�以精心定制�?一个�?ï¿½å…ƒæ ¼çš„æ•°ï¿½?�,这些数�?��?�以�?ï¿½è‡ªäºŽä½ çš„ï¿½?�端�?务器,也�?�以�?�自于客户端的输入,�?�?�客户端和�?务端的å˜å‚¨èƒ½åŠ›ï¼Œå°†æ•°ï¿½?��?久化�?å˜ã€‚
this.sheet = [
["💣", "💣", "💣"],
["💣", "🙉", "💣"],
["💣", "💣", "💣"],
];
this.table.cell((ri, ci) => this.sheet?.[ri]?.[ci] || "").render();如果想�?作更多�?ï¿½å…ƒæ ¼ï¼Œè¡Œå’Œåˆ—çš„æ•°ï¿½?ï¿½å’Œæ ·ï¿½?结构,比如行高度,列高度,�?ï¿½å…ƒæ ¼è¾¹æ¡†ï¼Œå—体排版,内外边�?,下划线,背景色和旋转角度ç‰ï¼Œå…·ä½“�?�以�?�考以下接�?�,支�?�?��?ä¸°å¯Œçš„å¤šæ ·çš„æ”¹åŠ¨ï¼š
{
row: { height, hide, autoFit },
col: { width, hide, autoFit },
cell: {
text,
style: {
border, fontSize, fontName,
bold, italic, color, bgcolor,
align, valign, underline, strike,
rotate, textwrap, padding,
},
type: text | button | link | checkbox | radio | list | progress | image | imageButton | date,
}
}除æ¤ä¹‹å¤–还�??ä¾›å…¶ä»–å®Œæ•´çš„è¡¨æ ¼ï¿½?作接�?�ç‰å¾…ä½ çš„æŽ¢ç´¢:
- scrollRows
- scrollCols
- cell
- row
- cellStyle
- freeze
- merges
- colHeader
- render
- selection
- onClick
- onSelected
- focus
- selectionStyle
- headerCellStyle
- freezeLineStyle
- headerLineStyle
- target
- scrollCols
- scrollRows
- startCol
- startRow
我们将上�?�常�?的接�?��?�了一些演示,�?行 OpenHarmonySheet,长按任一�?ï¿½å…ƒæ ¼å¼¹å‡ºå¯¹ï¿½?框并点击对应选项�?��?�查看常用接�?�的�?è¡Œç»“æžœï¼Œæ¤æ¼”示仅供�?�考,更多实际使用场景请�?�考文档实现:
在谈谈实现方案之�?ï¼Œæˆ‘ä»¬å…ˆè®²è®²è¡¨æ ¼æ¸²æŸ“æœ‰å¤šï¿½?�?ï¿½ï¼Œè¡¨æ ¼çš„æ¸²æŸ“ä¸€èˆ¬ï¿½?�说有两�?实现方案:
DOM渲染。Canvas渲染。
业界比较出�??çš„ handsontable 开�?库就是基于 DOM 实现渲染,�?ï¿½ç‰æ¸²æŸ“结果,需�?对 DOM èŠ‚ç‚¹è¿›è¡Œç²¾å¿ƒçš„è®¾è®¡ä¸Žæž„é€ ï¼Œä½†æ˜¾è€Œæ˜“ï¿½?�??万�?百万�?ï¿½å…ƒæ ¼çš„ DOM æ¸²æŸ“ä¼šäº§ç”Ÿè¾ƒå¤§çš„æ€§èƒ½é—®é¢˜ã€‚å› æ¤ï¼Œå¦‚ä»Šå¾ˆå¤šåœ¨çº¿è¡¨æ ¼å®žçŽ°éƒ½æ˜¯åŸºäºŽ Canvas 和�?ï¿½åŠ DOM �?�实现的,但使用 Canvas 实现需�?考虑�?�视区域�?滚动�?作�?画布层级关系,也有 Canvas 自身�?�临的一些性能问题,包括 Canvas 如何进行直出ç‰ï¼Œå¯¹å¼€ï¿½?�的�?求较高,但为了更好的用户体验,更倾�?�于 Canvas 渲染的实现方案。
æˆ‘ä»¬é€šè¿‡åˆ†ç±»æ”¶é›†è§†å›¾å…ƒç´ ï¼Œï¿½?进行�?类别渲染的方�?,�?å°‘ Canvas 绘图引擎切�?�状�?机的次数,�?低性能�?ï¿½è€—ï¼Œä¼˜åŒ–æ¸²æŸ“è€—æ—¶ï¼Œæ•´ä¸ªæ ¸å¿ƒå¼•æ“Žä»£ï¿½?控制在 1500 行左�?�,�?�补充演示代�? 300 行,方便大家�?�解阅读和进行二次开�?�。
| 顶层 | ||
|---|---|---|
| ↑ | DOM | 容器�?ï¿½ä»¶è¾“å…¥æ¡†ç‰ |
| ↑ | Canvas | é«˜äº®é€‰åŒºç‰ |
| ↑ | Canvas | 内容å—ä½“èƒŒæ™¯è‰²ç‰ |
| 底层 |
本项目基于 OpenHarmony 下的 JavaScript UI 框架,�?行环境请�?�考 OpenHarmony 项目�?置方法 进行项目�?置和�?行。
å¦‚æžœä½ ï¿½?熟悉 OpenHarmony çš„ JavaScript 开�?�,请�?�考该官方文档。
- 下载 OpenHarmonySheet 项目工程,将工程导入
DevEco Studio进行编译构建�?��?行调试。 - 进行编译构建,生�?一个
HAP应用安装包,生�?HAP应用安装包。 - 安装�?行�?�,�?��?�在设备上查看应用示例�?行效果,以�?�进行相关调试。









