iso : explications isométriques animées
Une scène raconte une histoire de produit en une boucle de 6 à 14 secondes : les éléments arrivent, la lumière voyage entre eux, une partie s'allume à la fois, et la boucle revient à son premier plan. Un flux n'est qu'une seule forme. Choisissez la forme dont l'histoire a besoin dans le tableau des temps : une boîte qui s'ouvre pour montrer ses couches, un transfert entre deux objets, une grille qui se remplit, une pile qui fait pivoter un choix, ou des tuiles que le spectateur soulève en survolant. Tout est du vrai SVG et HTML textuel, s'adapte à sa boîte, se met en pause hors écran et se fige pour les mouvements réduits.
Le kit se trouve à côté de ce fichier :
lib/iso.ts: le constructeurScene(projection, dalles, faces, fils, impulsions, étiquettes, pistes de mouvement),scene.exportSvg(),responsive()etlazyBox().lib/runtime.ts: joue une scène rendue avec WAAPI, démarre son intro une fois que 40 % de celle-ci (ou la moitié de la hauteur de l'écran, pour une scène haute) est visible, la met en pause hors écran, supprime ses animations une fois qu'elle est à plus d'un demi-écran et les replace sur la même image avant son retour, la fige pour les mouvements réduits, exposewindow.isoSeek(t).scripts/render.ts: rend un module de scène (export par défautPage) en un seul fichier HTML autonome avec le runtime intégré.scripts/shots.mjs: prend des captures d'écran de n'importe quelle page à des moments et largeurs choisis et écritsheet.png.scripts/check.ts: vérification stricte des types des fichiers de scène sur le disque par rapport au kit (bun scripts/check.ts scene.ts).scripts/page-idle.mjs: fait défiler une page avec plusieurs scènes une fois, puis s'arrête à 0, 20, 35, 50, 60 et 80 % et imprime le temps du thread principal à chaque étape, avec le CPU à 1x et 4x (node scripts/page-idle.mjs page=<url> width=393 dpr=3 scroll=4). Exécutez-le sur toute page avec plus de quelques scènes.scripts/perf.mjs: joue une page dans Chrome à une largeur de téléphone avec le CPU limité (node scripts/perf.mjs page=<out.html> width=393 throttle=4 seconds=20) et imprime le taux de trames, les percentiles de temps de trame, les trames tardives, les longues tâches et le temps du thread principal. Exécutez-le sur toute scène avec 30 objets en mouvement ou plus.scripts/contrast.mjs,scripts/render-all.ts: mesure chaque texte dans une page rendue par rapport aux pixels juste derrière ses glyphes à l'image fixe et à quatre autres moments, et échoue en dessous de 4.5:1 (node scripts/contrast.mjs <out.html>;bun scripts/render-all.ts <dir>écrit d'abord chaque exemple du catalogue). Exécutez-le sur chaque scène avant de la déclarer terminée.scripts/study-rive.mjs,scripts/compare-rive.mjs: outils facultatifs pour étudier une animation.rivde votre propre cadre par cadre et la comparer avec une scène du kit. Les deux ont besoin deruntime=<dossier avec canvas_advanced.mjs et rive.wasm>.examples/: 64 scènes pour des produits fictifs, chaque fichier exporteexample(title, story, prompt, level, category, layout, interaction, etart: les scènes compacte et large deart(compact, wide, breakpoint)) et unePagepar défaut, doncscripts/render.tsrend n'importe laquelle d'entre elles. Une scènespotest une petite scène unique,art(compact).hero.tsexportehero(unArt) etmaterial-gallery.tsexportegalleries(unArtpar famille de finition), chacun avec unePagepar défaut.examples/shared.tscontient les surfaces, les rampes de couleurs,paints, les polices etfitX(), qui garde une étiquette à l'intérieur de sa scène sur la carte la plus étroite où elle se trouvera.
Choisissez l'exemple le plus proche de votre histoire et lisez-le avant de dessiner :
| Exemple | Niveau | Montre |
|---|---|---|
notifications.ts | simple | le fichier minimal : un noyau se ramifiant en trois tuiles, impulsions, flash d'arrivée et saut. Lisez-le d'abord |
ci-deploy.ts | simple | un paquet sautant le long d'une chaîne, un écrasement à l'atterrissage, une couleur d'état uniquement à la fin |
file-upload.ts | simple | des morceaux remplissant une barre de progression, puis des copies s'étendant vers des points de bord ; des tirets marchant sur les emplacements vides |
db-branching.ts | simple | un fantôme en pointillé qui devient solide, une tuile de changement qui glisse en arrière et fusionne |
service-map.ts | simple | survol : neuf tuiles slab({ hover }) avec des étiquettes révélées au survol ou au tap, plus une boucle qui raconte toujours l'histoire |
auth-flow.ts | moyen | un téléphone panel() vertical, une saisie tapée, des fantômes de transfert transportant un jeton |
etl-pipeline.ts | moyen | des tuiles source alimentant un noyau le long de fils qui se dessinent avec elles, puis une grille qui se remplit et se vide ; des superpositions regroupées avec la dalle sur laquelle elles reposent pour qu'elles bougent ensemble |
queue-workers.ts | moyen | une file d'attente de slots pilotée par une table d'événements que la build valide (pas de débordement, pas de prise à partir de vide) |
search-index.ts | moyen | des tuiles en ligne sur une page claire, une requête tapée, des correspondances s'allumant, des résultats classés s'élevant |
feature-flags.ts | moyen | jeu : un vrai <button> dans une étiquette et un script intégré qui rejoue une étape de boucle avec seek |
agent-tools.ts | complexe | un noyau avec des satellites, des impulsions d'appel et de réponse, une carte de réponse qui tape |
checkout-payment.ts | complexe | un anneau d'étapes avec un chemin de retour en pointillé et une ligne d'état qui s'estompe |
edge-cache.ts | complexe | la même requête deux fois : un échec chaud vers l'origine, puis un succès froid en périphérie |
observability.ts | complexe | trois aspects de signal (tirets, points, plages), des barres prism avec heightTo, une alerte et une récupération |
keycard-slot.ts, battery-charge.ts, tile-loader.ts, empty-box.ts | spot | un petit objet pour une liste de fonctionnalités, un chargeur ou un état vide ; une carte panel() verticale coupée à la lèvre d'un slot, des cellules heightTo, une vague de chute diagonale, des tirets marchants |
sync-loop.ts, inbox-fill.ts, alert-stack.ts, table-scan.ts | spot | des flèches écrasées sur le plan, un plateau à façade vitrée, des cartes glissant sur une pile avec un badge qui compte et chevauche la carte du dessus, une lentille glissant ligne par ligne |
audit-ledger.ts, dns-resolve.ts, cron-jobs.ts | simple | des pages s'empilant avec des sceaux, un saut à la fois avec des réponses, un cadran qui déclenche des tâches |
vault-rotate.ts, firewall-filter.ts, zero-trust.ts | moyen | un échange de clé avec le motif de slot, un paquet rejeté rebondissant, chaque requête estampillée |
workload-scheduler.ts, autoscale.ts, health-check.ts | moyen | placement à partir d'une petite table de règles, faire de la place pour un nouveau nœud, un nœud retiré et réintégré |
blue-green.ts, snapshot-restore.ts | moyen | un fil re-ciblé, un curseur glissant en arrière le long d'une chronologie |
embedding-clusters.ts, training-loss.ts, recs-rerank.ts, ab-test.ts | moyen | des points se stabilisant en grappes, des barres prism tombantes, une étagère de slot se réordonnant, une division avec un gagnant |
data-erasure.ts, rag-answer.ts | complexe | une requête se ramifiant vers six systèmes avec un compteur, des cartes récupérées volant dans un modèle et une réponse tapant |
hero.ts | complexe | une invite fait croître un système en niveaux de 1, 5, 15 et 40 : une rotation d'entrée et une chute pondérée, des sorties glissant de dessous, des pops, un bassin qui tombe avec un remplissage de grille, des requêtes infinies, une inclinaison vers la souris au repos ; des flashs par fill et des tuiles répétées par <use> pour rester économique ; seulement du SVG text(), donc exportSvg() fonctionne dessus |
funnel-live.ts, billing-cycle.ts, parcel-delivery.ts, test-shards.ts | moyen, complexe | layout: 'wide' : bandes 3:1 lues de gauche à droite (étapes se rétrécissant, une bande de mois qui se décale sans saut, des véhicules transportant une boîte, des fragments courant dans des couloirs et fusionnant) |
kanban-move.ts, stock-sync.ts, diff-merge.ts, refund-flow.ts | simple à complexe | layout: 'diagonal' : une carte ou un jeton voyageant le long d'un axe isométrique, des portes devenant vertes, un remboursement parcourant le même chemin en une seconde couleur |
live-cursors.ts, two-approvals.ts, design-comments.ts, translate-six.ts | simple, moyen | layout: 'panels' : des panel() verticaux se faisant face ; des curseurs tapant, deux coches déverrouillant un cadenas, des épingles et un fil, un message en six langues avec une pile de polices thaï et japonaise |
cms-publish.ts, sensor-gateway.ts, smart-home.ts, currency-convert.ts | simple, moyen | layout: 'radial' : une copie vers cinq canaux, des lectures entrant puis une vague de sol sortant, des appareils changeant d'état tour à tour, une impulsion faisant une fois le tour d'un fil en anneau |
ota-rollout.ts, build-cache.ts, calendar-booking.ts, load-slider.ts | simple à complexe | layout: 'field' : des vagues à travers un champ d'appareils avec un arrêt et une nouvelle tentative, des succès de cache calculés à partir d'une table de dépendances, une réunion évitant un conflit ; load-slider est un contrôle de glissement (un vrai champ de saisie intervalle, souris, tactile et clavier) qui cherche la boucle tandis que le trafic ambiant continue de bouger |
video-transcode.ts, image-resize.ts, fraud-gauge.ts, data-lineage.ts, drill-down.ts | simple à complexe | layout: 'exploded' : des rendus se soulevant et s'encodant, des tailles se décolant, des couches de risque s'ajoutant au-delà d'une ligne, un chemin amont s'allumant à travers des couches ; drill-down ouvre une partie dans ses couches à partir d'un vrai bouton en cherchant le segment de boucle de cette partie |
materials-*.ts, material-gallery.ts | 49 finitions de côté dans six familles (Métal, Verre, Naturel, Motif Tech, Rétro, Générative) en tant que fabriques (scene) => Material, une galerie sans étiquette par famille. Les finitions génératives prennent une graine. Copiez une finition plutôt que d'en inventer une |
Configuration unique : cd <ce dossier>/scripts && bun install && bunx playwright install webkit (les captures d'écran utilisent Google Chrome installé pour Chromium). Vérification de type : scripts/node_modules/.bin/tsc -p <ce dossier>.
1. Storyboard avant de dessiner
Ouvrez d'abord recipes.md. Il contient tous les motifs de mouvement et de composition mesurés (arrivées, connexions, flux, comparaisons, choix, inactif, interaction, easings, surfaces), chacun avec ses nombres et appels au kit. Composez l'histoire à partir de ces recettes et conservez leurs minutages.
Écrivez d'abord l'histoire sous forme de tableau chronologique : ce que le spectateur doit comprendre, les 3 à 6 temps qui le montrent, et pour chaque temps quel élément bouge, comment, quand et pendant combien de temps. Une idée par temps. Un temps qui nécessite un paragraphe pour s'expliquer est deux temps ou la mauvaise image.
Formes de temps éprouvées, avec des minutages mesurés :
| Temps | Ce qui se passe | Minutage |
|---|---|---|
| Arriver | Les pièces tombent une par une, dépassent un peu, se stabilisent | dropIn : 0,55 s chacune, 83 ms de décalage, 46 px de chute |
| Transfert | Des contours fantômes d'une tuile glissent d'un objet à un autre | 4 fantômes, 0,22 s d'écart, 1,2 s chacun, ease.io |
| Voyager | Une courte impulsion lumineuse court le long d'un fil ; la cible clignote à l'arrivée | 270 px/s, impulsion ~0,12 à 0,16 du fil, montée 0,1 s, fondu 0,6 s |
| Révéler les couches | Une boîte solide devient transparente ; les couches intérieures s'allument une à la fois avec une étiquette | allumage 0,23 s, maintien ~1 s, extinction 0,2 s, la suivante commence à la fin de la dernière |
| Faire pivoter le choix | Une pile monte d'un cran ; la carte active devient d'une couleur d'accent | 0,9 s de déplacement toutes les 3 s, démarrage lent, longue sortie en douceur |
| Remplir et vider | Une grille se remplit tuile par tuile, tient, retombe à vide | 7 images (0,12 s) d'écart à l'entrée, 2 images d'écart à la sortie |
| Tirer | Quand un fil rétrécit ou grandit, la boîte à laquelle il est attaché est tirée d'environ 6 px le long du fil, tient, puis revient en douceur, donnant l'impression que le fil a une tension | 0,23 s de traction, 0,25 s de maintien, 0,65 s de retour avec une longue sortie en douceur |
| Soulever au survol | Une tuile s'élève de 16 px et prend sa couleur de marque, son nom apparaît | slab({ hover }) : 270 ms d'activation, 325 ms de désactivation, ease.out |
Règle de connexion : un fil est à l'écran seulement lorsque ses deux extrémités le sont. Un fil vers quelque chose qui arrive au milieu de la boucle se dessine avec son impulsion (wire({ dash: '1 1' }), puis at(id, 'strokeDashoffset', t, d, 1, 0, ease.linear)) et part avec la chose qu'il rejoint. Ne mettez jamais un tel fil dans l'intro. Un fil ne traverse jamais la face supérieure d'une boîte : ajoutez-le avant les objets qu'il rejoint (afin qu'ils le peignent par-dessus) et commencez-le au bord de la boîte, pas à son centre.
Règle de boucle : la dernière image égale la première. Tout ce qui change pendant la boucle change en retour avant la fin. Le mouvement de l'intro va dans intro() ; la boucle commence quand l'intro se termine, et l'intro doit se terminer sur la même image que celle par laquelle la boucle commence. Un élément sans intro montre la valeur de départ de la boucle pendant l'intro, qui est le dernier to de ses segments de boucle.
L'ordre de peinture est fixe : ce qui est ajouté plus tard recouvre toujours ce qui a été ajouté plus tôt. Un mouvement qui changerait qui recouvre qui (une carte passant du haut de la pile au bas) est dessiné sous forme de slots à la place. Chaque élément est un slot qui se déplace d'un pas et s'estompe ou se recolore comme son voisin, et chaque segment recommence à partir de la valeur de repos du slot, donc tous les slots sautent en arrière ensemble tandis que l'image semble la même. Donnez à chaque slot une étiquette par option et faites un fondu entre elles pour montrer celle qui est à côté du slot actif.
2. Choisissez une mise en page, puis composez d'abord pour le téléphone
Chaque exemple nomme sa composition dans layout : wide (une longue bande lue de gauche à droite ; sa scène large est d'environ 3:1, comme 1200x400, avec un point de rupture de 720, et la vitrine la montre comme une carte pleine largeur), diagonal (une longue rangée le long d'un axe isométrique), panels (des panel() verticaux se faisant face), radial (un noyau avec des choses sur un anneau), field (plusieurs tuiles vues de haut ; un ratio légèrement plus raide aide), exploded (des couches soulevées verticalement) ou object (un ou deux objets pour une liste de fonctionnalités ou un état vide). Choisissez celle dont l'histoire a besoin avant de dessiner ; une page de scènes se lit mieux quand elles varient.
Une scène que le spectateur peut jouer (interaction: 'play' | 'drag' | 'explore') livre un petit script de module intégré dans art()'s head : vrai <button> ou <input type="range"> dans une label({ html }), événements de pointeur pour la souris et le tactile (touch-action: none sur un contrôle de glissement), les touches aussi, un MutationObserver pour lier les scènes qui se montent plus tard, la boucle en pause pendant que le spectateur agit et reprise après quelques secondes d'inactivité, et une boucle qui raconte toujours l'histoire sans entrée (feature-flags.ts, load-slider.ts, drill-down.ts).
Les pages de stand et d'atterrissage sont ouvertes sur des téléphones. Construisez deux scènes et enveloppez-les avec responsive(compact, wide) :
- compact : environ 390 de large, portrait ou carré (390x420 à 390x640). Empilez l'histoire de haut en bas. Concevez en unités 390 pour qu'une étiquette
size: 13fasse 13 px sur un téléphone. Jamais en dessous de 13. - wide : environ 900 à 1200 de large, paysage, l'histoire de gauche à droite.
Sa
... [tronqué pour la traduction]