Building a High‑Performance Multi‑Dimensional Table with Konva.js
This article explains how to create a high‑performance, multi‑dimensional table system using Konva.js, covering project overview, feature list, core architecture, module design, key mechanisms such as group rendering, virtual scrolling and batch drawing, implementation details, performance optimizations, and future extensions.
Konva.js Multi‑Dimensional Table System
Based on Konva.js , this article presents a high‑performance multi‑dimensional table system that supports massive rendering, group management, filtering, sorting, and more, aiming to provide an interactive experience similar to Tencent Docs or Feishu Sheets.
Table of Contents
1. Project Overview
2. Feature List
3. Core Architecture Design
4. Module Description
5. Key Mechanisms
6. Implementation Process
7. Performance Optimization
8. Extensibility
9. Conclusion
1. Project Overview
The project implements a high‑performance two‑dimensional/multi‑dimensional table system with Konva.js, supporting:
Massive table rendering (rows and columns up to millions)
Grouped data display
Multi‑dimensional filtering and sorting
Various cell types (text, image, status tags, custom renderers)
Responsive layout and virtual scrolling optimization
The system is suitable for complex scenarios such as task management, resource scheduling, and project planning.
2. Feature List
Feature Module
Description
Table Rendering
Uses Konva Layer + Group for efficient drawing of cells, borders, and backgrounds.
Group Management
Supports collapsible/expandable groups.
Filtering
Multi‑column field filtering (text, numeric, status).
Sorting
Single‑column and multi‑column sorting logic.
Cell Types
Text, image, status tag, custom renderer.
Interaction
Selection, box selection, multi‑select, hover tips, scroll sync.
Virtual Scrolling
Renders only elements within the viewport to improve performance.
Dynamic Layout
Adaptive row height, column width, and container size.
Batch Update Mechanism
Uses requestAnimationFrame for batch drawing, avoiding redundant renders.
3. Core Architecture Design
## 🎯 Main Controller
- table
- Layer system
- `backgroundLayer` – background layer
- `bodyLayer` – main content layer
- `featureLayer` – feature layer
- Group system
- `topLeft Group` – top‑left frozen area
- `topRight Group` – top‑right frozen area
- `bottomLeft Group` – bottom‑left frozen area
- `bottomRight Group` – bottom‑right frozen area
- Data management
- **LinearRowsManager**
- `linearRows: ILinearRow[]` – linear row data
- `buildLinearRows()` – construct row data
- `toggleGroup()` – toggle group state
- Layout system
- **CellLayout** (abstract base)
- `GroupTabLayout` – group row layout
- `RecordRowLayout` – data row layout
- `AddRowLayout` – add‑row layout
- `BlankRowLayout` – blank row layout
- `headerLayout` – header layout
- Utility classes
- **VirtualTableHelpers**
- `getItemMetadata()` – get item metadata
- `findNearestItem()` – find nearest item
- Event handling
- `setupEvents()` – initialize events
- `scroll()` – scroll handling
- `handleCellClick()` – cell click handling4. Module Description
1. Renderer Module
Responsible for visual rendering logic:
Uses Konva.Layer to manage background, content, and interaction layers.
Each row or group of cells is represented by a Konva.Group.
Supports incremental rendering and batch updates.
Fast matrix‑coordinate positioning for rendering regions.
2. Model Module
Provides an abstract data source.
Supports filtering, sorting, group aggregation, and dynamic updates.
Synchronizes data changes with the rendering layer via the observer pattern.
3. Controller Module
Listens to user input events (mouse, scroll, drag).
Controls rendering queue and update cadence.
Manages current selection and focused cell.
Synchronizes with the Model layer for data updates.
5. Key Mechanisms
1. Group Rendering
Each group uses an independent Konva.Group.
When collapsed, only the group header is rendered.
Expanding loads child nodes in batches.
Lazy loading optimizes performance.
2. Virtual Scrolling
Calculates rows and columns that should be rendered within the visible area.
Reduces memory usage and repaint frequency.
Supports synchronized horizontal and vertical scrolling.
3. Batch Draw
private _waitingForDraw = false;
private animQueue: Function[] = [];
public batchDraw() {
if (!this._waitingForDraw) {
this._waitingForDraw = true;
requestAnimationFrame(() => {
this.animQueue.forEach(fn => fn());
this.animQueue = [];
this._waitingForDraw = false;
});
}
return this;
}
private requestAnimFrame(callback: Function) {
this.animQueue.push(callback);
if (this.animQueue.length === 1) {
req(() => {
const queue = this.animQueue;
this.animQueue = [];
queue.forEach(cb => cb());
});
}
}
render() {
this.renderContent();
this.core.updateScrollBars();
}6. Implementation Process
1. Simple Konva.js Table Rendering (inefficient for large data)
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 10; col++) {
const rect = new Konva.Rect({
x: col * cellSize,
y: row * cellSize,
width: cellSize,
height: cellSize,
fill: 'lightgrey',
stroke: 'black',
strokeWidth: 1
});
layer.add(rect);
}
}2. Helper Class – VirtualTableHelpers
getVisibleRowRange(frozenRowsHeight: number) {
const rowCount = this.linearRowsManager.getRowCount();
const viewportHeight = this.visibleHeight - frozenRowsHeight - this.scrollBarSize;
const startRow = VirtualTableHelpers.getRowStartIndexForOffset({
itemType: "row",
rowHeight: this.getRowHeight,
columnWidth: this.getColumnWidth,
rowCount,
columnCount: this.cols,
instanceProps: this.instanceProps,
offset: this.scrollY
});
const endRow = VirtualTableHelpers.getRowStopIndexForStartIndex({
startIndex: Math.max(this.frozenRows, startRow),
rowCount,
rowHeight: this.getRowHeight,
columnWidth: this.getColumnWidth,
scrollTop: this.scrollY,
containerHeight: viewportHeight,
instanceProps: this.instanceProps
});
const buffer = 2;
return {
start: Math.max(this.frozenRows, startRow - buffer),
end: Math.min(rowCount - 1, endRow + buffer)
};
}7. Performance Optimization
Cell values that involve heavy calculations (e.g., formulas) are processed in a Web Worker.
Data statistics, filtering, sorting, and searching use asynchronous chunking.
Icons and images are cached and reused.
if (textContext && textContext.includes('cacl')) {
this.alloyWorker.cookie.exportStaion(Math.random() * 10 >= 5 ? 40 : 39).then(result => {
const groups = this.groups.bottomRight.find(`#${row}-${col}`) as Group[];
if (groups.length) {
const textNode = groups[0].children[1] as Konva.Text;
textNode.text(result);
}
});
textContext = 'Calculating...';
}8. Extensibility
Future work includes adding features such as highlighting, selection zones, custom column types, and more advanced data analysis.
Conclusion
If you have questions, feel free to comment. Many features still need development and polishing; updates will be posted as progress is made. This article aims to spark ideas and guide step‑by‑step implementation of complex table functionalities.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
