In‑Depth Analysis of LeaferJS Rendering Engine Architecture and Performance
This article examines the architecture, rendering pipeline, update mechanism, and event‑picking strategy of the LeaferJS canvas library, illustrating how its lightweight node creation, selective full‑ and partial‑rendering, and optimized hit‑testing achieve high performance compared with alternatives like Konva.
1. Introduction
A new Chinese canvas rendering engine, LeaferJS, claims to render one million rectangles in 1.5 seconds. Curious about the claim, the author investigates its architecture and performance.
2. Architecture Design
Flame‑graph analysis shows that creating a node in LeaferJS is extremely lightweight, performing only a setAttr operation, while most time is spent on node creation and layout; actual rendering takes only about 3 ms.
LeaferJS consists of two repositories: leafer (core rendering) and ui (higher‑level components such as Image, Line, etc.). The official demo demonstrates how an App can contain multiple Leafer instances, each creating its own canvas layer, similar to Konva’s Layer concept.
import { App, Leafer, Rect } from 'leafer-ui'
const app = new App({ view: window, type: 'user' })
const backgroundLayer = new Leafer()
const contentLayer = new Leafer({ type: 'design' })
const wireframeLayer = new Leafer()
app.add(backgroundLayer)
app.add(contentLayer)
app.add(wireframeLayer)
const background = new Rect({ width: 800, height: 600, fill: 'gray' })
const rect = new Rect({ x: 100, y: 100, fill: '#32cd79', draggable: true })
const border = new Rect({ x: 200, y: 200, stroke: 'blue', draggable: true })
backgroundLayer.add(background)
contentLayer.add(rect)
wireframeLayer.add(border)Each Leafer acts as a container that can hold child nodes (e.g., Rect ), enabling canvas‑layer optimization.
2.1 Leafer
The Leafer class is decorated with registerUI , which registers the class in UICreator.list . Instances can be created via Leafer.one(data) . Various internal modules handle attribute setting ( __setAttr ), event propagation, canvas management, and image resources.
Key internal modules include:
interaction : wraps DOM events and dispatches them to leaf nodes.
canvasManager : creates, reuses, and destroys canvas elements.
imageManager : manages image downloading and caching.
Creator : provides factory methods such as renderer (creates the low‑level canvas renderer) and watcher (observes property changes to trigger re‑render).
2.2 Leaf
The Rect class extends UI and is decorated with useModule(RectRender) and registerUI . It implements __drawPathByData to render either a rounded rectangle or a plain rectangle based on the presence of cornerRadius .
export class Rect extends UI implements IRect {
public get __tag() { return 'Rect' }
@dataProcessor(RectData)
public __: IRectData
constructor(data?: IRectInputData) { super(data) }
public __drawPathByData(drawer: IPathDrawer, _data: IPathCommandData): void {
const { width, height, cornerRadius } = this.__
if (cornerRadius) {
drawer.roundRect(0, 0, width, height, cornerRadius)
} else {
drawer.rect(0, 0, width, height)
}
}
}The class uses several decorators ( useModule , dataProcessor ) and inherits from UI , which in turn inherits from Leaf . Leaf provides low‑level capabilities such as matrix transforms ( LeafMatrix ), bounding boxes ( LeafBounds ), event handling ( LeafEventer ), and a data proxy ( LeafDataProxy ) that intercepts property set operations via decorators like @opacityType and @positionType .
export const LeafDataProxy: ILeafDataProxyModule = {
__setAttr(name: string, newValue: unknown): void {
if (this.leafer && this.leafer.ready) {
this.__[name] = newValue
const { CHANGE } = PropertyEvent
const event = new PropertyEvent(CHANGE, this, name, this.__.__get(name), newValue)
if (this.hasEvent(CHANGE) && !this.isLeafer) this.emitEvent(event)
this.leafer.emitEvent(event)
} else {
this.__[name] = newValue
}
},
__getAttr(name: string): unknown {
return this.__.__get(name)
}
}3. Update Mechanism
When __setAttr is called, it emits a CHANGE event. The Watcher listens for this event, adds the node to an update queue, and dispatches a RenderEvent.REQUEST to start rendering. Rendering is scheduled via requestAnimateFrame to batch updates and avoid frequent re‑renders.
The render loop distinguishes between full‑render ( fullRender ) and partial‑render ( partRender ). Full‑render traverses the entire scene graph from the root Leafer node, while partial‑render calculates a combined bounding Block for changed nodes and uses canvas clip to restrict drawing to the affected area.
3.1 Visible‑Area Rendering
fullRender renders every node when usePartRender is false or during the initial paint. It checks each node’s world bounds against the current viewport and skips those outside the visible area.
3.2 Partial Rendering
partRender merges pre‑ and post‑change bounding boxes of all dirty nodes, computes a clipping region, clears it, and then re‑draws only nodes whose bounds intersect the region. This reduces canvas work but incurs extra CPU for hit‑testing.
const rect = new Rect({ x: 0, y: 0, width: 100, height: 100 })
rect.x = 100Moving the rectangle updates its combined bounding box to { x: 0, y: 0, width: 200, height: 100 } , and clipRender ensures only the affected area is repainted.
4. Event Picking
Canvas elements are a single DOM node, so detecting which shape a user interacts with requires custom hit‑testing. Konva uses a color‑key method that doubles rendering cost. LeaferJS improves this by rendering the target leaf to an off‑screen hitCanvas only when an event occurs, then using isPointInPath or isPointInStroke to determine hits.
The interaction module captures DOM events at the root Leafer and forwards them to a selector that calls getByPoint . This method traverses the scene graph, using hitRadiusPoint for branches and __hitWorld for leaf nodes, which draws the leaf onto the off‑screen canvas and checks the point against the path.
5. Summary
LeaferJS is a Chinese‑origin canvas library that currently supports CanvasKit and Mini‑app extensions and encourages plugin contributions. Although still early in its lifecycle, its lightweight node model, selective rendering strategies, and optimized hit‑testing give it strong performance potential compared with established libraries like Konva.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
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.