Effects · Decoration
Halftone
A grid of dots on a travelling wave, on the library's motion channel. It stops when the panel scrolls out of view.
Starts when it scrolls into view
NORD FROST · DARK · RADIUS 6/10 · BASE 16PX · SHADOW
Code
Cut from the gallery’s own decorations, so it is the code that drew the frame above rather than a description of it.
fn halftone(cx: &App) -> impl IntoElement {
const COLUMNS: usize = 22;
const ROWS: usize = 9;
let ink = cx.theme().primary.opacity(0.55);
let radius = f32::from(decoration::frame_radius(cx));
decoration::animated("halftone", Duration::from_secs(6), move |delta| {
// One wave crossing the grid, so a dot's size depends on where it is
// as well as when it is — a field rather than a pulse.
let phase = delta * std::f32::consts::TAU;
div()
.size_full()
.children((0..ROWS * COLUMNS).filter_map(move |cell| {
let (row, column) = (cell / COLUMNS, cell % COLUMNS);
let across = (column as f32 / COLUMNS as f32) * std::f32::consts::TAU;
let wave = (phase + across + row as f32 * 0.6).sin() * 0.5 + 0.5;
let size = 3.0 + wave * 13.0;
let x = (column as f32 + 0.5) * CARD.width / COLUMNS as f32;
let y = (row as f32 + 0.5) * CARD.height / ROWS as f32;
Some(dot(x, y, size, radius)?.bg(ink))
}))
})
}
Applying it
Every framed component takes a Decoration, and the layer decides for itself whether it belongs to the shape — either by carrying the frame’s radius, or by not reaching a corner. The two rules are the whole contract.
crates/gallery/src/decorations.rs
let card = ApprovalCard::new("decorated-gate", "Publish the launch plan?")
.description(
"The card is unchanged. Everything behind and in front of it \
is the application's.",
)
.decoration(kind.build(cx))