Ruch
Krótki, celowy ruch, który wyjaśnia, skąd elementy się biorą i dokąd znikają. Czasy trwania i krzywe przejścia są tokenami, a sprężyny działają w czystym CSS.
Ruch w prfct ma jedno zadanie: wyjaśniać zmianę. Menu wyrasta z przycisku, który je otworzył; okno dialogowe wznosi się na swoje miejsce; gałka przełącznika sprężyście przeskakuje po torze. Nic nie rusza się tylko po to, żeby było widać, że się rusza.
Kompilacja zakończona
Pojawia się w 220 ms (ease-enter), znika w 100 ms (ease-exit).
"use client"
import * as React from "react"
import { BellIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
export default function MotionChoreography() {
const [open, setOpen] = React.useState(false)
return (
<div className="flex h-56 w-full max-w-sm flex-col items-center gap-4">
<Button variant="outline" onClick={() => setOpen((value) => !value)}>
<BellIcon data-icon="inline-start" />
{open ? "Ukryj" : "Pokaż"} powiadomienie
</Button>
<div
data-open={open || undefined}
className="w-full origin-top rounded-xl bg-popover p-4 shadow-floating ring-1 ring-edge transition-[opacity,scale,translate] duration-fast ease-exit not-data-open:pointer-events-none not-data-open:-translate-y-1 not-data-open:scale-[0.97] not-data-open:opacity-0 data-open:duration-moderate data-open:ease-enter"
>
<p className="text-sm font-semibold">Kompilacja zakończona</p>
<p className="text-sm text-muted-foreground">
Pojawia się w 220 ms (ease-enter), znika w 100 ms (ease-exit).
</p>
</div>
</div>
)
}Zasady
Natychmiastowy. Informacja zwrotna pojawia się w ciągu 100 ms od działania użytkownika. Stany najechania i naciśnięcia są niemal natychmiastowe; interfejs nigdy nie czeka z reakcją, aż skończy się animacja.
Kierunkowy. Elementy wchodzą ze swojego źródła — od strony wyzwalacza, od krawędzi ekranu — i wychodzą tą samą drogą. Wyskakujące elementy skalują się od --transform-origin, zmiennej, którą Base UI ustawia na punkt zakotwiczenia.
Asymetryczny. Elementy znikają szybciej, niż się pojawiają. Wyjścia mają krótsze czasy trwania i przyspieszające krzywe, więc zamykanie nigdy nie wydaje się ociężałe.
Opcjonalny. Ruch to ozdoba nałożona na działający interfejs. Po włączeniu ograniczenia ruchu wszystko nadal działa — tyle że natychmiast.
Czasy trwania
Krzywe przejścia
prfct ma cztery krzywe cubic-bézier i dwie sprężyny. Sprężyny są symulowane na podstawie parametrów fizycznych (sztywności i tłumienia) i zapisane jako funkcje CSS linear() — to prawdziwy ruch sprężyny w przejściach CSS, bez biblioteki animacji i bez JavaScriptu w głównym wątku.
Każda kropka pokonuje ten sam dystans w tym samym czasie; różni się tylko krzywa.
Domyślna dla elementów, które zmieniają się w miejscu.
Elementy wchodzące na ekran. Szybki start, długie wyhamowanie.
Elementy znikające. Przyspiesza przy wyjściu; łącz z krótszymi czasami.
Elementy przemieszczające się z miejsca na miejsce na ekranie.
Fizyczna, responsywna reakcja: przełączniki, znaczniki pól radio, puszczenie po przeciągnięciu.
Zabawne podkreślenie: potwierdzenia sukcesu, pojawiające się odznaki.
// A switch thumb that settles like a physical toggle
<span className="transition-transform duration-spring ease-spring" />Klasy Tailwinda ease-in, ease-out i ease-in-out są przypisane do krzywych ease-exit, ease-enter i ease-move, więc standardowa klasa też trzyma się tempa systemu.
Klasy choreografii
Komponenty współdzielą ruch dzięki trzem klasom narzędziowym, więc każda nakładka w systemie porusza się tak samo:
Sterują nimi atrybuty data-starting-style i data-ending-style z Base UI, więc animacje da się przerwać: zamknięcie menu w połowie otwierania płynnie odwraca ruch od miejsca, w którym akurat jest.
Ograniczony ruch
Gdy system operacyjny prosi o ograniczenie ruchu, prfct skraca każde przejście i każdą animację do zera. Połysk szkieletów ładowania i obrót spinnera się zatrzymują; stan nadal się zmienia, więc nic nie ginie. Ponieważ prfct nigdy nie przekazuje znaczenia ruchem, jest to zawsze bezpieczne.