iso: Explicadores isométricos animados
Una escena cuenta una historia de producto en un solo bucle de 6 a 14 segundos: las cosas llegan, la luz viaja entre ellas, una parte se ilumina a la vez y el bucle vuelve a su primer fotograma. Un flujo es solo una forma. Elige la forma que la historia necesita de la tabla de ritmos: una caja que se abre para mostrar sus capas, una transferencia entre dos objetos, una cuadrícula que se llena, una pila que gira para ofrecer una opción, o mosaicos que el espectador levanta al pasar el ratón. Todo es SVG y HTML real, se escala con su contenedor, se pausa fuera de pantalla y se detiene para movimiento reducido.
El kit se encuentra junto a este archivo:
lib/iso.ts: el constructorScene(proyección, losas, caras, cables, pulsos, etiquetas, pistas de movimiento),scene.exportSvg(),responsive()ylazyBox().lib/runtime.ts: reproduce una escena renderizada con WAAPI, inicia su introducción una vez que el 40 % de ella (o la mitad de la altura de la pantalla, para una escena alta) está visible, la pausa fuera de pantalla, elimina sus animaciones cuando está a más de media pantalla de distancia y las restaura en el mismo fotograma antes de que regrese, la detiene para movimiento reducido, exponewindow.isoSeek(t).scripts/render.ts: renderiza un módulo de escena (Pagecomo exportación por defecto) a un archivo HTML independiente con el tiempo de ejecución incrustado.scripts/shots.mjs: captura pantallas de cualquier página en momentos y anchos elegidos y escribesheet.png.scripts/check.ts: verificación estricta de tipos de archivos de escena en cualquier lugar del disco contra el kit (bun scripts/check.ts scene.ts).scripts/page-idle.mjs: desplaza una página con muchas escenas una vez, luego se detiene en 0, 20, 35, 50, 60 y 80 % e imprime el tiempo del hilo principal en cada uno, con la CPU a 1x y 4x (node scripts/page-idle.mjs page=<url> width=393 dpr=3 scroll=4). Ejecútalo en cualquier página con más de unas pocas escenas.scripts/perf.mjs: reproduce una página en Chrome con un ancho de teléfono y la CPU limitada (node scripts/perf.mjs page=<out.html> width=393 throttle=4 seconds=20) e imprime la frecuencia de fotogramas, percentiles de tiempo de fotograma, fotogramas tardíos, tareas largas y tiempo del hilo principal. Ejecútalo en cualquier escena con 30 o más objetos en movimiento.scripts/contrast.mjs,scripts/render-all.ts: mide cada texto en una página renderizada contra los píxeles justo detrás de sus glifos en el fotograma estático y cuatro momentos más, y falla por debajo de 4.5:1 (node scripts/contrast.mjs <out.html>;bun scripts/render-all.ts <dir>escribe primero cada ejemplo del catálogo). Ejecútalo en cada escena antes de darla por terminada.scripts/study-rive.mjs,scripts/compare-rive.mjs: herramientas opcionales para estudiar una animación.rivpropia fotograma a fotograma y compararla con una escena del kit. Ambas necesitanruntime=<carpeta con canvas_advanced.mjs y rive.wasm>.examples/: 64 escenas para productos ficticios, cada una es un archivo que exportaexample(título, historia, prompt, nivel, categoría, diseño, interacción yart: las escenas compacta y ancha deart(compact, wide, breakpoint)) y unPagepor defecto, por lo quescripts/render.tsrenderiza cualquiera de ellas. Una escenaspotes una sola escena pequeña,art(compact).hero.tsexportahero(unArt) ymaterial-gallery.tsexportagalleries(unArtpor familia de acabado), cada una con unPagepor defecto.examples/shared.tscontiene las superficies, rampas de color,paints, fuentes yfitX(), que mantiene una etiqueta dentro de su escena en la tarjeta más estrecha en la que se colocará.
Elige el ejemplo más cercano a tu historia y léelo antes de dibujar:
| Ejemplo | Nivel | Muestra |
|---|---|---|
notifications.ts | simple | el archivo mínimo: un núcleo que se ramifica en tres mosaicos, pulsos, destello de llegada y salto. Léelo primero |
ci-deploy.ts | simple | un paquete saltando a lo largo de una cadena, un aterrizaje con compresión, un color de estado solo al final |
file-upload.ts | simple | fragmentos llenando una barra de progreso, luego copias que se extienden a puntos de borde; guiones de marcha en ranuras vacías |
db-branching.ts | simple | un fantasma discontinuo que se vuelve sólido, un mosaico de cambio que se desliza hacia atrás y se fusiona |
service-map.ts | simple | hover: nueve mosaicos slab({ hover }) con etiquetas reveladas al pasar el ratón o al tocar, además de un bucle que aún cuenta la historia |
auth-flow.ts | medio | un teléfono panel() vertical, entrada tecleada, fantasmas de transferencia que llevan un token |
etl-pipeline.ts | medio | mosaicos fuente que alimentan un núcleo a lo largo de cables que se dibujan con ellos, luego una cuadrícula que se llena y vacía; superposiciones agrupadas con la losa en la que se asientan para que se muevan juntas |
queue-workers.ts | medio | una cola de ranuras impulsada por una tabla de eventos que la compilación valida (sin desbordamiento, sin tomar de vacío) |
search-index.ts | medio | mosaicos de arte lineal en una página clara, una consulta tecleada, coincidencias iluminadas, resultados clasificados que ascienden |
feature-flags.ts | medio | reproducir: un <button> real en una etiqueta y un script en línea que repite un paso del bucle con seek |
agent-tools.ts | complejo | un núcleo con satélites, pulsos de llamada y respuesta, una tarjeta de respuesta que escribe |
checkout-payment.ts | complejo | un anillo de pasos con una ruta de retorno discontinua y una línea de estado que se funde |
edge-cache.ts | complejo | la misma solicitud dos veces: un fallo en caliente al origen, luego un acierto en frío en el borde |
observability.ts | complejo | tres looks de señal (guiones, puntos, tramos), barras prism con heightTo, una alerta y recuperación |
keycard-slot.ts, battery-charge.ts, tile-loader.ts, empty-box.ts | spot | un objeto pequeño para una lista de características, cargador o estado vacío; una tarjeta panel() vertical recortada en el borde de una ranura, celdas heightTo, una onda de caída diagonal, guiones de marcha |
sync-loop.ts, inbox-fill.ts, alert-stack.ts, table-scan.ts | spot | flechas aplastadas sobre el plano, una bandeja con frente de vidrio, tarjetas deslizándose sobre una pila con una insignia que cuenta y cabalga la tarjeta superior, una lente que se desliza fila por fila |
audit-ledger.ts, dns-resolve.ts, cron-jobs.ts | simple | páginas apilándose con sellos, un salto a la vez con respuestas, un dial que activa trabajos |
vault-rotate.ts, firewall-filter.ts, zero-trust.ts | medio | un intercambio de claves con el patrón de ranura, un paquete rechazado que rebota, cada solicitud sellada |
workload-scheduler.ts, autoscale.ts, health-check.ts | medio | colocación desde una pequeña tabla de reglas, hacer espacio para un nuevo nodo, un nodo retirado y reincorporado |
blue-green.ts, snapshot-restore.ts | medio | un cable redirigido, un cursor deslizándose hacia atrás a lo largo de una línea de tiempo |
embedding-clusters.ts, training-loss.ts, recs-rerank.ts, ab-test.ts | medio | puntos asentándose en clústeres, barras prism que caen, un estante de ranuras reordenándose, una división con un ganador |
data-erasure.ts, rag-answer.ts | complejo | una solicitud que se ramifica a seis sistemas con un contador, tarjetas recuperadas volando hacia un modelo y una respuesta que escribe |
hero.ts | complejo | un prompt que hace crecer un sistema en niveles de 1, 5, 15 y 40: un giro de plato giratorio y una caída ponderada, salidas deslizándose desde abajo, pops, un grupo que cae con un llenado de cuadrícula, solicitudes interminables, una inclinación hacia el ratón en reposo; destellos por fill y mosaicos repetidos por <use> para mantenerse económicos; solo SVG text(), por lo que exportSvg() funciona con él |
funnel-live.ts, billing-cycle.ts, parcel-delivery.ts, test-shards.ts | medio, complejo | layout: 'wide': tiras 3:1 leídas de izquierda a derecha (etapas que se estrechan, una tira de mes que se desplaza sin salto, vehículos que transportan una caja, fragmentos que corren en carriles y se fusionan) |
kanban-move.ts, stock-sync.ts, diff-merge.ts, refund-flow.ts | simple a complejo | layout: 'diagonal': una tarjeta o token que viaja a lo largo de un eje isométrico, puertas que se vuelven verdes, un reembolso que recorre la misma ruta en un segundo color |
live-cursors.ts, two-approvals.ts, design-comments.ts, translate-six.ts | simple, medio | layout: 'panels': panel() verticales enfrentados; cursores escribiendo, dos marcas que desbloquean un candado, pines y un hilo, un mensaje en seis idiomas con una pila de fuentes tailandesa y japonesa |
cms-publish.ts, sensor-gateway.ts, smart-home.ts, currency-convert.ts | simple, medio | layout: 'radial': una copia a cinco canales, lecturas entrando y luego una onda de piso hacia afuera, dispositivos cambiando de estado por turno, un pulso una vez alrededor de un cable de anillo |
ota-rollout.ts, build-cache.ts, calendar-booking.ts, load-slider.ts | simple a complejo | layout: 'field': ondas a través de un campo de dispositivos con una parada y reintento, aciertos de caché calculados a partir de una tabla de dependencias, una reunión que salta un conflicto; load-slider es un control de arrastre (un input de rango real, mouse, toque y teclas) que busca en el bucle mientras el tráfico ambiental sigue moviéndose |
video-transcode.ts, image-resize.ts, fraud-gauge.ts, data-lineage.ts, drill-down.ts | simple a complejo | layout: 'exploded': versiones que se elevan y codifican, tamaños que se desprenden, capas de riesgo que se acumulan más allá de una línea, un camino ascendente que se ilumina a través de capas; drill-down abre una parte en sus capas desde un botón real buscando el segmento de bucle de esa parte |
materials-*.ts, material-gallery.ts | 49 acabados laterales en seis familias (Metal, Vidrio, Natural, Patrón técnico, Retro, Generativo) como fábricas (scene) => Material, una galería sin etiquetas por familia. Los acabados generativos toman una semilla. Copia un acabado en lugar de inventar uno |
Configuración única: cd <este dir>/scripts && bun install && bunx playwright install webkit (las capturas de pantalla usan Google Chrome instalado para Chromium). Verificación de tipos: scripts/node_modules/.bin/tsc -p <este dir>.
1. Haz un storyboard antes de dibujar
Primero abre recipes.md. Contiene cada patrón de movimiento y composición medido (llegadas, conexiones, flujos, comparaciones, elección, inactividad, interacción, easing, superficies), cada uno con sus números y llamadas al kit. Compón la historia a partir de esas recetas y mantén sus tiempos.
Escribe primero la historia como una tabla de línea de tiempo: qué debe entender el espectador, los 3 a 6 ritmos que lo muestran, y para cada ritmo qué elemento se mueve, cómo, cuándo y durante cuánto tiempo. Una idea por ritmo. Un ritmo que necesita un párrafo para explicarse son dos ritmos o la imagen equivocada.
Formas de ritmo probadas, con tiempos medidos:
| Ritmo | Qué sucede | Tiempo |
|---|---|---|
| Llegada | Las piezas caen una por una, sobrepasan un poco, se asientan | dropIn: 0.55 s cada una, 83 ms de desfase, 46 px de caída |
| Transferencia | Contornos fantasma de un mosaico se deslizan de un objeto a otro | 4 fantasmas, 0.22 s de diferencia, 1.2 s cada uno, ease.io |
| Viaje | Un pulso de luz corto recorre un cable; el objetivo parpadea al llegar | 270 px/s, pulso ~0.12 a 0.16 del cable, subida 0.1 s, desvanecimiento 0.6 s |
| Revelar capas | Una caja sólida se vuelve transparente; las capas internas se iluminan una a la vez con una etiqueta | encendido 0.23 s, mantener ~1 s, apagado 0.2 s, el siguiente comienza cuando termina el último |
| Girar la elección | Una pila se desplaza hacia arriba una ranura; la tarjeta activa se vuelve acento | 0.9 s de movimiento cada 3 s, inicio lento, ease-out largo |
| Llenar y vaciar | Una cuadrícula se llena mosaico por mosaico, se mantiene, vuelve a vaciarse | 7 fotogramas (0.12 s) de separación al entrar, 2 fotogramas de separación al salir |
| Tirón | Cuando un cable se encoge o crece, la caja a la que está unido se mueve unos 6 px a lo largo del cable, se mantiene, luego vuelve suavemente, para que el cable parezca tener tensión | 0.23 s de tirón, 0.25 s de espera, 0.65 s de regreso con un ease-out largo |
| Levantar al hover | Un mosaico se eleva 16 px y toma su color de marca, su nombre aparece | slab({ hover }): 270 ms encendido, 325 ms apagado, ease.out |
Regla de conexión: un cable está en pantalla solo mientras ambos extremos lo están. Un cable hacia algo que llega a medio bucle se dibuja a sí mismo con su pulso (wire({ dash: '1 1' }), luego at(id, 'strokeDashoffset', t, d, 1, 0, ease.linear)) y se va con lo que se une. Nunca pongas ese cable en la introducción. Un cable nunca cruza la cara superior de una caja: agrégalo antes de los objetos que une (para que se pinten sobre él) y comiénzalo en el borde de la caja, no en su centro.
Regla del bucle: el último fotograma es igual al primero. Cualquier cosa que cambie durante el bucle debe volver a cambiar antes de que termine. El movimiento solo de introducción va en intro(); el bucle comienza cuando termina la introducción, y la introducción debe terminar en la misma imagen con la que comienza el bucle. Un elemento sin introducción muestra el valor inicial del bucle durante la introducción, que es el último to de sus segmentos de bucle.
El orden de pintura es fijo: lo que se agrega después siempre cubre lo que se agregó antes. El movimiento que cambiaría quién cubre a quién (una tarjeta que se mueve de la parte superior a la inferior de una pila) se dibuja como ranuras en su lugar. Cada elemento es una ranura que se mueve un paso y se desvanece o recolorea como su vecino, y cada segmento comienza de nuevo desde el valor de reposo de la ranura, por lo que todas las ranuras saltan hacia atrás juntas mientras la imagen se ve igual. Dale a cada ranura una etiqueta por opción y funde entre cuál se muestra junto a la ranura activa.
2. Elige un diseño, luego compón primero para el teléfono
Cada ejemplo nombra su composición en layout: wide (una tira larga leída de izquierda a derecha; su escena ancha es aproximadamente 3:1, por ejemplo 1200x400, con breakpoint 720, y la vitrina lo muestra como una tarjeta de ancho completo), diagonal (una fila larga a lo largo de un eje isométrico), panels (panel() verticales enfrentados), radial (un núcleo con cosas en un anillo), field (muchos mosaicos vistos desde arriba; un ratio ligeramente más inclinado ayuda), exploded (capas separadas verticalmente) u object (uno o dos objetos para una lista de características o estado vacío). Elige el que la historia necesita antes de dibujar; una página de escenas se lee mejor cuando varían.
Una escena que el espectador puede reproducir (interaction: 'play' | 'drag' | 'explore') incluye un pequeño script de módulo en línea en el head de art(): <button> real o <input type="range"> en una label({ html }), eventos de puntero para mouse y toque (touch-action: none en un control de arrastre), teclas también, un MutationObserver para vincular escenas que se montan más tarde, el bucle pausado mientras el espectador actúa y reanudado después de unos segundos de inactividad, y un bucle que aún cuenta la historia sin entrada (feature-flags.ts, load-slider.ts, drill-down.ts).
Las páginas de cabina y aterrizaje se abren en teléfonos. Construye dos escenas y envuélvelas con responsive(compact, wide):
- compact: aproximadamente 390 de ancho, retrato o cuadrado (390x420 a 390x640). Apila la historia de arriba a abajo. Diseña en unidades de 390 para que una etiqueta
size: 13sea 13 px en un teléfono. Nunca por debajo de 13. - wide: aproximadamente 900 a 1200 de ancho, apaisado, la historia de izquierda a derecha.
Sa
…
[truncado para traducción]