Frontend Development 14 min read

Deep Dive into LeaferJS: Architecture, Rendering Performance, Update Mechanism, and Event Picking

This article analyses the LeaferJS canvas rendering engine, covering its lightweight node creation, core architecture, rendering pipeline, partial‑render optimization, and event‑picking mechanism, while providing code examples and performance insights for front‑end developers.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Deep Dive into LeaferJS: Architecture, Rendering Performance, Update Mechanism, and Event Picking

1. Introduction

A new Chinese canvas rendering engine claims to render one million rectangles in 1.5 seconds; the author investigates its architecture and performance.

2. Architecture Design

Flame‑graph analysis shows that leaferjs creates nodes with minimal work, mainly calling setAttr . Most time is spent on node creation and layout, while actual rendering takes only about 3 ms.

The library consists of two repositories: leafer (core rendering) and ui (high‑level components such as Image, Line, etc.). A typical usage example is:

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 instance creates its own canvas, similar to Konva layers, enabling canvas‑layer optimization.

2.1 Leafer

The Leafer class is decorated with @registerUI() , uses data processors, and defines properties such as pixelRatio . The decorator registers the class in UICreator.list , allowing creation via Leafer.one(data) . Property access is intercepted by boundsType , which uses Object.defineProperty to call __setAttr .

Key internal modules:

interaction : handles DOM events and redispatches them to nodes.

canvasManager : manages canvas creation, destruction, and reuse.

imageManager : downloads and caches image resources.

During initialization, init creates a canvas based on the provided view configuration, supporting canvas‑layer management.

2.2 Leaf

The Rect class demonstrates how shapes are defined. It uses the @useModule(RectRender) decorator to mix rendering methods, extends UI , and implements __drawPathByData to draw either a rounded rectangle or a plain rectangle.

@useModule(RectRender)
@registerUI()
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 relies on several decorators and mixins:

useModule mixes RectRender methods.

@opcityType , @positionType intercept set operations via LeafDataProxy.__setAttr .

Rendering ultimately calls __render from the Branch mixin, which traverses child nodes.

3. Update Mechanism

When a property changes, __setAttr emits a CHANGE event. The Watcher captures this event, adds the node to an update queue, and dispatches a RenderEvent.REQUEST . Rendering is then scheduled via requestAnimateFrame to batch updates.

Two rendering paths exist:

Full render ( fullRender ) – renders the entire scene tree, used on first paint or when usePartRender is false.

Partial render ( partRender ) – merges pre‑ and post‑update bounding boxes into a Block , clips to that region, and redraws only intersecting nodes.

Example of partial rendering for a rectangle moved 100 px to the right:

const rect = new Rect({ x: 0, y: 0, width: 100, height: 100 })
rect.x = 100

The update block becomes { x: 0, y: 0, width: 200, height: … } , and clipRender clears and redraws only the affected area.

4. Event Picking

Canvas elements cannot natively report which shape was clicked, so LeaferJS implements a hit‑testing system. The interaction module listens to DOM events on the root Leafer node and forwards them to leaf nodes via a selector.

For a mouse click, selector.getByPoint invokes FindPath.eachFind , which recursively traverses branches and leaves. Branches are first filtered with hitRadiusPoint ; leaves are tested with __hitWorld , which draws the node onto an off‑screen hitCanvas and uses isPointInPath or isPointInStroke to determine a hit.

This approach avoids custom geometric collision code but still requires a second canvas draw, similar to Konva’s color‑key method, though LeaferJS delays the extra draw until an event occurs, improving initial render performance.

5. Summary

LeaferJS is a Chinese‑authored canvas rendering library that achieves high performance through lightweight node creation, efficient partial rendering, and a pragmatic event‑picking strategy. While still early in its lifecycle, its architecture suggests strong potential to become a competitive alternative to existing canvas frameworks.

Performance OptimizationJavaScriptfrontend developmentEvent HandlingCanvas RenderingLeaferJS
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.