Phoenix LiveView function component that attaches Etcher's annotation
overlay to a named <Fresco.canvas>.
The component renders a hidden host <div phx-hook="EtcherLayer">. The
client-side hook:
- Looks up the named Fresco canvas via
window.Fresco.onReady/2 - Appends a pencil button to Fresco's nav column via
handle.appendNavButton/3 - On pencil click, opens a bottom toolbar with the configured drawing tools and toggles annotation mode
- Draws shapes as SVG overlays locked to canvas-pixel coordinates
- Hydrates initial annotations from
handle.getExtension("etcher") - Pushes
etcher:annotations-changedevents to the consumer's LiveView with the full annotations array on every commit / edit / delete
Usage
<Fresco.canvas id="board" canvas={@canvas} class="w-full h-screen" />
<Etcher.layer
fresco_id="board"
tools={[:rectangle, :circle, :polygon, :freehand, :callout, :text, :dimension, :eraser]}
/>The event your LiveView must handle
def handle_event("etcher:annotations-changed", %{"annotations" => annotations}, socket) do
new_canvas =
Fresco.Canvas.put_extension(socket.assigns.canvas, "etcher", %{
"version" => "1",
"annotations" => annotations
})
{:noreply, assign(socket, canvas: new_canvas)}
endThat's the only required wiring. UUIDs are generated client-side (UUIDv7), so there's no tmp_id β real-uuid round-trip β the server never has to assign ids.
Tools
Configure which drawing tools appear in the bottom toolbar. The default exposes all seven drawing kinds plus the eraser:
tools={[:rectangle, :circle, :polygon, :freehand, :callout, :text, :dimension, :eraser]}Subsetting hides specific tools (e.g. only :rectangle, :freehand).
Drop :eraser if you don't want users deleting from the toolbar.
Annotation hydration
Initial annotations come from the canvas's extensions.etcher map.
The consumer's mount/3 typically reads a .fresco file:
canvas = Fresco.Canvas.read!("/path/to/scene.fresco")
{:ok, assign(socket, canvas: canvas)}β¦or builds the canvas with pre-loaded annotations:
etcher_data = %{
"version" => "1",
"annotations" => [
%{"uuid" => "01HXY...", "kind" => "rectangle",
"geometry" => %{"x" => 100, "y" => 100, "w" => 200, "h" => 150}}
]
}
canvas = Fresco.Canvas.new(width: 1000, height: 1000)
|> Fresco.Canvas.put_extension("etcher", etcher_data)Either way <Etcher.layer> reads the annotations through Fresco's
handle at mount time and renders each shape.
Summary
Functions
Mounts an Etcher annotation layer onto a named Fresco canvas.
Functions
Mounts an Etcher annotation layer onto a named Fresco canvas.
Renders a hidden <div phx-hook="EtcherLayer"> that hosts the JS
engine; the visible UI (pencil nav button + bottom toolbar + SVG
shapes) is created by the hook on top of the Fresco canvas.
Attributes
fresco_id(:string) (required) - DOM id of the<Fresco.canvas>this layer attaches to.id(:string) - Optional DOM id for the layer host element; defaults to"etcher-layer-<fresco_id>". Defaults tonil.tools(:list) - Subset of drawing tools to show in the toolbar. Defaults to[:rectangle, :circle, :polygon, :freehand, :callout, :text, :dimension, :eraser].- Global attributes are accepted.