The scene model: a fixed-size canvas plus a z-ordered list of elements.
A scene is a design with holes. Text and image values may be literals
or placeholder/1 references resolved at render time against a values
map (see OpenFresco.render_svg/3). The struct is versioned and
serializes to/from JSON (to_json!/1 / from_json!/1) so it can be
stored — e.g. inside a Fresco canvas's extensions["open_fresco"] blob —
and migrated forward (migrate/1).
Elements
Elements are plain maps (mirroring Fresco.Canvas's image maps) with a
:type tag and a common :box (%{x, y, w, h}), :id, and :z
(z-order, ascending). Build them with the constructors:
text/2— a text block (:value,:size,:weight,:font,:fill,:align,:line_height)image/2— a positioned image (:value,:fit,:radius)shape/2— a filled (rounded) rectangle (:fill,:radius)button/2— a shape + centered label composite (:label,:preset,:fill,:text_fill,:radius)
Fills
Any fillable surface takes a fill built with solid/1, gradient/2, or
image_fill/2. Fills are tagged maps so a fill-type switch in an editor
can round-trip the inactive variants' values.
Values
A value is either a literal binary or placeholder(key). resolve/2
turns a value into {:ok, resolved} or :unresolved against a map.
Summary
Functions
Append an element (map from a constructor) to the scene.
An anchor: position an element relative to another (by id). edge is
:below / :above / :left / :right; :gap (default 16) is the space
between; :align (:start / :center / :end, default :start) aligns
the cross-axis. Pass as the :anchor option to an element builder. When
the target's rendered height changes (e.g. a title wraps), the anchored
element moves during layout. Chains resolve in order; cycles are rejected.
A button composite (rounded shape + centered label). :preset is
:solid (fill), :outline (stroke only), or :soft (tinted fill).
auto_width: true sizes the box to the measured label plus :padding
(horizontal, default 24) during layout; otherwise the explicit :box wins.
Parse a scene from a JSON string, running migrate/1.
Build a scene from a decoded JSON map (string keys), running migrate/1.
A linear-gradient fill. angle is in degrees, measured clockwise from
"to the right" (0 = left→right, 90 = top→bottom). Each stop is
%{offset: 0..1, color: "#rrggbb", alpha: 0..1}.
A positioned-image element. :value resolves to an href (or draws the
unresolved stand-in). Supports an :underlay for legibility, same as text.
An image fill. fit is :cover, :contain, or :stretch. Used as a
canvas :background, an optional :overlay_color (:dark/:light) +
:overlay_opacity tints the image (og's hero-darkening legibility trick).
Migrate an older scene to the current version. Currently the identity for
"1"; the seam is here so stored scenes survive format changes.
A new empty scene. :width / :height default to the 1200×630 OG size;
:background is an optional fill (default nil = transparent).
A canvas-relative placement (CSS-inset style), resolved against the canvas
size at render time — so a template built with :place adapts to any
canvas dimensions (landscape, square, portrait). Pass as an element's
:place option; it overrides the static :box position on the axes it
specifies.
A placeholder value, resolved at render time against the values map.
Set the canvas size (elements with :place re-place at render time).
Resolve a value against a values map.
A filled (rounded) rectangle element. :fill may be solid/gradient/image;
:stroke (+ :stroke_width) adds a border; :underlay a legibility box.
A solid-color fill.
A preset-text element — text/2 with type: :stamp. Mirrors og's stamp
(a reusable text snippet, e.g. a fixed byline); :preset is an alias for
:value. Renders identically to text.
A text-block element. Text word-wraps to :box width and vertically
aligns via :valign (:top / :middle / :bottom). An :underlay
(opacity > 0) draws a soft glow behind the text for legibility over busy
backgrounds — :underlay_color is :dark or :light.
Serialize to a JSON string.
Serialize to a JSON-ready map (string keys).
Types
@type fill() :: %{type: :solid, color: String.t()} | %{type: :gradient, angle: number(), stops: [gradient_stop()]} | %{type: :image, value: value(), fit: :cover | :contain | :stretch}
Functions
Append an element (map from a constructor) to the scene.
An anchor: position an element relative to another (by id). edge is
:below / :above / :left / :right; :gap (default 16) is the space
between; :align (:start / :center / :end, default :start) aligns
the cross-axis. Pass as the :anchor option to an element builder. When
the target's rendered height changes (e.g. a title wraps), the anchored
element moves during layout. Chains resolve in order; cycles are rejected.
A button composite (rounded shape + centered label). :preset is
:solid (fill), :outline (stroke only), or :soft (tinted fill).
auto_width: true sizes the box to the measured label plus :padding
(horizontal, default 24) during layout; otherwise the explicit :box wins.
Parse a scene from a JSON string, running migrate/1.
Build a scene from a decoded JSON map (string keys), running migrate/1.
@spec gradient(number(), [gradient_stop()]) :: fill()
A linear-gradient fill. angle is in degrees, measured clockwise from
"to the right" (0 = left→right, 90 = top→bottom). Each stop is
%{offset: 0..1, color: "#rrggbb", alpha: 0..1}.
A positioned-image element. :value resolves to an href (or draws the
unresolved stand-in). Supports an :underlay for legibility, same as text.
An image fill. fit is :cover, :contain, or :stretch. Used as a
canvas :background, an optional :overlay_color (:dark/:light) +
:overlay_opacity tints the image (og's hero-darkening legibility trick).
Migrate an older scene to the current version. Currently the identity for
"1"; the seam is here so stored scenes survive format changes.
A new empty scene. :width / :height default to the 1200×630 OG size;
:background is an optional fill (default nil = transparent).
A canvas-relative placement (CSS-inset style), resolved against the canvas
size at render time — so a template built with :place adapts to any
canvas dimensions (landscape, square, portrait). Pass as an element's
:place option; it overrides the static :box position on the axes it
specifies.
Keys (all optional, canvas px): :left / :right / :top / :bottom
insets from the edges, and :w / :h explicit sizes. Per axis:
- both insets → stretches between them (
w = width - left - right) - one inset + size → pinned to that edge at that size
- size only → centered on that axis
- neither → keeps the element's
:boxvalue
Examples
# A content strip 64px in from both sides, pinned 48px above the bottom:
Scene.place(left: 64, right: 64, bottom: 48, h: 200)
# A centered square badge:
Scene.place(w: 120, h: 120, top: 40)
A placeholder value, resolved at render time against the values map.
Set the canvas size (elements with :place re-place at render time).
Resolve a value against a values map.
A literal binary resolves to itself. A placeholder resolves to
values[key] when present and non-nil, else :unresolved.
A filled (rounded) rectangle element. :fill may be solid/gradient/image;
:stroke (+ :stroke_width) adds a border; :underlay a legibility box.
A solid-color fill.
A preset-text element — text/2 with type: :stamp. Mirrors og's stamp
(a reusable text snippet, e.g. a fixed byline); :preset is an alias for
:value. Renders identically to text.
A text-block element. Text word-wraps to :box width and vertically
aligns via :valign (:top / :middle / :bottom). An :underlay
(opacity > 0) draws a soft glow behind the text for legibility over busy
backgrounds — :underlay_color is :dark or :light.
Serialize to a JSON string.
Serialize to a JSON-ready map (string keys).