Concepts
What is Rendering?
Rendering is the data table rendering capability of the APITable core.
Why do we need Rendering?
Instead of using Dom for table rendering, we chose Canvas for rendering, which allows the tables to scroll smoothly even with large data volumes.
In the actual development process, we used the Konva framework, which provides excellent Canvas rendering performance and a rich graphical drawing API, allowing us to quickly implement various data table features.
But don't worry, the actual Konva development process is not much different from the usual React component development area, we provide perfect documentation and sample code, so that developers can easily get started and quickly develop high-quality data tables.
Here is a brief introduction to Canvas and Konva:
Canvas
Canvas is an important feature in HTML5, which can use JavaScript to draw graphics on web pages, with the advantages of high performance, cross-platform, scalability, flexibility, high interactivity, support for animation effects, embeddability, and so on.
Canvas belongs to the underlying drawing function interface of the browser, we can think of it as a canvas to draw whatever we need. In terms of dynamic update and instant rendering, the performance is significantly better than DOM, so Canvas is more suitable for charting and data visualization scenarios.
Konva
Konva is a Canvas-based JavaScript library that provides an easy-to-use way to create complex graphics, animations, and interactive applications. Our main considerations in the selection process were the following:
High performance: Konva is based on Canvas and offers several optimizations such as layer caching and event delegation, which makes it excellent at handling large-scale graphics and complex animations.
Ease of use: Konva provides a very intuitive API with which we can easily create elements such as shapes, text, lines and images, and easily perform layout and interactivity operations.
Extensibility: Konva provides a number of plugins and extension modules, such as React-Konva, making it possible to integrate seamlessly with frameworks such as React.
Cross-platform: Konva can render on a variety of desktop and mobile devices without additional plug-ins or software, making it more widely available and accessible.
Community and developer activity: Konva is a mature and popular Canvas library with an active developer community and a large collection of documentation and examples.
What are the Components of Rendering?
As an example, the grid view has the following UI components:
GridView
GridStage
GridLayer
Layout
CellValue
FieldHead
Stat
... More Components
DomGrid
EditorContainer
Menus
Data structure of Rendering
The data structure of Rendering consists of the data structure of a datasheet and the derived linear data structure.
This subsection belongs to the drawing pre-knowledge, after understanding the data structure below to better understand how to draw.
Rows
Each view holds a separate list of rows, which is stored persistently in the database and can be thought of as a sequential list of raw rows.
VisibleRows
visibleRows is the final ordered list of rows after filtering, sorting, grouping and searching.
LinearRows
linearRows is a linear data structure that adds group headers, blank rows, and added rows to visibleRows. It serves as a guide to how Canvas draws vertically and contains four data structures: blank rows, group headers, record rows, and added rows.
Interface Statement
type ILinearRowBase = {
depth: number;
recordId: string;
};
type ILinearRowBlank = ILinearRowBase & {
type: CellType.Blank;
};
type ILinearRowAdd = ILinearRowBase & {
type: CellType.Add;
};
type ILinearRowGroupTab = ILinearRowBase & {
type: CellType.GroupTab;
};
type ILinearRowRecord = ILinearRowBase & {
type: CellType.Record;
groupHeadRecordId: string;
displayIndex: number;
};
type ILinearRow = ILinearRowBlank | ILinearRowAdd | ILinearRowGroupTab | ILinearRowRecord;Example of data structure
The following is a real linearRows data for reference purposes only:
[
{
"type": "Blank",
"depth": 0,
"recordId": "recrMKrMY5jDf"
},
{
"type": "GroupTab",
"depth": 0,
"recordId": "recrMKrMY5jDf"
},
{
"type": "GroupTab",
"depth": 1,
"recordId": "recrMKrMY5jDf"
},
{
"type": "Record",
"depth": 2,
"recordId": "recrMKrMY5jDf",
"displayIndex": 1,
"groupHeadRecordId": "recrMKrMY5jDf"
},
{
"type": "Record",
"depth": 2,
"recordId": "recslJsH5mGO7",
"displayIndex": 2,
"groupHeadRecordId": "recrMKrMY5jDf"
},
{
"type": "AddRecord",
"depth": 2,
"recordId": "recslJsH5mGO7"
},
{
"type": "Blank",
"depth": 1,
"recordId": "recslJsH5mGO7"
}
]UI Structure Analogy
Columns
The columns are much simpler than rows, and there is no filtering, sorting, grouping, etc. in the column dimension, only filtering based on the hidden property in each column.
Rendering's drawing process
Based on the above datasheet data structure, we can plot rows and columns.
Explanation of terms
containerWidth
The width of the viewable area
containerHeight
The height of the viewable area
scrollTop
The vertical distance of the scrolling table
scrollLeft
The horizontal scroll distance of the table
rowHeight
Row height, including group header rows, blank rows, record rows and added rows, different types of rows have different heights
columnWidth
The width of the column, if not set by the user, the default width is used
rowStartIndex
The index of the first row in the visible area of the table, this variable will change frequently when scrolling vertically
rowStopIndex
The index of the last row in the visible area of the table, this variable changes frequently when scrolling vertically
columnStartIndex
The index of the first column in the visible area of the table, which changes frequently when scrolling horizontally
columnStopIndex
The index of the last column in the visible area of the table, which changes frequently when scrolling horizontally
Overview of the process
Taking vertical scrolling as an example, the process is as follows:

The specific process is as follows:
Since the Canvas does not have scroll events, set the DOM scroll bar to trigger the scroll event instead, and get the totalHeight and scrollTop information from it.
Initialize the Coordinate coordinate class (see below) based on the initial information for subsequent coordinate calculation
Calculate the rowStartIndex from Coordinate based on scrollTop
Calculate rowStopIndex from Coordinate based on scrollTop, rowStartIndex and containerWidth
Iterate through the index values between rowStartIndex and rowStopIndex to get the rows to be drawn vertically
According to the coordinate information provided by Coordinate, and GridLayout and CellHelper rendering classes to draw the table lines and cell content
After drawing all of them, you get a "frame", and repeat the above steps to get a visual dynamic scrolling
Coordinate
Responsible for the calculation of the base coordinate system.
Explanation of terms
rowMetaDataMap
A collection of row coordinates
columnMetaDataMap
A collection of column coordinates
lastRowIndex
The index of the last row in the row coordinate collection
Specific implementation
Taking vertical scrolling as an example, the process is as follows:
When traversing the data, rowMetaDataMap collects the row information and changes the lastRowIndex
If the target rowIndex is smaller than lastRowIndex, it will be obtained directly from rowMetaDataMap
If the target rowIndex is larger than lastRowIndex, the iteration starts from lastRowIndex and loop step 1
type IndicesMap = Record<number, number>;
type CellMetaData = {
offset: number;
size: number;
};
type CellMetaDataMap = Record<number, CellMetaData>;
interface ICoordinate {
columnWidth: number;
rowHeight: number;
columnCount: number;
rowCount: number;
containerWidth: number;
containerHeight: number;
rowIndicesMap: IndicesMap;
columnIndicesMap: IndicesMap;
lastRowIndex?: number;
lastColumnIndex?: number;
rowMetaDataMap?: CellMetaDataMap;
columnMetaDataMap?: CellMetaDataMap;
}
/**
* Used to construct raster coordinate system, assist in Grid and Gantt view for plotting
*/
class Coordinate<ICoordinate> {}CellHelper
Responsible for drawing the cell content.
Specific implementation
interface IRenderProps {
x: number;
y: number;
columnWidth: number;
rowHeight: number;
recordId: string;
field: IField;
cellValue: ICellValue;
isActive?: boolean;
editable?: boolean;
rowHeightLevel: RowHeightLevel;
...
}
class CellHelper extends KonvaDrawer {
public renderCellValue(renderProps: IRenderProps, ctx?: CanvasRenderingContext2D) {
switch (fieldType) {
// ...
case FieldType.Text:
return this.renderCellText(renderProps, ctx);
}
}
private renderCellText(renderProps: IRenderProps, ctx?: CanvasRenderingContext2D) {...}
// ...
}
// Wrapping of Canvas basic drawing methods
class KonvaDrawer {
public line(props: ILineProps) {...}
public text(props: ITextProps) {...}
public image(props: IImageProps) {...}
// ...
}