Hareket ve etkileşim
Bir şekil, mekanizması gerçeği gibi hareket ettiğinde canlıdır: bir kızak kayar ve oturur, bir yay bir kez hedefi aşar, bir ibre yeni değere salınır. Hareket izlenebilecek kadar sakin ve modele sadık olmalıdır.
İlkeler
- Zamana dayalı.
dt'yirequestAnimationFramezaman damgalarından alın ve arka plandaki bir sekme sıçramasın diye 1/30 s ile sınırlayın. Asla kare başına sabit bir miktar ilerletmeyin. - Hedeflere doğru yumuşatın. İşaretçi ve klavye bir hedef belirler; parça onu üstel yumuşatmayla (
x += (target − x)·(1 − e^(−dt/τ)), τ = 0.1–0.3 s) ya da bir yayla izler. Parçalar asla imlece yapışmaz. - Konu esnekse gerçek yaylar. Gerçek modelden gelen sertlik ve sönümü kullanın; kararlılık için saniyede yaklaşık 240 alt adımla. Hem hata hem hız 5e-4'ün altına düşünce hedefe oturtun.
- Şekil başına tek döngü. Tek bir
tick(dt)modeli günceller, sonra DOM'u ref'ler üzerinden yazar:setAttribute("transform" | "d"),textContent,dataset. React'i her karede yeniden çizdirmeyin. - Oturunca ya da ekran dışındayken durun. rootMargin'i
120px 0pxolan birIntersectionObserverkullanın ve en yeni girdiyi okuyun (entries[entries.length - 1]): sahne iki geri çağrı arasında çıkıp geri gelirse tek bir geri çağrı iki girdiyi de en eskiden başlayarak getirir ve ilkini okumak şekli ekranda donmuş bırakır. Her şey hedefine ulaşınca döngüyü durdurun. - Azaltılmış hareket.
prefers-reduced-motion: reduceayarlıysa tur, akış ya da nabız olmadan doğrudan hedeflere atlayın. Şekil yine de girdiye tepki vermelidir. - Boşta tur. Kimse etkileşimde değilken, yaklaşık 1.4 s sonra şeklin amacını gösteren senaryolu bir döngü oynatın. Üzerine gelme, odak ya da basışta durdurun ve son etkileşimden yaklaşık 3.6 s sonra sürdürün. Tur, kullanıcının süreceği hedeflerin aynısını sürer.
- Okuma değeri canlı güncellenir; aynı modelden, gerçek birimlerle ve kısa:
blok 2 · okuma 14.0 px,120.0 × 32.0 · eğim 10.6 px. Onu hedeften değil, parçaların bulunduğu yerden okuyun: Raptor herhangi bir yay hareket ederken ya da beklerkensökülüyorya datoplanıyorder, ancak ondan sonrasökülmüş · 11 grupya da1.630 kg · soğuk. Bir evre sözcüğünü ve yanındaki sayıyı aynı kaynağın aynı anından alın: kör testli bir tezgâh, tahliye valfi 92'de açılmasına rağmenstrok sonu · tahliye 85 bar(sözcük modelden, sayı ondan 0.25 s geride kalan bir manometre ibresinden) ve geri çektikten hemen sonraboşta · 44 baryazdı.
İşaretçi
- İşaretçiyi bir eksenin ekran vektörüyle dünya uzayına eşleyin. x boyunca hareket için
axisVector(P, [1, 0, 0])kullanın ya da şeklin genişlik oranını parçanın hareket aralığına eşleyin. Aralık eşlemesi daha basittir ve hareket kısa olduğunda daha iyi hissettirir. - Üzerine gelme önizler, basış onaylar. Dokunmatikte üzerine gelme yoktur; bu yüzden dokunmayı eyleme bağlayın ve sayfa yine kaydırılabilsin diye
touch-action: pan-ytutun. - Sürüklerken işaretçi yakalamayı (pointer capture) kullanın ve
pointerupya dapointercancel'da bırakın.
Klavye ve erişilebilirlik
- Şeklin kökü odaklanabilirdir (
tabIndex=0);role="slider"(ya da ayrık seçenekler varsa düğmelerle birlikte"group"), kontrolleri açıklayan biraria-labelve "Basılı: 129.6 × 34.6 piksel, eğim 10.6 px" gibi sözcüklerlearia-valuenow/aria-valuetexttaşır. Değer metnini döngüden değil, okuyucunun seçimi değiştiğinde yazın (bir parça seçildiğinde, sökülmüş ya da monte edilmiş, ateşleniyor ya da soğuk): ekran okuyucular odaktaki bir kaydırıcının değer metnindeki her değişikliği okur; bu yüzden her karede tıklayan bir okuma ("Pc 290 bar · 222 tf") bir sayı yağmuruna dönüştü. Görsel okumayı ve erişilebilir değeri ayrı tutun (Raptor'unsay()'i yalnızca okuma değerini,describe()değer metnini yazar); evreleri canlı bölge taşır. - Tuşlar: oklar mantıklı bir birimle adım atar (daha büyük adımlar için Shift), Home ve End uç noktalara gider, Boşluk ya da Enter basar ya da değiştirir, Escape bırakır.
- Bir odak halkası gösterin:
outline: 1px solid var(--anatomy-muted); outline-offset: 6px. <svg>,role="img"ve nesneyi ve neyi gösterdiğini anlatan biraria-labeltaşır.
Düz JavaScript döngüsü (çerçevesiz)
let frame = 0, last = 0, visible = false, touring = !still.matches, clock = 0;
function tick(now) {
const dt = last ? Math.min((now - last) / 1000, 1 / 30) : 1 / 60;
last = now;
const calm = still.matches;
if (touring) { clock += dt; target = tourAt(clock); }
x = calm ? target : x + (target - x) * (1 - Math.exp(-dt / 0.28));
springTo(tip, heightUnder(x), dt, 420, 26, calm);
draw(); // ref'lerde setAttribute, readout.textContent = …
const settled = !touring && x === target && tip.rate === 0;
frame = visible && !settled ? requestAnimationFrame(tick) : 0;
}
const run = () => { if (!frame && visible) { last = 0; frame = requestAnimationFrame(tick); } };
new IntersectionObserver((entries) => { visible = entries[entries.length - 1].isIntersecting; if (visible) run(); }, { rootMargin: "120px 0px" }).observe(stage);
examples/dial-indicator/build.mjs, tur, işaretçi eşlemesi ve klavye duraklarıyla eksiksiz bir sürüm içerir.
React döngüsü
Statik sanatı sunucuda kurulan prop'larda tutun ve yalnızca <g> sarmalayıcılarını ve şekil değiştiren yolları hareket ettirin:
"use client";
export function RigLive(props: { base: React.ReactNode; carriage: React.ReactNode; dial: React.ReactNode }) {
const { anchor, say } = useReadout<HTMLDivElement>();
const car = React.useRef<SVGGElement>(null);
const needle = React.useRef<SVGPathElement>(null);
const state = React.useRef({ x: REST, target: REST, tip: { at: 0, rate: 0 } });
const visible = useInView(anchor);
const kick = useLoop((dt, calm) => {
const s = state.current;
s.x = follow(s.x, s.target, dt, 0.28, calm);
const settled = spring(s.tip, heightUnder(s.x), dt, 420, 26, calm);
car.current?.setAttribute("transform", translateAlong(P, [s.x, 0, 0]));
needle.current?.setAttribute("d", needleAt(s.tip.at));
say(readoutOf(s));
return !(settled && s.x === s.target);
}, visible);
return (
<div ref={anchor} tabIndex={0} role="slider" onPointerMove={(e) => { state.current.target = toWorld(e); kick(); }}>
<IsoFigure width={W} height={H} label={LABEL}>
{props.base}
<g ref={car}>{props.carriage}</g>
{props.dial}
<path ref={needle} className="iso-line" data-tone="lit" d={NEEDLE_REST} />
</IsoFigure>
</div>
);
}
examples/test-rig/live.tsx yaylar, tur, basma ve çekme, klavye ve aria ile tam üretim sürümüdür.
Değişen şekiller
Bir parça konumu yerine şeklini değiştirdiğinde (şişen bir kapsül, sıkışan bir yay, dönen bir ibre), her karede yalnızca onun yollarını kitle yeniden kurun (slabOf, coil ya da lineOnTop) ve yollarına yazın: fill, shades[0..3], top, crease, bevel, outline. Bir katının maliyeti 0.1 ms'nin oldukça altındadır. Ölçün: bir kare yaklaşık 1 ms betik süresinin altında kalmalıdır.
- Önceden hesaplanmış kareler kademelidir. ±15° üzerinde önceden kurulmuş 41 kareden çizilen bir kol, topuzunu kare başına yaklaşık 0.8 px hareket ettirdi; ağır çekimde görünürdü. Dönen bir parçayı her karede yeniden kurun ya da bir adım 0.25 px'ten fazlasını oynatmayacak kadar çok kare önceden hesaplayın.
- Bir salmastradan kayarak çıkan bir çubuk şekil değiştirmez: hareket eden tek bir tam boy çubuktur; salmastra yüzünde yerinde duran bir kırpmayla kesilir (
G.rodOut,holdStill,craft.md, bölüm 3). Onu asla üst üste binen birkaç kopya olarak çizmeyin.
Tek seferlik olaylar için zarflar
Bir parlama, bir tık, bir patlama, bir sarsıntı ya da bir şok, ayarladığınız bir değer değil, yaşı olan bir olaydır. Onu yükselişi ve sönümü olan bir zarfla sürün. kit/gl.mjs, smooth(a, b, x) (smoothstep) ve burst(age, rise, fall)'u dışa aktarır; ikincisi smooth(0, rise, age) ile tepe noktasından sonra exp(−(age − rise)/fall)'un çarpımıdır ve olaydan önce 0'dır:
import { smooth, burst, settle } from "../../kit/gl.mjs";
const flash = calm ? 0 : burst(clock - mainAt, 0.08, 0.13);
Bağımsız bir sayfada glScript() üçünü de zaten içine gömer; bu yüzden onları doğrudan kullanın ve kendinizinkini tanımlamayın: aynı modül betiğindeki ikinci bir const smooth bir SyntaxError'dur.
- Yükseliş 40–120 ms'dir ve smoothstep başlangıçta ona sıfır eğim verir. Bir değeri tek karede 1 yapmak bir patlamadır (pop).
- Olayın başlangıç zamanını saklayın (
mainAt = clock) ve her zarfı her karede onun yaşından hesaplayın. Örtüşen iki olay böylece basitçe toplanır. - Her olaya kendi bayrağını verin (
popped); böylece hızlı bir yeniden başlatma bir sonrakini yutamaz ve hâlâ sönmekte olanı kesemez. - Azaltılmış harekette her olay zarfı 0'dır.
Kritik sönümlü oturma
Hedefi aşmadan varması gereken bir değer için (bir renk tonu, bir parlaklık, bir lamba, bir erim, bir frekans) kitin settle(state, target, dt, seconds, calm)'ini kullanın: state = { x, v } üzerinde zaman sabiti seconds olan kritik sönümlü bir yay, w = 1/seconds ile v += (w²·(target − x) − 2w·v)·dt, dört alt adımda. Üstel izlemeden daha pürüzsüzdür, çünkü hız da süreklidir. Hata ve hız 1e-4'ün altına inince hedefe oturur, calm altında doğrudan oraya atlar ve hâlâ hareket ederken true döndürür; böylece döngüyü canlı tutabilir:
const moving = settle(freq, state.tf, dt, 0.625, calm);
Dalga leğeni frekansını (0.625 s) ve daldırıcı aralığını (0.4 s) settle'dan geçirir; böylece bir tuş basışı ya da işaretçi hareketi dalga boyunu asla sıçratmaz.
- Bir evrenin değiştirdiklerini ayırın. Tek bir fiziksel nicelik (örneğin yanma odası basıncı) rengi, şekli ve parlaklığı sürdüğünde, o nicelikteki hızlı bir yay üçünü birden sıçratır. Renge kendi daha yavaş
settle'ını verin (0.3–1 s) ve parlaklığı değişim boyunca tek yönde hareket edecek şekilde hesaplayın. - Bir sönme yalnızca koyulaşır. Bir duruş başladığında parlaklığı o anki düzeyinde dondurun ve yalnızca azalan niceliklerle ölçekleyin.
- Bir yayı değiştirmek komşularını da değiştirir. Bir yayı kritik sönümlemek, onun eski aşmasına dayanan her evreyi yavaşlatır; bu yüzden hepsini yeniden kontrol edin.
Patlatılmış görünümler
Bir makineyi üzerine gelince sökmek, parçalarını göstermenin güçlü bir yoludur (examples/raptor-engine/):
- Her parçayı monte edilmiş konumunda kurun, bir montaj grubu adıyla etiketleyin ve her gruba bir dünya hareket vektörü verin (
PIECES.nozzle.move = [0, 0, -120]). Hareketler gerçek montajı izler: lüle en uzağa iner, yan birimler radyal olarak dışa kayar (out(deg, r, z)) ve sehpa hareket etmez. - Grup başına bir yay; monte için 0, sökülmüş için 1 değeriyle. Raptor sertlik 64 ve sönüm 14.2 kullanır, kritiğin hemen altında; böylece parçalar hafif bir ağırlık hissiyle oturur ve kare başına 4 alt adım atılır.
- Yayları kademelendirin. Açılırken her grubun hedefi bir öncekinden 35 ms sonra değişir; hiçbir şeyin hiçbir şeyin içinden geçmesine izin vermeyen bir sırayla (Raptor:
nozzle, downcomer, controller, plumbing, lox, ftp, hotgas, chamber, orpb, otp, gimbal, stand; aşağıdaki "Kademe sırasını belirleyin"e bakın). Kapanış ters sırayı kullanır; böylece makine içten dışa doğru yeniden toplanır. Bekleyen hedefleri{ value, at }olarak tutun veclock >= atolduğunda uygulayın. - Bir geri dönüşte yalnızca değişeni yeniden planlayın. Okuyucu yarı yolda geri döndüğünde (hızlı bir üzerine gelme, Boşluk, bir dokunuş), henüz çıkmamış her grubun bekleyen hedefini iptal edin ve yalnızca hedefi değişenleri, o daha kısa listedeki sıralarına göre kademelendirin. Son çıkan böylece hemen ilk geri dönen olur. Tüm listeyi yeniden planlamak, erken grupları 0.3 s boyunca sökülmüş konuma doğru uçar halde, sonrakileri ise evde bıraktı; patlatma ancak bir sonraki grup 35 ms sonra yoldan çekildiği için çalışıyordu: o aralıkta bir geri dönüş sıcak gaz kanalını henüz ayrılmamış bir metan pompasına sürdü. Bu kuralla kademe boyunca paylar her karede sıralı kalır (erken gruplar hep önde gider); aşağıdaki önek kuralının dayandığı da budur.
- Dönüşümlerle hareket ettirin. Her öğenin ekran ötelemesi, izdüşürülmüş ve yay değeriyle çarpılmış hareket vektörüdür:
translate(tx ty). 0'da özniteliği kaldırın; böylece monte edilmiş bir şekil tam olarak statik sanattır. - Çizgilerin bir yere inmesini koruyun. İki grubu birleştiren parçalar (çerçeve ile bir pompa kulağı arasındaki bir eyleyici ya da bir kutu ile bir fiş arasındaki bir kablo) fiziksel olarak birlikte kaldıkları gruba aittir ve sökülmüşken de bir şeyde biter. Kabloların iki ucunda da uç noktaları olur.
- Seçim. Üzerine gelme, klavye okları ve bir parça listesi hepsi bir grubu seçer. Seçilen parçalar tek
litvurgusu olur:.it[data-on]üzerindeki CSShi'ılit'e vemid'ihi'a taşır, üstleri ve yanları aydınlatılmış dolgulara geçirir. Diğer her parça[data-focus]üzerindeki CSS ile bir ton aşağı iner (hi'danmid'e,mid'denlo'ya) ve okuma değeri seçilen parçanın ne yaptığını söyler.
Hareket eden parçaların derinlik yeniden sıralaması
İzleyiciye doğru ya da ondan uzağa hareket eden parçalar hareket ettikçe ressamın sırasını değiştirir. Değiştirdiklerinde her karede sıralayın:
- Her öğeye statik bir anahtar verin:
k = dot3(ref, viewOf(P)) + bias; buradarefmerkezidir vebiasbirbirine değen parçalar arasındaki eşitlikleri çözer. Ona bir de kalkış verin:lift = dot3(move, viewOf(P)). - Her karede anahtarı
k + lift × spring'dir. İndeksleri bu anahtara göre sıralayın ve DOM düğümlerini yalnızca sıra gerçekten değiştiğinde yeniden ekleyin (tek birDocumentFragment). Karelerin çoğu hiçbir şeyi değiştirmez. - Anahtarı ve hareket vektörünü öğe üzerinde saklayın (
data-k,data-m); böylece canlı betiğin geometriye ihtiyacı olmaz. - Borular parça başına sıralanır (
tubePieces→piece.mid); böylece bir boru bir parçanın önünden ve bir başkasının arkasından geçebilir. - Statik anahtarları yalnızca dinlenme pozlarından değil, hareketten oturtun. Her öğenin anahtarı
k + lift × spring'dir, ama kademeli yaylarla her grubun her karede kendi payı vardır; bu yüzden monte ve sökülmüş halde doğru sıralanan iki öğe arada yine de ters çizilebilir: ilk Raptor'da bir basınçlandırma hattı, her patlatmanın %40'ı boyunca önünde olduğu bir eyleyici kolonunun arkasına saklandı. Raptor, üst üste binen her çift için gerçek derinliği 0, 0.1 … 1'de, kendi yaylarının canlı kademeyle oturana kadar yeniden oynatılan her dördüncü karesinde, açılırken, kapanırken ve her kademe sınırının iki yanında tersine çevrilirken ve önek durumlarında ölçer (A.explodeStates(STAGGER, SPRING).depth, yani[...A.uniformStates(10), ...A.springStates(STAGGER, { ...SPRING, stride: 4, reversals: A.reversalsOf(STAGGER, SPRING.stagger) }), ...A.prefixStates(STAGGER)], 1.119 durum, ve{ order: STAGGER, spring: SPRING }ile yapılmış bir kaydedici içinA.settle(R, P)'nin varsayılanı);orderKeyshepsini birden çözer (her kural o karenin paylarını kullanır). Yalnızca açılış ve kapanış, hızlı bir üzerine gelmenin oluşturduğu karelerde yanlış çiftler bıraktı: yanma odası bandının önünde durması gereken bir temizleme flanşı 0.20 s'deki bir geri dönüşte onun arkasında kayboldu. Denetim, dinlenmede ve yeniden oynatılan herhangi bir karede yanlış bir çift olursa başarısız olur; yalnızca kimsenin görmediği tekdüze durumlar bir kilitlenme barındırabilir. - Yaylar için tek kaynak. Kademe sırasını ve yay sabitlerini (
SPRING = { stagger, stiffness, damping, substeps }) derlemede tutun ve verisinde sayfaya iletin; böylece denetim izleyicinin gördüğünü tam olarak yeniden oynatır. - Bir parçayı monte edildiği şeyle birlikte hareket ettirin (
ride). Yanma odasına cıvatalı bir sensör boru tesisatıyla birlikte vurgulanır ama yanma odasının yayıyla hareket eder. Her öğe,piece'in (birlikte vurgulandığı grup) yanındaride'ı (onu hareket ettiren yayın grubu, hareket vektöründen bulunur) kaydeder; sayfadata-ride'ı okur veplace()hem öteleme hem sıralama içinpiece[node.ride].x'i kullanır. Başka bir grubun vektörünü ödünç alıp kendi yayını koruyan bir öğe, bağlantısı boyunca kayar ve uçuş ortasında ona gömülür. - Önek kuralı. Her grubun yolu, daha önce çıkanlar dışarıdayken evde park etmiş, daha sonra ayrılan her gruptan açık olmalıdır: bir kademe sınırındaki geri dönüşün dondurduğu kare budur.
A.prefixStates(STAGGER)tam olarak bunları pozlar (ilkk − 1dışarıda,k'ıncı 0.25 … 1'de, kalanı evde) ve denetim bunları yeniden oynatılan karelerle birlikte kontrol eder. Raptor'da bu, sıcak gaz manifoldunu cıvatalandığı metan pompasından sonraya (vektörü pompanın içine bakar) ve kontrolcüyü boru tesisatından önceye (oksijenden ön yakıcıya giden hat kontrolcünün ölçüm hatlarıyla çevrilidir, bu yüzden ancak onlardan sonra çıkabilir) taşıdı. - Kademe sırasını, hiçbir şeyin bir şeyin içinden geçmesi gerekmeyecek şekilde belirleyin. Yaylar örtüşür: başlangıçlar arasında 35 ms ve oturmak için yaklaşık bir saniye olduğundan her grup aynı anda hareket eder ve göreli hareket her vektörün bir karışımıdır. İlk Raptor patlatması uçuş ortasında 66 çifti birbirinin içinden geçirdi. Bunu sıfıra indiren kurallar, denetim tarafından her karede ve geri dönüşlerde kontrol edilir:
- Dışarıda olan önce ayrılır. Her iki yanda, diğer her şeyin arkasına ya da altına sıkıştırıldığı parça önce hareket eder: Raptor'un lülesi her şeyden önce iner (her şey onun üzerinde durur) ve metan pompasına ulaşan her ölçüm hattının ve kablo demetinin dışından geçen iniş borusu ikinci gider. Kapanış bunu tersine çevirir; böylece dıştaki parça eve son dönen olur.
- Bir parçanın patlatma vektörü başka bir grubun bir yüzüne doğru bakıyorsa, o grup sırada daha önce gelir. İniş borusu lülenin port T'sine 84 iner; lüle 120 iner ve 35 ms önce ayrılır; böylece port açılırken ve kapanırken her zaman flanşın altındadır.
- Her vektörü, diğer taraf hâlâ yerindeyken parçanın dokunduğu her yüzden uzağa yöneltin, yalnızca cıvatalandığı ikisinden değil: yanma odası ile pompa arasından geçen bir hat, iki bağlantı normalinin de izin verdiği sektörde hareket etmelidir (Raptor'un ceket hatları iniş olmadan −120°'ye gider ve iki pedin üzerinden de kayar), çekirdek bir parçanın etrafına sarılmış bir kablo yalnızca tüm sarımı için dışa doğru olan sektörde hareket edebilir (90°'lik bir sarım 90°'ye izin verir; 180°'yi aşan bir sarım hiçbir şeye izin vermez, bu yüzden onu kısaltın ya da yakın tarafta bitirin) ve altına cıvatalı bir parçanın üzerinden aşağı süpürecek bir hat inmemelidir.
- Birbirinin içinden geçen şeyler bir yayı paylaşır. Kablo demeti halkasından bir bloğa yükselen ölçüm hatlarının ve o bloğun altındaki kablo demetinin ayrı yayları olamaz: aralarında 35 ms ile birbirlerini keserek geçerler. Raptor'un ölçüm hatları ve bloğu, boru tesisatı olarak vurgulanmaya devam ederken kontrolcüyle hareket eder (
BLOCK_MOVE = PIECES.controller.move; böyleceridecontroller'a çözülür). - Yalnızca tek bir parçaya bağlı her hatta yalnızca o parçanın hareketini verin.
- Yalnızca dinlenmeden çıkan ikisini değil, bir izleyicinin oluşturabileceği yolları kontrol edin: hızlı bir üzerine gelme yayları yarı yolda tersine çevirir (
reversals) ve dönüşte geç kalan bir grup zaten evde olan bir kablonun içinden süpürebilir. Birkaç yuvarlak zamanda değil, her kademe sınırında tersine çevirin (A.reversalsOf): kötü pencereler yaklaşık 15 ms genişliğindedir ve 0.15, 0.3 ve 0.5 s hepsini kaçırdı. Bu kareleri hem derinlik durumlarına hem açıklık kontrolüne verin ve katıları da katılara karşı test edin (A.solidClearances): ev sahibindeki bir çıkıntının altına sıkıştırılmış bir flanş, eve dönerken ev sahibinin yay kuyruğuyla 0.67 derinlikte buluştu; oysa her boru kontrolü sıfırdaydı.
Elle döndürme
3B bir şekil için (references/3d.md):
- Sürükleme.
controller.angleAt(clientX, clientY, zFace, "dial"), işaretçinin yüzün düzlemindeki açısıdır; turlar boyunca açılmış (unwrapped) olarak. İşaretçi basıldığında onu ve mevcut hedefi saklayın. Harekette hedef = başlangıç + Δ yapın ve hareket yaklaşık 1.5°'yi geçince işaretçiyi yakalayın. Grup üstel izlemeyle izler (τ = 0.12 s). Sahnetouch-action: pan-y'yi korur. - Tuşlar. Ok tuşları bir bölüm adım atar, Shift on adım atar, Home göstergeye döner.
- Bağlı gruplar. Onları
follow(values)üzerinden tek bir değerden sürün (kadranın dörtte biri hızında bir çark, tek birlift'ten bir parmak zinciri); böylece bir araç, bir sürükleme ve tur hepsi uyuşur. - Hareketler ve klipler zamanın saf fonksiyonlarıdır (
tapAt(t),breakAt(t)): derleme onları örnekler, denetim kare kare yürür ve sayfa sıçramadan sıfırlamak için onları geriye oynatabilir. - Kanca devralma. Herhangi bir
window.__isoTurnçağrısıonHold'u çalıştırır: turu durdurun ve okuyucu yeniden harekete geçene kadar bir aracın ayarladığı değerleri koruyun. - Yarım hız. Yoğun bir şekil
controller.setsüresinin ortalamasını tutar ve 14 ms'nin üstünde bir kare atlayarak, 9 ms'nin altında her karede çizer; zamana dayalı hareket değişmez. - Azaltılmış harekete tek bir durağan kare düşer. Ekran dışında döngü durur (IntersectionObserver).
Hazırlama (arming)
Bir eylem makinenin belirli bir durumda olmasını gerektiriyorsa (ateşlemeden önce monte edilmiş, döndürmeden önce kapı kapalı), önce hazırlayın:
- Onu o duruma getiren hedefleri ayarlayın (
setApart(false),select(null)). - Hazırlandı olarak işaretleyin: düğme
data-state="armed"gösterir ve okuma değeritoplanıyor · hazırlanıyorder. - Eylemin saatini ancak her yay vardığında ve hiçbir şey beklemediğinde başlatın.
Eylemi asla yarı sökülmüş bir makinede çalıştırmayın ve zaman kazanmak için parçaları asla yerine yapıştırmayın.
Turdan devralma
Kullanıcı boşta tur sırasında harekete geçtiğinde turu durdurun ve durumu olduğu yerde bırakın. Devralma yardımcınız bir şeyi sıfırlıyorsa (örneğin gücü zorla açıyorsa) ve kullanıcının eylemi de o şeyin bir değiştirmesiyse, ikisi aynı karede birbirini iptal eder ve bir animasyonu yeniden oynatır. Yardımcıya bir bayrak geçin (takeOver(switching)) ve her değiştirmeyi yalnızca varsayılanında değil, turun her evresinde test edin.
Tur sırasında basılan bir adım tuşu, turun çok ileride olabilecek hedefinden değil, okuyucunun gördüğü değerden adım atmalıdır. Dalga leğeninin tuşları tur sırasında freq.x ve gap.x'ten başlar (aksi halde state.tf ve state.td) ve steer() devraldığında iki yayın da hızını sıfırlar; böylece değer yalnızca tuşun yönünde hareket eder.
Evreleri duyurma
Evreleri olan bir makine için görsel olarak gizli bir aria-live="polite" öğesi ekleyin ve ona yalnızca evre değişikliklerini yazın: hazırlanıyor, soğutma, tahliye, ön yakıcılar yandı, ana aşama, kapanış, soğuk. Ona asla kare başına sayılar yazmayın. Sürekli bir kontrol için duyurmadan önce girdinin yaklaşık 700 ms sessiz kalmasını bekleyin (dalga leğeninin announce'u). Canlı bölgeyi yüklemede boş bırakın ve focus'ta güncel durumla doldurun; böylece bir ekran okuyucu bayat bir satır yerine geldiğinde durumu duyar. Etiketi değişen bir düğmenin ("Ateşle"den "Durdur"a) ayrıca aria-pressed'e ihtiyacı yoktur.
CSS hareketi
.iso-line[data-flow], bir yol boyunca ilerleyen şeyler için kesik çizgileri canlandırır..iso-dots circle[data-pulse], "etkin" bir yüzey için bir nokta ızgarasını yavaşça yanıp söndürür.- Üzerine gelince kalkışlar için bir grubu
transition: transform 700ms var(--anatomy-ease)ile sarın ve üzerine gelince yukarı öteleyin.
Bunların hepsi ISO_CSS içinde azaltılmış harekette kapanır.