Reference

Kompositions-API

Reference til film, tid, grundelementer til tidsstyring, animationsmatematik, scene og kamera, markør, tempo og videomateriale.

Alt her eksporteres fra @zxn/motion-core, medmindre andet er angivet.

En film

En film er en React-komponent plus de oplysninger, en vært skal bruge for at vise den: størrelse, billedfrekvens og antal billeder. defineFilm validerer oplysningerne og returnerer filmen; et indgangsmodul eksporterer et array af dem.

import { defineFilm, FullFrame, useTimeline } from "@zxn/motion-core";

type Props = { readonly title: string; readonly color: string };

function Intro({ title, color }: Props) {
  const { frame } = useTimeline();
  return <FullFrame style={{ background: color }}><h1>{title} — {frame}</h1></FullFrame>;
}

export const films = [defineFilm({
  id: "intro",
  title: "Intro",
  width: 1920,
  height: 1080,
  frameRate: 30,
  frames: 150,
  defaultProps: { title: "Hello", color: "#111827" },
  controls: {
    title: { type: "text", label: "Title" },
    color: { type: "color", label: "Background" },
  },
  component: Intro,
})];

Props skal være JSON: De gemmes i projektfiler, sendes på kommandolinjen og overføres mellem processer. controls beskriver dem for en vært, der vil vise et egenskabspanel (text, number, color, boolean, choice).

describeFilm(film) returnerer den FilmDescriptor, afspilleren og rendereren tager imod. FilmSurface monterer en film ved ét præcist billede; værter bruger den, film gør ikke.

Tid

Hook Returnerer
useTimeline() { frame, absoluteFrame, film }. frame er lokalt for den omsluttende <Cue>; absoluteFrame er filmens. film indeholder width, height, frameRate, frames.
useFilmConfig() Kun film-delen.
useVirtualTime() { frame, seconds, milliseconds } for det billede, der renderes. Brug det til at flytte afspilningspositionen i tidsbaserede biblioteker: timeline.seek(seconds), lottie.goToAndStop(...).
useTempo() Filmens musikalske ur; se Tempo.

En film må ikke aflæse et rigtigt ur. Lad billednummeret styre alt, så renderes filmen ens i afspilleren, når du trækker afspilningsmarkøren, og ved eksport.

Grundelementer til tidsstyring

  • <FullFrame> fylder filmen. Den er en flex-container, så alignItems og justifyContent centrerer indholdet.
  • <Cue start length> viser sine børn i length billeder fra start og genstarter frame ved nul indeni, så en komponent animerer ens, uanset hvor den placeres. Når et tempo er angivet, tager den også startBar/startBeat og lengthBars/lengthBeats.
  • <Hold frame> fryser sine børn ved ét billede.
  • <Repeat every> gentager sine børns lokale tid.

Animationsmatematik

mapRange(frame, [0, 30], [0, 1], { clamp: true, ease: easing.easeOut });
springValue({ frame, frameRate: film.frameRate, stiffness: 170, damping: 26, clamp: true });
seededRandom("stream-7");          // the same number every time, for this seed

mapRange tager tilsvarende lister med to eller flere støttepunkter, så ét kald kan beskrive indgang, pause og udgang. springValue bruger en lukket formel: Den beregner fjederens position ved et billede i stedet for at føre en simulering frem trinvis. Derfor svarer det at trække afspilningsmarkøren ind midt i en fjederbevægelse til en sekventiel rendering. easing indeholder linear, easeIn, easeOut og easeInOut.

Scene, kamera og markør

En film, der viser en brugergrænseflade, har brug for et sted at placere den og noget at se på den med. <Stage> er en flade med fast størrelse og sit eget koordinatsystem; alt på den placeres af én layout-tabel, og det er den samme tabel, kameraet indrammer efter, og markøren bevæger sig mellem punkterne i.

const layout = { panel: { x: 400, y: 300, width: 600, height: 400 }, "export-button": { x: 1716, y: 112, width: 92, height: 32 } };

<Stage width={1920} height={1080} layout={layout} shots={shots} contain vignette>
  <StageItem id="panel">…</StageItem>
  <StageItem id="sky" depth={0.6} />
  <Pointer path={path} />
</Stage>

Intet måler DOM'en. Layout er ikke tilgængeligt før filmens første optegning, så et kamera, der målte, ville indramme det forkerte på billede nul i hver eksport. Ved at deklarere et rektangel én gang bliver »kameraet ser på det, markøren lander på det, og det reagerer« én samlet sandhed i stedet for tre, der skal holdes i takt manuelt.

<Camera shots> tager en liste over indstillinger: { at, look, zoom, tilt?, settle?, hold?, padding? }. look er et punkt på fladen eller id'et på noget, scenen har placeret; et navngivet element indrammes ud fra sine grænser plus padding, så ingen skal indtaste pixelkoordinater. settle er "spring" (standard), "ease" eller "cut"; zoom interpoleres multiplikativt, så en bevægelse fra 1×→4× bruger lige lang tid på 1–2 og 2–4. contain forhindrer en indstilling i at vise noget uden for fladens kant. idle tilføjer en drift med fast startværdi, så en fastholdt indstilling ikke er helt livløs.

Én bevidst bevægelse pr. indstilling, og hold så stille. Et kamera, der ankommer og allerede er på vej videre, virker nervøst. reviewCameraShots() kontrollerer dette. Det er en advarsel, ikke en fejl: En bevidst hurtig panorering mellem to slag kan sagtens være tilsigtet.

Parallakse er depth-proppen på <StageItem>: 0 bevæger sig med fladen, mens 1 er så langt væk, at kameraets bevægelse slet ikke flytter det. useCamera() returnerer den aktuelle visning til alt, der skal reagere på den.

<Pointer path> tegner en markør i fladens koordinatsystem, så kameraet tager den med. Nøglepunkter er { at, to, press?, drag? }, hvor to er et punkt eller et element-id. Bevægelsen accelererer og bremser blødt og buer lidt væk fra den rette linje, fordi en markør, der glider med konstant fart, virker som et diagram. reviewPointerPath() advarer om bevægelser, der er for hurtige til at virke håndstyrede.

usePointer(path).pressedOn("export-button") er sand på præcis de billeder, hvor markøren trykker på den, så en komponent tegner sin egen trykkede tilstand ud fra samme sti, der bevæger markøren. Intet skal tilpasses manuelt.

Tempo

En film kan deklarere tempo: { bpm, beatsPerBar = 4, offsetSeconds = 0 }. useTempo() giver derefter bar(n), beat(n), barAt(frame), beatAt(frame) og onBeat(frame, division?).

Alle konverteringer går gennem én funktion, så et sceneskift og et blink på samme nedslag lander på samme billede i stedet for med ét billedes mellemrum. Hvert slag beregnes ud fra sit eget indeks i stedet for ved at lægge et slags antal billeder til det foregående: Ved akkumulering afrundes én gang pr. slag, og fejlen vokser, så afvigelsen ved takt 200 i en film på 29,97 fps er hele billeder. onBeat er sand på præcis ét billede pr. underdeling, så et blink varer ét billede i stedet for at flimre over to.

Videomateriale

<Footage asset="ASSET_ID" from? speed? duration? fit? muted /> viser en video inde i filmen, så den kan ligge i et skråtstillet kort, maskeres eller få tekst lagt ovenpå.

Det er et canvas, ikke et <video>: Et videoelement søger efter sin egen tidsplan, og »tæt nok på« er ikke et billede. <Footage> deklarerer, hvilken mediefil og hvilket tidspunkt den ønsker, og værten udfylder hvert canvas via en billedleverandør, der er installeret som globalThis.zxnFootage, og venter på dem alle før optagelse. En manglende udbyder eller et billede, udbyderen ikke kan levere, giver en synlig fejl, der navngiver mediefilen.

Kommandolinjerendereren installerer en filbaseret udbyder: zxn-motion render … --footage <id>=<file>. Andre værter leverer deres egen. from og speed omsætter filmens billede til kildens, og sidste billede fastholdes i stedet for at fortsætte forbi slutningen. Lyd ligger uden for rammen: En film er et billede.

Hold filmen hurtig

Rendereren rasteriserer DOM'en én gang pr. billede, så en film koster det, dens DOM koster at lægge ud og tegne.

  • Foretræk få store elementer frem for tusindvis af små. Et partikelfelt eller en tegnregn med ét element pr. partikel er langsomt ved enhver opløsning; tegn det i stedet på ét <canvas> ud fra billednummeret.
  • Sløring er dyrt at tegne: filter: blur(), backdrop-filter og store radier for text-shadow/box-shadow, ganget med hvert element, der har dem.
  • Arbejd i 1920×1080, medmindre du behøver mere. En vært kan skalere en film; fire gange så mange pixels giver fire gange så meget tegnearbejde.
  • Beregn ud fra billednummeret; akkumuler ikke tilstand på tværs af billeder. Billeder kan blive efterspurgt i vilkårlig rækkefølge.