d0f860a33e
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m30s
A 96x96 transparent Lottie for the VK app loading screen: the «Э» tile (score 8) drops in under gravity, lands with a soft cushion squash (left/right edges bulge convex, corners follow via a chordal spline), springs back and loops (~1.1s). A warm glint is caught on the bounce. Pure 2D shapes (ddd:0) for player compatibility; ~11KB (<24KB limit). Glyphs are real LiberationSans outlines (Arial-metric, matching the game's font stack). Includes a reproducible, dependency-light build pipeline (build/generate.js + extract.js + glyphs.json) and a README. The preview GIF's green-baize background is preview-only; the asset itself is transparent.
116 lines
5.3 KiB
Markdown
116 lines
5.3 KiB
Markdown
# Erudit — VK loading-screen logo (Lottie)
|
||
|
||
Animated logo for the **VK app loading screen** (shown before the SPA assets load).
|
||
The Erudit «Э» tile (score `8`) **drops in under gravity, lands with a soft squash —
|
||
its left/right edges bulging into a cushion — then springs back up**, looping. A light
|
||
"glint" is caught at the moment of the bounce.
|
||
|
||
| File | Purpose |
|
||
|------|---------|
|
||
| `erudit-loader.json` | The Lottie to upload to VK. |
|
||
| `erudit-loader-preview.gif` | Looping preview. Rendered on a green "baize" background **only in the preview** — the real asset has a **transparent** background. |
|
||
| `build/` | The reproducible build pipeline (see *Regenerate*). |
|
||
|
||
**VK requirements met:** 96×96 px · vector Lottie JSON · ≤ 24 KB (~11 KB) · seamless
|
||
~1.1 s loop · transparent background.
|
||
|
||
Design reference: a photo of the physical wooden Erudit tile.
|
||
|
||
---
|
||
|
||
## How it works
|
||
|
||
A single flat layer holds the tile — a rounded-rect **face path**, the two glyphs, and
|
||
a thin border — and everything is driven by that layer's transform plus a few colour /
|
||
shape tracks. The anchor sits on the tile's **bottom edge**, so the squash happens
|
||
against the floor.
|
||
|
||
### Bounce (gravity)
|
||
`position.y` of the bottom edge goes apex → floor → apex with **no dwell** (the apex is
|
||
an instantaneous turn-around). The fall uses a strong **ease-in** (accelerate) and the
|
||
rise a strong **ease-out** (decelerate), so it reads as real gravity. While falling fast
|
||
the tile slightly **stretches** (tall+thin); that is the squash-and-stretch setup.
|
||
|
||
### Soft cushion (squash)
|
||
On contact the layer **squashes** (scaleY↓, scaleX↑) about the bottom anchor, with a
|
||
touch of skew. Crucially the squash/cushion is **decoupled from the fall speed** — it
|
||
eases in and out *slowly* (`ES`), so the landing feels soft even though the fall is
|
||
fast. The squash is deliberately gentle (≈ 86 % / 112 %).
|
||
|
||
### The cushion shape (the hard part)
|
||
The face is **not** a plain rounded rect — it is a path whose left/right contour bows
|
||
out into a convex cushion on impact:
|
||
- the rest rounded-rect outline is sampled (fine corner arcs + edge mids), and on impact
|
||
every point is pushed outward by a smooth **barrel** profile `f(y) = b·(1 − (y/HH)²)`
|
||
(max at the middle, fading to zero at the top/bottom);
|
||
- so the **whole side — corners included — bows out as one piece**, never just the
|
||
straight middle;
|
||
- bezier handles are computed as a **chordal spline** (handle length ∝ the adjacent
|
||
chord), which stays smooth across the uneven corner/edge point spacing. This is what
|
||
keeps the corner↔cushion junction kink-free — both at rest and fully bulged — which a
|
||
naïve uniform Catmull-Rom (or moving discrete points with fixed tangents) does not.
|
||
|
||
### Glint
|
||
At the bounce the face flashes a little lighter (`FACE_GLINT`) and the glyphs warm
|
||
toward brown-red (`BLACK_LIT`), then settle back. A cheap, warm "catch the light".
|
||
|
||
### Border & background
|
||
The tile carries a thin static **border** a touch darker than the face (`BORDER`) so it
|
||
reads on any background. The **green baize background is added only when rendering the
|
||
preview GIF** — the Lottie itself is transparent.
|
||
|
||
### Player compatibility
|
||
Only plain 2-D shapes (`ddd:0`) — no 3-D layers, expressions or effects — so every
|
||
Lottie player (lottie-web on the web, rlottie on native) renders it identically.
|
||
|
||
---
|
||
|
||
## Glyphs
|
||
|
||
`Э` (U+042D) and `8` are **real outlines from LiberationSans-Regular** — metric-
|
||
compatible with Arial, matching the game's `--font: system-ui … Arial` stack
|
||
(see `ui/src/app.css`). `build/extract.js` pulls the contours into `build/glyphs.json`
|
||
as Lottie cubic paths; a hair of same-colour stroke adds a touch of weight.
|
||
|
||
---
|
||
|
||
## Regenerate
|
||
|
||
Requirements: Node ≥ 18. `extract.js` additionally needs `opentype.js`
|
||
(`npm i opentype.js`); **`generate.js` has no dependencies**.
|
||
|
||
```sh
|
||
cd assets/vk
|
||
|
||
# 1. (optional) re-extract the glyphs — only if you change the font:
|
||
# default font: /usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf
|
||
node build/extract.js [/path/to/font.ttf] # -> build/glyphs.json
|
||
|
||
# 2. build the animation (reads build/glyphs.json):
|
||
node build/generate.js erudit-loader.json # -> erudit-loader.json
|
||
|
||
# 3. (optional) live preview — needs lottie-web (npm i lottie-web):
|
||
node build/build-preview.js erudit-loader.json preview.html
|
||
# then open preview.html in a browser.
|
||
```
|
||
|
||
The preview GIF is assembled by rendering the Lottie frame-by-frame (lottie-web canvas,
|
||
filled with the green baize) and stitching with `ffmpeg`; the live HTML preview is the
|
||
quickest way to eyeball changes.
|
||
|
||
## Tunables (top of `build/generate.js`)
|
||
|
||
| Symbol | Meaning |
|
||
|--------|---------|
|
||
| `OP`, `FR` | loop length (frames) / fps — overall speed |
|
||
| `T_HIT / T_PEAK / T_LIFT / T_REC` | beats: contact / squash peak / lift-off / cushion recovered |
|
||
| `EI / EO / ES` | easings: fast fall / fast rise / slow soft cushion |
|
||
| `APEX`, `FLOOR` | bottom-edge screen-y at the top of the bounce / at rest |
|
||
| `HW`, `HH`, `CR` | tile half-width / half-height / corner radius |
|
||
| `scl` squash values | the squash amount (scaleX↑ / scaleY↓) |
|
||
| `bulge` `b` | cushion depth (how far the sides bow out) |
|
||
| `FACE / FACE_GLINT` | wood face / glint flash |
|
||
| `BORDER` | tile rim colour |
|
||
| `BLACK / BLACK_LIT` | glyph / glyph-at-glint (brown-red) |
|
||
| preview bg `#3C7858` | the green-baize colour used **only** in the GIF |
|