Enkel Svep Med Vanilj JavaScript

0
49

Jag brukade tro att genomföra svep gester tvungen att vara mycket svårt, men jag har nyligen funnit mig själv i en situation där jag var tvungen att göra det och upptäckte att verkligheten är långt ifrån lika dyster som jag hade föreställt mig.

Denna artikel kommer att ta dig steg för steg genom att genomföra med den minsta mängden av kod som jag kunde komma på. Så, låt oss hoppa rakt in i det!

HTML-Struktur

Vi börjar med en .behållare som har en massa bilder inuti:

<div class=’container’>
<img src=’img1.jpg’ alt=’bild’/>

</div>

Grundläggande Stilar

Vi använder display: flex för att se till att bilder som går vid sidan av varandra utan mellanrum emellan. align-objekt: center mitten justera dem vertikalt. Vi gör både bilder och behållare ta den bredd av behållaren är förälder (kroppen i vårt fall).

.container {
display: flex;
align-objekt: center;
width: 100%;

img {
min-width: 100%; /* behövs så att Firefox inte göra img shrink to fit */
width: 100%; /* kan inte ta ut antingen som bryter Chrome */
}
}

Det faktum att både den .behållaren och dess barn-bilder har samma bredd som gör dessa bilder läcker ut på höger sida (markerad med röd ram) att skapa en vågrät rullningslist, men det är exakt vad vi vill ha:

Den första layouten (se live demo).

Med tanke på att inte alla bilder har samma mått och proportioner, vi har lite vitt utrymme ovanför och under några av dem. Så, vi kommer att trimma att genom att ge det .behållaren en tydlig höjd som bör ganska mycket arbete för den genomsnittliga bildförhållande av dessa bilder och inställning overflow-y för att dölja:

.container {
/* samma som innan */
overflow-y: hidden;
höjd: 50vw;
max-height: 100vh;
}

Resultatet kan ses nedan, med alla bilder som är klippta till samma höjd och inga tomma ytor längre:

Resultatet efter bilder som är klippta av overflow-y på .behållare (se live demo).

Okej, men nu har vi en vågrät rullningslist på .behållaren. Jo, det är faktiskt en bra sak för någon JavaScript fall.

Annars kan vi skapa en CSS-variabel –n för antalet bilder och vi använder denna för att göra .behållare som är tillräckligt bred för att hålla alla dess image barn som fortfarande har samma bredd som dess förälder (kroppen i det här fallet):

.container {
–n: 1;
width: 100%;
bredd: calc(var(–n)*100 procent).

img {
min-width: 100%;
width: 100%;
bredd: calc(100%/var(–n));
}
}

Observera att vi håller den föregående bredd förklaringar som fallbacks. Calc() värden kommer inte att ändra en sak tills vi sätter –n från JavaScript efter att få vår .container och antalet barn bilder det håller:

const _C = dokument.querySelector(‘.container’),
N = _C.barn.längd.

_C.style.setProperty(‘–n’, N)

Nu är det vår .behållaren har utökats för att passa alla bilder inuti:

Layout med utökade behållare (demo).

Byta Bilder

Nästa, vi bli av med den vågräta rullningslisten genom att ställa overflow-x: hidden på vår container förälder (kroppen i vårt fall) och så skapar vi en annan CSS-variabel som håller index för den för tillfället valda bilden (- jag). Vi använder denna för att positionera den .behållare med hänsyn till det virtuella via en översättning (kom ihåg att % – värden inuti översätta() funktioner i förhållande till storleken av de element som vi har satt denna transform):

body { overflow-x: hidden }

.container {
/* samma stil som innan */
förändra: översätta(calc(var(–jag, 0)/var(–n)*-100%));
}

Ändra –jag till ett annat heltal större eller lika med noll, men mindre än –n, ger en annan bild i bild, som illustreras av den interaktiva demo nedan (där värdet av –jag styrs av en rad input):

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Okej, men vi vill inte använda ett reglage för att göra detta.

Den grundläggande idén är att vi kommer att upptäcka riktning rörelsen mellan “touchstart” (eller “mousedown”) händelse och “touchend” (eller “funktioner”) och klicka sedan på uppdatera-jag därför att flytta behållaren så att nästa bild visas (om det finns) i önskad riktning flyttar in i den virtuella.

function lock(e) {};

funktion flytta(e) {};

_C.addEventListener(‘mousedown’, lås, false);
_C.addEventListener(‘touchstart’, lås, false);

_C.addEventListener(‘funktioner’, flytta, false);
_C.addEventListener(‘touchend’, flytta, false);

Observera att detta endast kommer att fungera för musen om vi sätter pekaren-händelser: ingen på bilder.

.container {
/* samma stil som innan */

img {
/* samma stil som innan */
pekare-händelser: none;
}
}

Även Kanten som behöver ha kontakt händelser aktiveras från ca:flaggor som det här alternativet är inaktiverat som standard:

Aktivera touch händelser i Kanten.

Innan vi fylla lock() och flytta() funktion, vi förena peka och klicka fall:

funktion ena(e) { return e.changedTouches ? e.changedTouches[0] : e };

Låsning på “touchstart” (eller “mousedown”) innebär att få och lagra x-koordinat i en första samordna variabel x0:

låt x0 = null;

function lock(e) { x0 = unify(e).clientX };

För att se hur man flyttar vår .behållare (eller om vi ens göra det eftersom vi inte vill gå vidare när vi har kommit till slut) kommer vi att kontrollera om vi har utfört lås () – åtgärd, och om vi har, kan vi läsa aktuell x-koordinat beräkna skillnaden mellan det och x0 och lösa vad man ska göra av sina tecken och den nuvarande index:

låt i = 0;

funktion flytta(e) {
om(x0 || x0 === 0) {
låt dx = unify(e).clientX – x0, s = Matematik.registrera(dx);

if((i > 0 || s < 0) && (i < N – 1 || s > 0))
_C.style.setProperty(‘–jag, jag -= s);

x0 = null
}
};

Resultatet på att dra åt vänster/ höger kan ses nedan:

Växla mellan bilder på svep (demo). Försök att flytta till höger på den första bilden eller vänster på den sista inte göra någonting, eftersom vi inte har någon annan bild innan eller efter, respektive.

Som nämnts ovan är det förväntade resultatet och de resultat vi får i Chrome för lite för att dra och Firefox. Dock Kanten navigerar bakåt och framåt när vi drar åt vänster eller höger, vilket är något som Chrome också gör på ett lite mer drag.

Kanten navigera sidvisning bakåt eller framåt på vänster eller höger svep.

För att åsidosätta detta, behöver vi lägga till en “touchmove” event listener:

_C.addEventListener(‘touchmove’, e => {e.preventDefault()}, false)

Okej, nu har vi något som fungerar i alla webbläsare, men det ser inte ut som att vad vi verkligen efter… ännu!

Jämn Rörelse

Det enklaste sättet att flytta för att få vad vi vill ha är genom att lägga till en övergång:

.container {
/* samma stil som innan */
övergång: transform .5s enkel outl
}

Och här är det, en mycket grundläggande svep effekt i ca 25 rader JavaScript och ca 25 rader CSS:

Arbetar svep effekt (demo).

Tyvärr, det är en Kant bugg som gör att en övergång till en CSS-variabel beroende calc() översättning misslyckas. Usch, jag antar att vi måste glömma Kanten för nu.

Förfina det Hela

Med alla coola svep effekter ute, vad vi har hittills inte riktigt klippa av det, så låt oss se vilka förbättringar som kan göras.

Bättre Visuella Ledtrådar Medan Du Drar

Först händer ingenting medan vi dra, alla åtgärder som följer “touchend” (eller “funktioner”) händelse. Så, medan vi drar, vi har ingen indikation på vad som kommer att hända härnäst. Är det en nästa bild för att växla till i önskad riktning? Eller har vi kommit till slutet av raden och ingenting kommer att hända?

Att ta hand om det, vi justera översättning belopp lite genom att lägga till en CSS-variabel –tx som ursprungligen 0px:

förändra: översätta(calc(var(–jag, 0)/var(–n)*-100% + var(–tx, 0px)))

Vi använder två fler evenemang lyssnare: en för “touchmove” och en annan för “mousemove”. Observera att vi redan förebygga bakåt och framåt navigering i Chrome med “touchmove” lyssnare:

funktionen dra(e) { e.preventDefault() };

_C.addEventListener(‘mousemove’, dra, false);
_C.addEventListener(‘touchmove’, dra, false);

Nu ska vi fylla dra () – funktionen! Om vi har utfört lås () – åtgärd, kan vi läsa aktuell x-koordinat beräkna skillnaden dx mellan denna samordna och den första x0 och-tx till detta värde (som är en pixel värde).

funktionen dra(e) {
e.preventDefault();

om(x0 || x0 === 0)
_C.style.setProperty(‘–tx’, `${Math.runda(unify(e).clientX – x0)}px”)
};

Vi måste också se till att återställa –tx till 0px i slutet och ta bort övergång för den tid dra. För att göra detta lättare, vi flyttar övergången förklaring på .smidig klass:

.smidig { övergången: transform .5s ease-out; }

I låset() funktion, vi tar bort denna klass från .behållare (vi kommer att lägga till det igen i slutet på “touchend” och “funktioner”) och även ange ett låst boolesk variabel, så att vi inte behöver för att hålla utför x0 || x0 === 0 in. Vi använder sedan den låst variabel för kontroller istället:

låt låsta = false;

function lock(e) {
x0 = unify(e).clientX;
_C.classList.växla(‘jämn’, !(låsta = sant))
};

funktionen dra(e) {
e.preventDefault();
om(låst) { /* samma som innan */ }
};

funktion flytta(e) {
om(låst) {
låt dx = unify(e).clientX – x0, s = Matematik.registrera(dx);

if((i > 0 || s < 0) && (i < N – 1 || s > 0))
_C.style.setProperty(‘–jag, jag -= s);
_C.style.setProperty(‘–tx’, ‘0px’);
_C.classList.växla(‘jämn’, !(låsta = false));
x0 = null
}
};

Resultatet kan ses nedan. Medan vi ändå dra, vi har nu en visuell indikation av vad som kommer att hända härnäst:

Svep med visuella ledtrådar medan du drar (demo).

Fixa övergången-längd

Vid denna punkt, vi använder alltid den samma övergång-längd oavsett hur mycket av bildens bredd vi har fortfarande att översätta efter det att dra. Vi kan fixa det på ett ganska enkelt sätt genom att införa en faktor f, som vi också satt som en CSS-variabel för att hjälpa oss att beräkna den faktiska animation längd:

.smidig { övergången: omvandla calc(var(–f, 1)*.5s) ease-out; }

I JavaScript, får vi en bild bredd (uppdaterad på “ändra storlek”) och beräknar för vad bråkdel av detta har vi dras horisontellt:

låt w;

funktion storlek() { w = – fönstret.innerWidth };

funktion flytta(e) {
om(låst) {
låt dx = unify(e).clientX – x0, s = Matematik.registrera(dx),
f = +(s*dx/w).toFixed(2);

if((i > 0 || s < 0) && (i < N – 1 || s > 0)) {
_C.style.setProperty(‘–jag, jag -= s);
f = 1 – f
}

_C.style.setProperty(‘–tx’, ‘0px’);
_C.style.setProperty(‘–f’, f);
_C.classList.växla(‘jämn’, !(låsta = false));
x0 = null
}
};

storlek();

addEventListener(‘ändra storlek’, storlek, false);

Detta ger oss ett bättre resultat.

Gå tillbaka om det inte finns tillräckligt dra

Låt oss säga att vi inte vill gå vidare till nästa bild om vi bara dra en liten bit under en viss tröskel. För nu, ett 1px skillnaden under drag innebär att vi går framåt till nästa bild och det känns lite onaturligt.

För att åtgärda detta, vi satt ett gränsvärde på låt oss säga 20% av bildens bredd:

funktion flytta(e) {
om(låst) {
låt dx = unify(e).clientX – x0, s = Matematik.registrera(dx),
f = +(s*dx/w).toFixed(2);

if((i > 0 || s < 0) && (i < N – 1 || s > 0) && f > .2) {
/* samma som innan */
}

/* samma som innan */
}
};

Resultatet kan ses nedan:

Vi bara vidare till nästa bild om vi drar nog (demo).

Kanske Lägga till en Studs?

Detta är något som jag inte är säker på var en bra idé, men jag längtar efter att prova i alla fall: ändra tidpunkten funktion så att vi inför en studs. Efter lite om och dra handtagen på cubic-bezier.com jag kom upp med en grund som verkade lovande:

Vad våra valda kubikmeter Bézier timing funktion ser ut jämfört med en vanlig enkel ut.

övergången till: omvandla calc(var(–f)*.5s) kubikmeter-bezier(1, 1.59, .61, .74);

Att använda en egen CSS timing funktion att införa en studs (demo).

Vad sägs Om JavaScript Sätt, Då?

Vi skulle kunna uppnå en bättre grad av kontroll över mer naturlig känsla och mer komplexa studsar genom att ta JavaScript vägen för övergången. Detta skulle också ge oss Edge stöd.

Vi börjar med att bli av med övergången och –tx och –f CSS variabler. Detta minskar vårt förvandla sig till vad det var från början:

förändra: översätta(calc(var(–jag, 0)/var(–n)*-100%));

Ovanstående kod betyder också –jag behöver nödvändigtvis inte vara ett heltal längre. Samtidigt som det fortfarande är ett heltal medan vi har en enda bild helt i visa, det är inte fallet längre medan vi dra eller under rörelse efter att gratisspelet aktiverats, “touchend” eller “funktioner” – händelser.

Till exempel, samtidigt som vi har den första bilden i utsikt, –jag är 0. Samtidigt har vi den andra helt i utsikt, –jag är 1. När vi är halvvägs mellan den första och den andra, –jag är .5. När vi har en fjärdedel av de första ett och tre fjärdedelar av de andra en tanke, –jag är .75.

Att vi sedan uppdatera JavaScript för att ersätta koden delar där vi uppdaterar dessa CSS-variabler. Först tar vi hand om lås () – funktionen, där vi dike att växla .smidig klass och dra () – funktion, där vi i stället för att uppdatera –tx variabel vi har dikat med att uppdatera-jag, vilket, som tidigare nämnts, behöver inte vara ett heltal längre:

function lock(e) {
x0 = unify(e).clientX;
låsta = true
};

funktionen dra(e) {
e.preventDefault();

om(låst) {
låt dx = unify(e).clientX – x0,
f = +(dx/w).toFixed(2);

_C.style.setProperty(‘–jag, jag – f)
}
};

Innan vi också att uppdatera flytta() funktion, vi införa två nya variabler, ini och fin. Dessa representerar de ursprungliga värde vi sätter –jag till i början av animation och det slutliga värdet kan vi ställa samma variabel för att i slutet av animation. Vi kan också skapa en animation funktion ani():

låt ini, fin;

funktion ani() {};

funktion flytta(e) {
om(låst) {
låt dx = unify(e).clientX – x0,
s = Matematik.registrera(dx),
f = +(s*dx/w).toFixed(2);

ini = i – r*f;

if((i > 0 || s < 0) && (i < N – 1 || s > 0) && f > .2) {
jag -= s;
f = 1 – f
}

fin=) jag;
ani();
x0 = null;
låsta = false;
}
};

Detta är inte alltför olik den kod som vi hade innan. Vad som har förändrats är att vi inte sätta några CSS-variabler i denna funktion längre men istället ställ ini och fin JavaScript variabler och ringa animation ani () – funktionen.

ini är den ursprungliga värde vi sätter –jag till i början av animation som “touchend”/ “mouseup” händelse utlöser. Denna ges av den nuvarande position som vi har när en av dessa två händelser som bränder.

fin är den slutliga värde vi sätter –jag till i slutet av samma animation. Detta är alltid ett heltal eftersom vi alltid avslutar med en bild helt i sikte, så fin och –jag är indexet av den bilden. Detta är nästa bild i önskad riktning om vi dras nog (f > .2) och om det finns en nästa bild i önskad riktning ((i > 0 || s < 0) && (i < N – 1 || s > 0)). I detta fall kommer vi också att uppdatera JavaScript-variabel som lagrar den aktuella bilden index (jag) och den relativa avståndet till det (f). Annars är det samma bild, så jag och f inte behöver för att få uppdaterade.

Nu, låt oss övergå till ani () – funktionen. Vi börjar med en förenklad linjär version som lämnar ut en ändring av riktning.

const NF = 30;

låt rID = null;

funktion stopAni() {
cancelAnimationFrame(rID)
rID = null
};

funktion ani(cf = 0) {
_C.style.setProperty(‘–jag, ini + (fin – ini)*cf/NF);

om(cf === NF) {
stopAni();
tillbaka
}

rID = requestAnimationFrame(ani.bind(detta ++cf))
};

Den huvudsakliga idén här är att övergången mellan det ursprungliga värdet ini och den sista en fin händer över en totalt antal bilder NF. Varje gång vi kallar ani() funktion, vi beräkna de framsteg som förhållandet mellan den aktuella bildrutan index cf och det totala antalet bildrutor NF. Detta är alltid ett tal mellan 0 och 1 (eller du kan ta det som en procentsats, som går från 0% till 100%). Vi använder sedan detta framsteg värde för att få det aktuella värdet av –jag och ställa in den i attributet style i vår container _C. Om vi fick den slutliga tillstånd (nuvarande ram index cf är lika med det totala antalet ramar NF, vi stänger animering-loop). Annars kan vi bara öka den nuvarande ram index cf och samtal ani() igen.

Vid denna punkt, att vi har en fungerande demo med en linjär JavaScript övergången:

Version med linjär JavaScript övergång (demo).

Detta har dock problem som vi haft i CSS-fallet: oavsett avstånd, vi måste ha för att smidigt att översätta vår del över på release (“touchend” / “funktioner”) och löptiden är alltid den samma eftersom vi alltid animera under samma antal bildrutor NF.

Låt oss fixa det!

För att göra detta måste vi införa en annan variabel anf där vi lagrar det faktiska antalet bilder som vi använder och vars värde vi beräknar att flytta () – funktionen, innan du ringer animation funktion ani():

funktion flytta(e) {
om(låst) {
låt dx = unify(e).clientX – x0,
s = Matematik.registrera(dx),
f = +(s*dx/w).toFixed(2);

/* samma som innan */

anf = Matematik.runda(f*NF);
ani();

/* samma som innan */
}
};

Vi måste också ersätta NF med anf i animation funktion ani():

funktion ani(cf = 0) {
_C.style.setProperty(‘–jag, ini + (fin – ini)*cf/anf);

om(cf === anf) { /* samma som innan */ }

/* samma som innan */
};

Med detta, vi har fasta tidsfel!

Version med linjär JavaScript övergång vid konstant hastighet (demo).

Okej, men en linjär timing funktion är inte alltför spännande.

Vi skulle kunna försöka JavaScript medel av CSS timing funktioner såsom enkel -, lätt-out eller ease-in-out och se hur de jämför. Jag har redan förklarat i en hel del detaljer hur man får dessa i tidigare länkad artikel, så jag tänker inte gå igenom det igen och bara släppa objekt med dem alla i koden:

const TFN = {
‘linjär’: function(k) { return k },
‘ease-in”: funktion(k, e = 1.675) {
tillbaka Matematik.pow(k, e)
},
‘ease-in”: funktion(k, e = 1.675) {
avkastning 1 – Matematik.pow(1 – k, e)
},
‘ease-in-out” – funktion(k) {
tillbaka .5*(Matematik.synd((k – .5)*Matematik.PI) + 1)
}
};

K-värdet är det framsteg, som är förhållandet mellan den aktuella bildrutan index cf och det faktiska antalet bilder övergången sker under anf. Detta innebär att vi ändrar ani () – funktionen lite om vi vill använda den lätthet-ut till exempel:

funktion ani(cf = 0) {
_C.style.setProperty(‘–jag, ini + (fin – ini)*TFN[‘enkel’](cf/anf));

/* samma som innan */
};

Version med lätthet ut JavaScript övergång (demo).

Vi skulle också kunna göra saker mer intressant genom att använda den typ av studsande timing funktion att CSS inte kan ge oss. Till exempel, något som illustreras av demo nedan (klicka för att utlösa en övergång):

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Den grafiska för att detta skulle vara något liknande det som den easeOutBounce tidtagningen från easings.net.

Grafisk representation av tidtagningen.

Processen för att få denna typ av timing-funktionen är liknande den för att få den JavaScript-version av CSS-lätthet-och-ut (igen, som beskrivs i tidigare länkad artikel på att efterlikna CSS timing funktioner med JavaScript).

Vi börjar med cosinus-funktionen på [0, 90°] intervall (eller [0, π/2] i radianer) för ingen studs, [0, 270°] ([0, 3·π/2]) för 1 studsa, [0, 450°] ([0, 5·π/2]) för 2 studsar och så vidare… i övrigt är det [0, (n + ½)·180°] intervall ([0, (n + ½)·π) n studsar.

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Inmatning av denna cos(k) – funktionen i [0, 450°] intervall, medan produktionen i [-1, 1] intervall. Men vad vi vill ha är en funktion vars domän är [0, 1] intervall och vars temperatur är också [0, 1] intervall.

Vi kan begränsa temperatur till [0, 1] intervall genom att bara ta det absoluta värdet |cos(k)|:

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Samtidigt som vi fick det intervall som vi ville ha för den temperatur vi vill att värdet av denna funktion på 0 till 0 och sitt värde vid andra änden av intervallet är 1. För närvarande, det är den andra vägen runt, men vi kan fixa detta om vi ändrar vår funktion till 1 – |cos(k)|:

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Nu kan vi gå vidare till att begränsa domän från [0, (n + ½)·180°] intervallet [0, 1] intervall. För att göra detta ändrar vi vår funktion att vara 1 – |cos(k·(n + ½)·180°)|:

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Detta ger oss både önskad domän och temperatur, men vi har fortfarande en del problem.

Först av allt, alla våra studsar har samma höjd, men vi vill att deras höjd för att minska k ökar från 0 till 1. Vår lösning i detta fall är att multiplicera cosinus med 1 – k (eller med en effekt av 1 – k för en icke-linjär minskning av amplituden). Interaktiv demo nedan visar hur detta amplitud förändringar för olika exponenter och hur detta påverkar den funktion vi har hittills:

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

För det andra, alla de studsar ta lika lång tid, trots att deras amplituder hålla minskar. Första tanken här är att använda en effekt av k inuti cosinus funktion istället för att bara k. Detta lyckas göra konstiga saker som cosinus inte slår 0 med lika stora intervaller, längre, vilket betyder att vi inte alltid får att f(1) = 1 längre vilket är vad vi skulle alltid behöver från en tidtagningen vi faktiskt kommer att använda. Men, för något som en = 2.75, n = 3 och b = 1.5, får vi ett resultat som ser ut tillfredsställande, så vi låter det vara som det är, även om det kunde vara fejk för bättre kontroll:

Tidpunkten funktion vi vill prova.

Detta är den funktion vi prova i JavaScript-om vi vill ha lite studsande att hända.

const TFN = {
/* den andra funktionen som vi hade innan */
‘bounce-in”: funktion(k, n = 3, a = 2.75, b = 1.5) {
avkastning 1 – Matematik.pow(1 – k, a)*Matematik.abs(Matematik.cos(Matematik.pow(k, b)*(n + .5)*Matematik.PI))
}
};

Hmm, verkar lite för extrem i praktiken:

Version med en studsande JavaScript övergång (demo).

Kanske kunde vi göra n beror på mängden av översättning som vi fortfarande behöver för att utföra från tidpunkten för utgivningen. Vi gör det till en variabel som vi sedan in i flytten () – funktionen innan du ringer animation funktion ani():

const TFN = {
/* den andra funktionen som vi hade innan */
‘bounce-in”: funktion(k, a = 2.75, b = 1.5) {
avkastning 1 – Matematik.pow(1 – k, a)*Matematik.abs(Matematik.cos(Matematik.pow(k, b)*(n + .5)*Matematik.PI))
}
};

var n;

funktion flytta(e) {
om(låst) {
låt dx = unify(e).clientX – x0,
s = Matematik.registrera(dx),
f = +(s*dx/w).toFixed(2);

/* samma som innan */

n = 2 + Matematik.runda(f)
ani();
/* samma som innan */
}
};

Detta ger oss vårt slutgiltiga resultat:

Version med den slutliga studsande JavaScript övergång (demo).

Det är definitivt fortfarande utrymme för förbättringar, men jag har inte en känsla för vad som gör en bra animation, så jag ska bara låta det vara som det är. Som det är, detta är nu funktionella webbläsare (utan att ha någon av Kanten frågor som den version som använder en CSS-övergången) och ganska flexibel.