Skip to main content

EC Canvas

<ec-canvas> is a composited rendering host that combines pixel-level paint primitives with standard LVGL widgets. It compiles to a native LVGL canvas widget, letting you draw backgrounds, separators, and overlays around interactive content — all in JSX.

When to use

Use <ec-canvas> when you need rendering that goes beyond what LVGL widget styling provides:

  • Custom-painted backgrounds (gradients, rounded cards, decorative shapes)
  • Separator lines, grid lines, or visual indicators
  • Overlays drawn on top of interactive widgets
  • Status indicators (circles, badges) with pixel-precise control

For standard UI layout and controls, prefer regular LVGL widgets or the UI Library components.

Architecture

An <ec-canvas> has three ordered rendering layers:

┌─────────────────────────────────┐
│ ec-canvas-overlay │ ← Paint primitives drawn last
│ ┌───────────────────────────┐ │
│ │ ec-canvas-content │ │ ← LVGL widgets (flexbox layout)
│ └───────────────────────────┘ │
│ ec-canvas-background │ ← Paint primitives drawn first
└─────────────────────────────────┘
ZoneAcceptsPurpose
<ec-canvas-background>Paint primitivesDecorative underlay drawn before widgets
<ec-canvas-content>LVGL widgetsInteractive content, laid out by LVGL flexbox/grid
<ec-canvas-overlay>Paint primitivesDecorative overlay drawn after widgets

All zones are optional. A canvas with only <ec-canvas-background> is valid for pure decorative elements.

Basic usage

<ec-canvas style={{ width: 280, height: 120, display: 'flex', flexDirection: 'column', padding: 12 }}>
<ec-canvas-background>
<ec-rect x={0} y={0} width={280} height={120} fill="#1a1a2e" radius={12} />
<ec-line x1={16} y1={80} x2={264} y2={80} stroke="#333366" strokeWidth={1} />
</ec-canvas-background>

<ec-canvas-content>
<lvgl-label text="Canvas Card" />
</ec-canvas-content>
</ec-canvas>

This renders a rounded dark card with a horizontal separator line, containing an LVGL label.

Styling

<ec-canvas> supports the same style prop as regular LVGL widgets. Use it for layout (flexbox), sizing, padding, and positioning:

<ec-canvas style={{
width: 200,
height: 100,
display: 'flex',
flexDirection: 'row',
padding: 8,
}}>
{/* ... */}
</ec-canvas>

Event handlers

<ec-canvas> supports touch event triggers:

PropDescription
onPressFires when the canvas is pressed
onReleaseFires when the canvas is released
onClickFires when the canvas is clicked
onLongPressFires when the canvas is long-pressed

Nesting

<ec-canvas> carries the lvgl:widget intent, so it can be placed anywhere a regular LVGL widget is accepted — inside <lvgl-page>, <lvgl-obj>, or any container widget:

<lvgl-obj style={{ display: 'flex', flexDirection: 'row' }}>
<ec-canvas style={{ width: 100, height: 60 }}>
<ec-canvas-background>
<ec-rect x={0} y={0} width={100} height={60} fill="#e53935" radius={8} />
</ec-canvas-background>
<ec-canvas-content>
<lvgl-label text="Red" />
</ec-canvas-content>
</ec-canvas>

<ec-canvas style={{ width: 100, height: 60 }}>
<ec-canvas-background>
<ec-rect x={0} y={0} width={100} height={60} fill="#1e88e5" radius={8} />
</ec-canvas-background>
<ec-canvas-content>
<lvgl-label text="Blue" />
</ec-canvas-content>
</ec-canvas>
</lvgl-obj>

Compilation

At build time, <ec-canvas> is lowered to a native ESPHome LVGL canvas widget:

  • Paint primitives become native canvas draw actions (lvgl.canvas.draw_rectangle, draw_line, draw_arc, draw_polygon, draw_text, draw_image)
  • The canvas buffer is allocated as ARGB8888 with transparent background
  • Widget children are emitted as standard LVGL widget entries
  • Reactive paint props generate C++ bindings that redraw on state changes