Colores, layout y escenas Web
Disponible desde 0.1.0-rc.7, entregado en #308–#313. Estas funciones amplían /web experimental sin depender de un framework o NativeScript. La paridad funcional completa sigue siendo el objetivo general; esta versión no cubre todas las APIs nativas.
Colores, bordes y bindings continuos
La reproducción finita acepta backgroundColor, color, borderColor y sus cuatro lados; borderWidth y sus cuatro anchos; borderRadius, cuatro esquinas independientes y los radios X/Y de cada esquina. Las propiedades explícitas prevalecen sobre abreviaturas. Los números representan píxeles CSS para longitudes y ARGB empaquetado para colores. Las cadenas CSS se resuelven contra el destino al preparar la llamada; las esquinas admiten sintaxis elíptica y porcentajes.
Los colores usan el compilador compartido de OKLab con alfa premultiplicada y salida sRGB. Anchos y radios no son negativos y respetan geometría y estilo del borde. Jolt no muestra un borde con border-style: none, reemplaza imágenes de fondo ni cambia el contorno de foco. Cada canal de pintura se reemplaza independientemente; las capas aditivas/multiplicativas mantienen el contrato de transform/opacidad.
.to() también acepta cadenas de color y devuelve WebColorValue de solo lectura. Aplica springValue() antes del mapa; los colores no tienen velocidad escalar. bind() combina estos nodos y decoraciones numéricas en el scheduler existente bajo demanda. Descartar la fuente libera dependientes; descartar el binding conserva la pintura actual. Un handle antiguo no sobrescribe al nuevo.
Los mapas de color son independientes del destino: pasa colores resueltos, no variables CSS del elemento ni currentColor. El animate() finito resuelve esas expresiones contra su destino.
import { createWebRuntime } from '@rafaelaugusto/jolt/web'
const motion = createWebRuntime(document)
motion.animate(
card,
{
backgroundColor: '#d8f381',
color: '#15291e',
borderTopColor: '#dc672a',
borderRightWidth: 8,
borderRadius: '42px 12px 56px 20px / 16px 32px 24px 48px',
},
'snappy',
)
const input = motion.motionValue(0)
const follower = motion.springValue(input)
const paint = motion.bind(card, {
backgroundColor: follower.to([0, 1], ['#7655e8', '#ed8b46']),
borderRadius: follower.to([0, 1], [12, 64]),
})
input.set(1)Tamaño real y layout visual
animateSize() cambia width y height reales, por separado o juntos, con destinos numéricos y auto. El texto se redistribuye y los vecinos participan en layout normal. Box sizing, min/max, Grid y Flexbox siguen determinando la asignación. En Flexbox se interpola el tamaño CSS preferido para no aplicar grow/shrink dos veces. Los destinos intrínsecos se vuelven a medir mientras están activos; contenido nuevo puede prolongar la operación. La finalización restaura auto semántico o la declaración numérica solicitada.
La mutación es síncrona tras la captura; las microtareas del framework se ejecutan antes de medir. Carga datos primero. Mutaciones asíncronas, repeticiones y seek no corresponden a un destino intrínseco cambiante. El reemplazo hereda valor/velocidad en ejes compartidos, restaura ejes omitidos y resuelve la operación anterior como false. Pause elimina frames de tamaño. stop() conserva el tamaño; stop({ restore: true }), retirada y descarte restauran declaraciones todavía propias sin deshacer contenido ni escrituras externas nuevas.
animateLayout() captura hijos HTMLElement directos por identidad, aplica la mutación y mide en el siguiente frame. Anima posición y tamaño inversos mediante contribuciones privadas, preservando aplicación/press, transforms 2D estáticos y orígenes. Los nuevos hijos pueden aparecer con fade; los retirados salen inmediatamente. size: false desactiva inversión de tamaño y enter: false la entrada. Reemplazar recaptura la pose actual. stop() elimina inversiones y conserva el layout final. El handle ofrece finished y stop(). La geometría proyectiva o singular no tiene inversa 2D admitida.
const size = motion.animateSize(
card,
{ width: 420, height: 'auto' },
() => {
card.textContent = nextText
},
{ duration: 400 },
)
size.pause()
size.play()
await size.finished
const layout = motion.animateLayout(
list,
() => {
list.append(list.firstElementChild!)
},
{ size: 'scale', enter: true, duration: 400 },
)
await layout.finishedEscenas con seek
seekableTimeline() coordina tracks explícitas de elementos y valores numéricos mutables, steps anidados, offsets en milisegundos y repeticiones finitas en un reloj. Usa un descriptor por canal/layer; combina movimientos repetidos en keyframes. Callbacks, gestos y efectos secundarios no son descriptores reversibles. Destinos y valores pertenecen al mismo documento/runtime.
Pause, resume, stop y progress conservan el contrato finito. Se puede retroceder desde un endpoint pausado. Reemplazar un canal o hacer set() del valor mantiene los demás; el handle anterior no recupera el canal y finalmente resuelve false. Observar valores numéricos usa el scheduler; las escenas elegibles solo de elementos mantienen WAAPI.
const progress = motion.motionValue(0)
const scene = motion.seekableTimeline(
[
{
target: card,
keyframes: { x: [0, 240], borderRadius: [8, 40] },
transition: { duration: 1200, ease: 'linear' },
},
{
at: 200,
steps: [{ value: progress, keyframes: [0, 100], transition: { duration: 1000 } }],
},
],
{ paused: true },
)
scene.progress = 1
scene.progress = 0.3
scene.play()
await scene.finishedLifecycle, coste e integración
Reduced motion en vivo finaliza tracks espaciales/decorativas y tamaño intrínseco, incluso pausado; color/opacidad conservan sus tiempos. Libera destinos y descarta el contexto con su dueño. useWebMotion() también ofrece animateSize() y animateLayout() sobre la ref declarada y limpia con el scope Vue.
Se mantiene la política Web de superficies: usa superficies dedicadas sin animaciones/transiciones CSS competidoras. La pintura finita usa keyframes WAAPI preparados, pero colores pueden repintar y bordes provocar layout. El tamaño real mide layout cada frame activo; ningún camino garantiza aceleración del compositor. Evita renders del framework por frame.
Ejecuta npm run web:visual:typecheck, npm run web:visual:test y npm run web:visual; la demo usa el puerto 4287 con ?demo=paint, size, layout o scene. El informe y los comandos incluyen Chromium/Firefox/WebKit reales y consumidores empaquetados imperativo/Vue/SSR/nativo.
La adopción Mavis se sigue en controles #191, Accordion #192, FLIP compartido #193 y escenas #194, consumiendo el motor Jolt.
Referencias nativas conservadas
Estas grabaciones revisadas en inglés muestran equivalentes nativos existentes. No muestran Web ni acreditan nuevo rendimiento físico.