一、特性
1)开箱即用:提供不同场景下开箱即用的 React, Vue3 表组件及配套分析组件,只需要简单的配置即可轻松实现复杂场景。
2)多维交叉分析: 告别单一分析维度,全面拥抱任意维度的自由组合分析。
3)高性能:能支持全量百万数据下 < 4s 渲染,也能通过局部下钻来实现秒级渲染。
4)高扩展性:支持任意的自定义扩展(包括但不局限于布局,样式,交互,数据流等)。
5)交互友好:支持丰富的交互形式(单选、圈选、行选、列选、冻结行头、宽高拖拽,自定义交互等)
二、实现原理
G 是 Antv 系列的底层渲染引擎,为上层提供一致、高性能的 2D / 3D 图形渲染能力,适配 Web 端全部底层渲染 API (Canvas2D / SVG / WebGL / WebGPU / CanvasKit)。
s2 是基于 @antv/g 渲染的 canvas 表格,通过配置信息将原始数据处理,转换为以 行、列 纬度值为 path 的多维数组。在此之后,通过 hierarchy 来改变自动生成的层级结构。然后通过 layout 更改任意行、列、单元格的坐标信息。最后由 layoutResult 来确定行、列的笛卡尔交集的 dataCell 数据信息。
在性能的优化上做了,按需渲染和缓存设计:
按需渲染:只渲染可视区域内的单元格,滚动后通过 scrollX 和 scrollY,计算当前视窗中的单元格的节点索引,去动态并新增和删除单元格。
三、本地化
3.1 使用
// 生成 S2DataConfig: 数据配置,S2Options:参数配置,S2Theme:主题配置
const changeVariables = () => {options.value = tableConfig.initOptions(columns.value, tableRef.value, props.options, headerCallBack, globalClickCallBack)theme.value = tableConfig.initTheme(props.theme)dataConfig.value = tableConfig.initDataConfig(columns.value, tableData.value, props.customTableSheetTotal, dataCellDbClickCallBack)
}// s2 实例化
const initTable = () => {// 实例类型:TableSheet 明细表,PivotSheet 交叉表tableSheet.value = type.value === 'tableSheet' ? new TableSheet(tableRef.value, dataConfig.value, options.value) : new PivotSheet(tableRef.value, dataConfig.value, options.value)// 设置主题tableSheet?.value.setTheme(theme.value)tableSheet?.value.render()// 类属性赋值tableConfig.tableSheet = tableSheet.valuetableConfig.tableRef = tableRef.value// 注册单元格点击事件tableConfig.dataCellClick()
}// 画布尺寸监听,动态改变表格宽高
const addResizeEvent = () => {const resizeObserver = window.ResizeObserverconst callback = (domList) => {if (!domList[0]) returnlet { width, height } = tableRef.value.getBoundingClientRect()tableConfig.autoLayoutRender(width, height)}observer = new resizeObserver(callback)observer.observe(tableRef.value)
}
3.2 自定义表头样式
// 自定义 customColCell 进行继承实现
export class customColCell extends ColCell { initCell() {super.initCell() }// 基于 s2 getBackgroundColor 自定义表头二级列背景色getBackgroundColor() {let backgroundStyle = super.getBackgroundColor()...backgroundStyle = { ... }return backgroundStyle}// 基于 s2 addActionIcon 自定义表头 icon ,主要用于改变颜色addActionIcon(options) {...var icon = new GuiIcon({ ... })icon.set('visible', !defaultHide)icon.on('mouseover', function (event) { ... })icon.on('mouseleave', function (event) { ... })icon.on('click', function (event) { ... })this.actionIcons.push(icon)this.add(icon)}
}
3.3 自定义操作列
// 继承 s2 数据单元格,并追加自定义
export class customDataCell extends DataCell {initCell() {super.initCell()// 在绘制完原本的单元格后, 再绘制定制化内容this.renderCustomDataCell()}// 自定义单元格渲染renderCustomDataCell() {let textStyle = {fill: '#8B60F0',fontSize: 12,lineHeight: 34,textAlign: 'center',cursor: 'pointer',textBaseline: 'middle',opacity: 1}...actionList.map((item, index) => {textStyle = {...textStyle,...item?.textStyle}...switch (item.type) {case 'default':this.addShape('text', {attrs: {text: fieldValue,...position.text,...textStyle}})breakcase 'operate': this.addShape('text', {attrs: {x: x,y: position.text.y,text: item.name,...textStyle}})break}})}
}
3.4 自定义支持汇总
export class customDataCell extends DataCell {drawTextShape() {let { valueField, rowIndex } = this.metalet { dataCfg } = this.spreadsheet// 处理明细表汇总if (dataCfg.customTableSheetTotal.enable && rowIndex === dataCfg.data.length - 1) { this.meta.isTotals = trueif (valueField === '$$series_number$$') {this.meta.fieldValue = dataCfg.customTableSheetTotal.label}}super.drawTextShape()}// 汇总行填充色drawBackgroundShape() {super.drawBackgroundShape()let { rowIndex } = this.metalet { dataCfg } = this.spreadsheetif (dataCfg.customTableSheetTotal.enable && rowIndex === dataCfg.data.length - 1) this.backgroundShape.attr('fill', '#f5f7fa')}
}...
// 计算逻辑,另外
3.5 单元格数据格式化
// 封装的数据过滤方法
...
export const filterList = [{label: '数据转换',options: [...]},{label: '日期时间',options: [...]}
]static initDataConfig(columns, data, customTableSheetTotal = { enable: false, label: '汇总' }, dataCellDbClickCallBack) {...columnsCustom.map(item => {let { field, name, alias, formatter } = item...meta.push({field,name: alias || name,item,formatter,dataCellDbClickCallBack})})// 兼容二级表头columnsCustom.map(item => {...})// 汇总处理data = this.handleTableSheetTotal(columns, data, customTableSheetTotal)return {...this.defaultDataConfig(),fields,meta,data,customTableSheetTotal}}