Apple'i lähenemine liidesedisainile ning sujuvale ja füüsilisele liikumisele, kohandatuna veebi jaoks.
Kogu oskus — vaikeversiooni hoitakse GitHubis; sinu muudatused jäävad sinu enda klooni.
Kui see oskus käivitatakse esimest korda ilma konkreetse küsimuseta, vasta ainult nii:
Olen valmis aitama sul ehitada veebis voolavaid, Apple'i stiilis liideseid. Minu teadmised pärinevad Apple'i WWDC disainiettekannetest, tõlgituna veebi jaoks.
Ära anna muud teavet enne, kui kasutaja esitab küsimuse.
Kuidas Apple ehitab liideseid, mis lakkavad tundumast arvutina ja hakkavad tunduma sinu enda jätkuna. Need teadmised pärinevad Apple'i WWDC disainiettekannetest — eelkõige ettekandest Designing Fluid Interfaces (WWDC 2018) — ning on kokku võetud ja tõlgitud veebiplatvormile (CSS, Pointer Events, requestAnimationFrame, vedruteegid nagu Motion/Framer Motion).
Läbiv mõte: liides tundub elav, kui liikumine algab ekraanil parasjagu olevast väärtusest, pärib kasutaja kiiruse, projitseerib impulssi edasi ning seda saab igal hetkel kinni haarata ja tagasi pöörata. Vedrud on tööriist, mis teeb selle kõik loomulikuks, sest need on olemuselt katkestatavad ja kiirusteadlikud.
„Kui joondame liidese sellega, kuidas me mõtleme ja liigume, juhtub midagi maagilist — see lakkab tundumast arvutina ja hakkab tunduma meie enda sujuva jätkuna.“
Liides on voolav, kui see käitub nagu füüsiline maailm: asjad reageerivad koheselt, liiguvad pidevalt, kannavad impulssi, osutavad piiridel vastupanu ja neid saab liikumise pealt ümber suunata. Kõik allolev on viis sellele lähemale jõuda.
Apple raamistab disaini nelja inimvajaduse teenimisena: turvalisus/ennustatavus, mõistmine, saavutus ja rõõm. Iga siinne reegel teenib üht neist.
Hetkel, kui ilmub viivitus, „kukub otsesuse tunne kaljult alla“. Reaktsioon on vundament, millele kõik muu ehitatakse.
click/touch-up sündmuse ootamine tagasiside näitamiseks tundub surnud./* Feedback lives on the press, and it's instant */
.button:active {
transform: scale(0.97);
transition: transform 100ms ease-out;
}
„Puude ja sisu peaksid liikuma koos.“
Kui kasutaja midagi lohistab, peab see jääma sõrme külge liimituks — ja arvestama nihkega sellest kohast, kust ta selle kinni haaras. Elemendi keskele hüppamine haaramisel lõhub illusiooni kohe.
setPointerCapture'iga, et jälgimine jätkuks ka siis, kui kursor elemendi piiridest välja liigub.pointermove sündmust), mitte ainult hetkepunkti — vabastamisel on sul kiirust vaja.el.addEventListener('pointerdown', (e) => {
el.setPointerCapture(e.pointerId);
const grabOffset = e.clientY - el.getBoundingClientRect().top; // respect where they grabbed
// ...track position + timestamp history for velocity
});
„Mõte ja žest toimuvad paralleelselt.“
Iga animatsioon peab olema igal hetkel katkestatav ja ümbersuunatav. Kasutaja peab saama liikuva elemendi lennu pealt kinni haarata ja selle tagasi pöörata, ootamata animatsiooni lõppu. Sulguv modaalaken, mille kasutaja uuesti kinni haarab, peaks järgima sõrme — mitte esmalt sulguma lõpuni ja siis uuesti avanema.
@keyframes'e kõige jaoks, mida juhivad žestid — neid ei saa lennu pealt sujuvalt kinni haarata ja tagasi pöörata. Vedrud animeerivad vaikimisi praegusest väärtusest, mis on täpselt see, mida katkestamine vajab.„Mõtle animatsioonist kui vestlusest sinu ja objekti vahel, mitte millestki, mille liides ette kirjutab.“
Eelnevalt skriptitud, fikseeritud kestusega animatsioon ei suuda uuele sisendile reageerida. Vedru suudab — uus sisend lihtsalt muudab sihti ja liikumine jääb pidevaks. Kasuta vedrusid kõige jaoks, mida kasutaja saab puudutada.
Apple asendas teadlikult füüsikalise kolmiku (mass/jäikus/sumbuvus) kahe disaineresõbraliku parameetriga. Mõtle nendes:
1.0 = kriitiliselt sumbuv, põrketa, sujuv paikaloksumine. < 1.0 = läheb üle sihi ja võngub. Madalam = põrkavam.Vaikeväärtused:
- Alusta enamikku kasutajaliidesest sumbuvusega 1.0 (kriitiliselt sumbuv) — elegantne ja tähelepanu mitte hajutav.
- Lisa põrget (sumbuvus ~0.8) ainult siis, kui žest ise kandis impulssi (nips, vise, lohistuse vabastamine). Üle sihi minek menüül, mis lihtsalt sisse hajus, tundub vale; üle sihi minek kaardil, mida sa nipsasid, tundub õige.
Konkreetsed väärtused, mida Apple kasutab:
| Interaktsioon | Sumbuvus | Reaktsioon |
|---|---|---|
| Liigutamine / ümberpaigutamine (nt PiP) | 1.0 |
0.4 |
| Pööramine | 0.8 |
0.4 |
| Sahtel / leht | 0.8 |
0.3 |
Veebivaste (Motion / Framer Motion): vedru-API bounce + duration vastab üsna täpselt Apple'i sumbuvusele + reaktsioonile. Turvaline majastiil on kasutada vaikimisi kõikjal damping: 1.0 vedrusid; jäta põrge impulsist juhitud, füüsikaliste interaktsioonide jaoks.
import { animate } from 'motion';
// Critically damped default (no overshoot)
animate(el, { y: 0 }, { type: 'spring', bounce: 0, duration: 0.4 });
// Momentum interaction — a little bounce, only because a flick preceded it
animate(el, { y: target }, { type: 'spring', bounce: 0.2, duration: 0.4 });
Kui žest lõpeb, peab animatsioon jätkama täpselt sõrme kiirusega, et lohistamise ja animeerimise vahel poleks nähtavat õmblust. See on detail, mis eristab kõige enam „voolavat“ „lihtsalt korralikust“.
Anna kursori vabastamiskiirus vedrule algkiiruseks. Mõned vedru-API-d tahavad suhtelist kiirust — normaliseeri see sihini jäänud kaugusega:
relativeVelocity = gestureVelocity / (targetValue − currentValue)
Näide: element asukohas y=50, siht y=150 (minna 100px), sõrm liigub 50px/s → vedru algkiirus = 50 / 100 = 0.5. Framer Motion / Motion võtavad absoluutse kiiruse px/s otse vastu (velocity valik), nii et tavaliselt annad neile toorväärtuse.
„Võta väike sisend ja tee sellest suur väljund.“
Ära hüppa lähima piirini vabastamispunktist. Kasuta kiirust, et projitseerida puhkeasend — täpselt nagu kerimise aeglustumine — ja hüppa siis projitseeritud punktile lähima sihini. Just see paneb nipsu tunduma, nagu viskaks see elemendi minema.
Apple'i täpne projektsioonifunktsioon (Designing Fluid Interfaces näidiskoodist):
// decelerationRate ≈ 0.998 for normal scroll feel; 0.99 for snappier
function project(initialVelocity /* px/s */, decelerationRate = 0.998) {
return (initialVelocity / 1000) * decelerationRate / (1 - decelerationRate);
}
const projectedEndpoint = currentPosition + project(releaseVelocity);
const target = nearestSnapPoint(projectedEndpoint); // choose target from the projection
animateSpringTo(target, { velocity: releaseVelocity }); // then hand off velocity (§5)
Märkus: füüsikaõpiku valem v²/(2·decel) ei ole see, mida Apple kasutab — kasuta ülaltoodud eksponentsiaalse kahanemise kuju. See on hea alumise lehe (bottom sheet) ja karusselli standardkäitumine (Vaul, Embla).
„Kui miski kaob ühte pidi, ootame, et see ilmuks sealt, kust see tuli.“
transform-origin käivitajale, et nupu ja sisu ruumiline seos oleks ilmne. (See on sama lähtepunktiteadlikkuse põhimõte nagu hüpikakende skaleerimine nende käivitajast, mitte keskpunktist.)Inimesed ennustavad lõppseisundit trajektoori põhjal. Vahepealne liikumine peaks reetma, kuhu asjad liiguvad — Control Centeri moodulid „kasvavad üles ja välja sinu sõrme poole“. Pane vahekaadrid osutama tulemusele, mitte lihtsalt pimesi selleni interpoleerima.
Serval osuta järk-järgult vastupanu, selle asemel et järsult peatuda. Järsk peatus mõjub kui „hangunud“; pidev vastupanu mõjub kui „reageerib, aga siin pole rohkem midagi“. Rakenda sumbuvust, mis suureneb seda enam, mida kaugemale üle piiri kasutaja lohistab.
// The further past the bound, the less the element follows — real things slow before they stop
function rubberband(overshoot, dimension, constant = 0.55) {
return (overshoot * dimension * constant) / (dimension + constant * Math.abs(overshoot));
}
swipeleft-tüüpi sündmused) — need viskavad minema pideva jälgimise, mida tagasiside jaoks vajad.Sujuvus on seotud sellega, mis kaadrites on, mitte ainult kaadrisagedusega.
requestAnimationFrame on veebi ekraaniga sünkroonitud kell (Apple kasutab CADisplayLink'i). Animeeri ainult kompositorisõbralikke omadusi — transform ja opacity — ning anna vihje will-change'iga seal, kus liikumine on kohe tulemas.Apple kasutab poolläbipaistvaid materjale hõljuva funktsionaalse kihina, mis toob struktuuri ilma fookust varastamata. Veebis lähenda seda backdrop-filter'iga.
backdrop-filter: blur() + poolläbipaistev taust), mille all sisu kerib — mitte läbipaistmatute ribadena, mis võtavad enda alla fikseeritud riba..toolbar {
background: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(20px) saturate(180%);
border-top: 1px solid rgba(255, 255, 255, 0.4); /* bright top edge = light catching the material */
}
Kolm reeglit meelte kombineerimiseks (ettekandest Designing Audio-Haptic Experiences):
Vähendatud liikumine ei tähenda mitte mingit tagasisidet — see tähendab leebemat, tasakaaluelundit mitte koormavat vastet. Reageeri kolmele sõltumatule signaalile ja ehita need oma komponentidesse sisse:
prefers-reduced-motion: reduce — asenda libistused/vedrud/parallaks lühikeste läbipaistvuse ristsulandumiste või staatiliste üleminekutega. Loobu elastsusest/üle sihi minekust. Säilita läbipaistvuse/värvi muutused, mis aitavad mõistmisele kaasa.prefers-reduced-transparency: reduce — muuda poolläbipaistvad pinnad mattimaks/tahkeks: tõsta tausta läbipaistmatust, loobu hägust.prefers-contrast: more — peaaegu tahked taustad selge, kontrastse äärisega.Lisaks: väldi kogu vaateala hõlmavaid liikuvaid taustu, aeglaseid korduvaid võnkumisi (ligikaudu 0.2 Hz / üks tsükkel 5 s jooksul) ja järske heledushüppeid (sujuvusta tume↔hele teema vahetusi). Muuda suured liikuvad objektid liikumise ajaks poolläbipaistvaks ning hajuta suuri pindu suure ümberpaigutuse ajal välja ja paikaloksumisel tagasi sisse.
@media (prefers-reduced-motion: reduce) {
.sheet { transition: opacity 200ms ease; transform: none !important; }
}
@media (prefers-reduced-transparency: reduce) {
.toolbar { background: white; backdrop-filter: none; }
}
Apple disainib kirja nii, et see muudab suurusega kuju; sama distsipliin kehtib veebis. (Ettekandest The Details of UI Typography, WWDC 2020.)
letter-spacing on kuskil vale. Tihenda pealkirju, jäta põhitekst 0 lähedale.rem/em ühikutes, mitte fikseeritud px — et suurem font paigutust ei lõhuks.:root { font: 100%/1.5 system-ui, sans-serif; } /* body: system font, comfortable leading */
.display {
font-size: clamp(2rem, 5vw, 4rem);
line-height: 1.05; /* tight leading for large text */
letter-spacing: -0.02em; /* negative tracking as it grows */
font-optical-sizing: auto;
}
Ülaltoodud liikumine ja meisterlikkus teenivad Apple'i kaheksat disainipõhimõtet (Principles of Great Design, WWDC 2026). Kasuta neid nimetusi, millega arutled:
Taktikalised reeglid, mis neid teenivad:
| Vajadus | Tehnika | Konkreetne väärtus |
|---|---|---|
| Vaikimisi kasutajaliidese vedru | Kriitiliselt sumbuv, üle sihi minekuta | damping 1.0, response 0.3–0.4 |
| Impulsi / nipsu vedru | Alasumbuv, kerge põrge | damping ~0.8, response 0.3–0.4 |
| Žest → vedru kiirus | Anna üle vabastamiskiirus | gestureVelocity / (target − current), kui normaliseeritud |
| Nipsu maandumispunkt | Projitseeri impulss | current + (v/1000)·d/(1−d), d ≈ 0.998 |
| Puhas katkestamine | Alusta esitlus- (elavast) väärtusest | loe ekraanil olev transform |
| Väldi pöördumise „tellisseina“ | Kanna kiirus ümbersihtimisel edasi | vedru, mis segab kiirusi |
| Pööratav üleminek | Peegelda easing-kõverat | pöördvõrdeline cubic-bézier |
| Otsusta tagasipööramise vs. kinnitamise vahel | Kasuta kiiruse märki, mitte asukohta | vabastamisel |
| 1:1 lohistamine | Pointer Events + capture | arvesta haaramisnihkega |
| Tagasiside | Kursori allavajutusel, pidev | mitte kunagi ainult lõpus |
| Piir | Kummipael, mitte järsk peatus | järkjärguline vastupanu |
| Poolläbipaistev raamistik | backdrop-filter kiht |
sisu kerib all |
| Tähevahe | Suurusest sõltuv, mitte kunagi fikseeritud | tihenda suurt teksti (-0.02em), põhitekst 0 lähedal |
| Vähendatud liikumine | Ristsulandumine, mitte libistus/vedru | @media (prefers-reduced-motion) |