1 HTML-Element + 5 CSS-Egenskaper = Magi!

0
45

La oss si at jeg fortalte deg at vi kan få resultater under med bare en HTML-element og fem CSS-egenskaper for hver. Ingen SVG, ingen bilder (lagre for bakgrunnen på rot som er der bare for å gjøre det klart at vår en HTML-element har noen gjennomsiktige deler), ikke JavaScript. Hva ville du tror det innebærer?

De ønskede resultater.

Vel, denne artikkelen kommer til å forklare hvordan å gjøre dette, og så også vise deg hvordan å gjøre ting som er gøy med å legge i noen animasjon.

CSS-ing Gradient Stråler

HTML-koden er bare en <div>.

<div class=’stråler’></div>

I CSS, vi må sette mål for dette elementet, og vi trenger å gi den en bakgrunn, slik at vi kan se det. Vi også gjøre det sirkulære ved hjelp av border-radius:

.stråler {
bredde: 80vmin; høyde: 80vmin;
border-radius: 50%;
bakgrunn: linear-gradient(#b53, #f90);
}

Og… vi har allerede brukt opp fire av fem eiendommer for å få det resultatet nedenfor:

Se Pennen av thebabydino (@thebabydino) på CodePen.

Så hva er den femte? maske med et gjentakende-ikpsoniske-gradient() verdi!

La oss si at vi ønsker å ha 20-stråler. Dette betyr at vi må bevilge $p: 100%/20 av full sirkel for en ray og gapet etter det.

Å dele platen inn-stråler og hull (live).

Her vil vi holde hull i mellom stråler lik stråler (så det er .5*$s for enten en stråle eller en plass), men vi kan gjøre noen av dem bredere eller smalere. Vi ønsker en brå endring etter avslutningen stopp-posisjonen i ugjennomsiktig del (ray), så start-stopp-posisjonen for den gjennomsiktige delen (gap) skal være lik eller mindre enn det. Så hvis de slutter stopp-posisjonen for ray er .5*$p, deretter start-stopp-posisjonen for gapet kan ikke bli større. Det kan imidlertid være mindre og som hjelper oss til å holde ting enkelt fordi det betyr at vi kan rett og slett null.

Hvordan gjenta-ikpsoniske-gradient() fungerer (live).

$nr: 20; // antall stråler
$p: 100%/$nn; // prosent av sirkel allokert til ray og gap etter

.stråler {
/* samme som før */
maske: gjenta-ikpsoniske-gradient(#000 0% .5*$p, gjennomsiktig 0% $p);
}

Vær oppmerksom på at, i motsetning til for lineære og radial graderinger, stop posisjoner for ikpsoniske graderinger kan være unitless. De trenger å være enten prosenter eller kantete verdier. Dette betyr å bruke noe som gjennomsiktige 0 $p ikke fungerer, vi trenger gjennomsiktig 0% $p (eller 0deg i stedet for 0%, det spiller ingen rolle hvilken vi velge, det kan bare ikke være unitless).

Gradient-stråler (live demo, ingen Edge-støtte).

Det er et par ting å merke seg her når det gjelder å støtte:

  • Kant støtter ikke maskering på HTML-elementene på dette punktet, men dette er oppført som er under Utvikling, og et flagg for det (det gjør ikke noe, for nå) har allerede vist i om:flagg.
    Den Aktivere CSS Maskering flagg i Kanten.
  • ikpsoniske-gradient() støttes bare innebygd av Blink nettlesere bak Eksperimentelle Web-Plattformen har flagget (som kan aktiveres fra chrome://flagg eller opera://flagg). Støtte kommer til Safari som godt, men inntil det skjer, Safari fortsatt baserer seg på polyfill, akkurat som Firefox.
    Den Eksperimentelle Web-Plattformen har flagget er aktivert i Chrome.
  • WebKit nettlesere trenger fortsatt -webkit – prefiks for maske-egenskaper på HTML-elementer. Du skulle tro at det er ikke noe problem siden vi bruker polyfill som baserer seg på -prefix-gratis uansett, så, hvis vi bruker polyfill, trenger vi å inkludere-prefix-gratis før det uansett. Dessverre, det er litt mer komplisert enn som så. Det er fordi -prefix-gratis fungerer via funksjonen for gjenkjenning, som mislykkes i dette tilfellet fordi alle nettlesere støtter maske unprefixed… på SVG-elementer! Men vi bruker maske på et HTML-element her, så vi er i en situasjon hvor WebKit nettlesere trenger -webkit – prefiks, men -prefix-gratis vil ikke legge til det. Så jeg antar at det betyr at vi må legge den til manuelt:
    $nr: 20; // antall stråler
    $p: 100%/$nn; // prosent av sirkel allokert til ray og gap etter
    $m: gjenta-ikpsoniske-gradient(#000 0% .5*$p, gjennomsiktig 0% $p); // maske

    .stråler {
    /* samme som før */
    -webkit-maske: $m;
    maske: $m;
    }

    Jeg antar vi kan også bruke Autoprefixer, selv om vi må ta med-prefix-gratis uansett, men ved hjelp av både nettopp for dette føles litt som å bruke en ninja våpen til å drepe en flue.

Legge i Animasjon

En kul ting om ikpsoniske-gradient() blir støttet innebygd i Blink nettlesere er at vi kan bruke CSS variabler inne i dem (vi kan ikke gjøre det når du bruker den polyfill). Og CSS variabler kan nå også være animert i Blink nettlesere med en bit av Houdini magic (vi trenger Eksperimentelle Web-Plattformen har flagget ut til å være aktivert for det, men vi må også ha det aktivert for innfødte ikpsoniske-gradient() støtte, så det burde ikke være et problem).

For å forberede våre koden for animasjon, vil vi endre våre maskering gradient, slik at den bruker variabel alpha-verdier:

$m: gjenta-ikpsoniske-gradient(
rgba(#000, var (- en)) 0% .5*$p,
rgba(#000, calc(1 – var (- en))) 0% $p);

Vi deretter registrere alfa –en tilpasset bolig:

CSS.registerProperty({
name:’–‘,
syntaks: ‘<tall>’,
initialValue: 1;
})

Og til slutt, vil vi legge til en animasjon i CSS:

.stråler {
/* samme som før */
animasjon: 2s en lineær uendelig alternativ;
}

@keyframes en {{ –a: 0 } }

Dette gir oss følgende resultat:

Ray-alfa-animasjon (live demo, fungerer bare i Blink nettlesere med den Eksperimentelle Web-Plattformen har flagget er aktivert).

Meh. Ser ikke så stor. Vi kan imidlertid gjøre ting mer interessant ved å bruke flere alpha-verdier:

$m: gjenta-ikpsoniske-gradient(
rgba(#000, var(–a0)) 0%, rgba(#000, var(–a1)) .5*$p,
rgba(#000, var(–a2)) 0%, rgba(#000, var(–a3)) $p);

Neste trinn er å registrere hver av disse egendefinerte egenskaper:

for(la i = 0; i < 4; i++) {
CSS.registerProperty({
name: `–en${i}`,
syntaks: ‘<tall>’,
initialValue: 1 – ~~(jeg/2)
})
}

Og til slutt, legge til animasjoner i CSS:

.stråler {
/* samme som før */
animasjon: 2s en uendelig alternativ;
animasjon-navn: a0, a1, a2, a3;
animasjon-timing-funksjonen:
/* easings fra easings.net */
cubic-bezier(.57, .05, .67, .19) /* easeInCubic */,
cubic-bezier(.21, .61, .35, 1); /* easeOutCubic */
}

@for $jeg fra 0 til 4 {
@keyframes a#{$i} og {{ –a#{$i}: #{gulv($i/2)} } }
}

Merk at siden vi er innstillingsverdier for å egendefinerte egenskaper, må vi interpolere gulv () – funksjonen.

Flere ray-alfa-animasjoner (live demo, fungerer bare i Blink nettlesere med den Eksperimentelle Web-Plattformen har flagget er aktivert).

Det ser nå litt mer interessant, men sikkert vi kan gjøre bedre?

La oss prøve å bruke en CSS-variabel for stopp-posisjon mellom ray og gap:

$m: gjenta-ikpsoniske-gradient(#000 0% var(–p), transparent 0% $p);

Vi må da registrere dette variabel:

CSS.registerProperty({
name: ‘–p’,
syntaks: ‘<prosent>’,
initialValue: ‘0%’
})

Og vi animere den fra CSS ved hjelp av en keyframe animasjon:

.stråler {
/* samme som før */
animasjon: p .5s lineær uendelig alternative
}

@keyframes p {{ –s: #{$p} } }

Resultatet er mer interessant i denne saken:

Vekslende ray størrelse animasjon (live demo, fungerer bare i Blink nettlesere med den Eksperimentelle Web-Plattformen har flagget er aktivert).

Men vi kan fortsatt spice den opp litt mer ved å vippe hele horisontalt i mellom hver iterasjon, slik at det alltid er snudd til det motsatte seg. Dette betyr ikke snudd når –p går fra 0% til $p og vendes når –p går tilbake fra $s%til 0%.

Måten vi snu et element horisontalt, er ved å bruke en transformering: scalex(-1) til det. Siden vi ønsker at dette snur å bli brukt på slutten av den første iterasjon og deretter fjernet på slutten av den andre (omvendt), kan vi bruke det i en keyframe-animasjon så godt i ett med et trinn() timing funksjon og dobbel animasjon-varighet.

$t: .5s;

.stråler {
/* samme som før */
animasjon: s $t lineær uendelig alternative,
s 2*$t trinn(1) uendelig;
}

@keyframes p {{ –s: #{$p} } }

@keyframes s { 50% { forvandle: scalex(-1); } }

Nå er vi endelig har et resultat som faktisk ser ganske kul:

Vekslende ray størrelse animasjon med horisontal flip i mellom iterasjoner (live demo, fungerer bare i Blink nettlesere med den Eksperimentelle Web-Plattformen har flagget er aktivert).

CSS-ing Gradient Stråler og Bølger

For å få stråler og krusninger resultat, må vi legge til en ekstra overgangen til maske, denne gangen et gjentakende-radial-gradient().

Hvordan gjenta-radial-gradient() fungerer (live).

$nr: 20;
$p: 100%/$nr;
$stop-liste: #000 0% .5*$p, gjennomsiktig 0% $p;
$m: gjenta-ikpsoniske-gradient($stop-listen),
gjenta-radial-gradient(nærmest-side, $stop-liste);

.stråler-krusninger {
/* samme som før */
maske: $m;
}

Dessverre, med flere stopp stillinger fungerer bare i Blink nettlesere med samme Eksperimentelle Web-Plattformen har flagget er aktivert. Og mens ikpsoniske-gradient() polyfill dekker dette for å gjenta-ikpsoniske-gradient() del i nettlesere som støtter CSS maskering på HTML-elementer, men ikke støtte ikpsoniske graderinger opprinnelig (Firefox, Safari, Blink nettlesere uten flagget er aktivert), ingenting løser problemet for å gjenta-radial-gradient() del i disse nettleserne.

Dette betyr at vi er tvunget til å ha noen gjentagelse i vår kode:

$nr: 20;
$p: 100%/$nr;
$stop-liste: #000, #000 .5*$p, gjennomsiktig 0%, gjennomsiktig $p;
$m: gjenta-ikpsoniske-gradient($stop-listen),
gjenta-radial-gradient(nærmest-side, $stop-liste);

.stråler-krusninger {
/* samme som før */
maske: $m;
}

Vi er selvsagt å komme nærmere, men vi er ikke helt der ennå:

Formidler resultatet med de to maske lag (live demo, ingen Edge-støtte).

For å få det resultatet vi ønsker, trenger vi å bruke en maske-kompositt holderen og sett den til exclude:

$m: gjenta-ikpsoniske-gradient($stop-liste) utelate,
gjenta-radial-gradient(nærmest-side, $stop-liste);

Vær oppmerksom på at mask-kompositt støttes bare i Firefox 53+ for nå, selv om Kanten skal bli med i når det endelig støtter CSS maskering på HTML-elementer.

XOR-stråler og bølger (live demo, Firefox 53+).

Hvis du tror det ser ut som stråler og sprekkene mellom stråler er ikke like, du har rett. Dette er på grunn av en polyfill problemet.

Legge i Animasjon

Siden maske-kompositt bare fungerer i Firefox for nå og Firefox ennå ikke støtte ikpsoniske-gradient() problemfritt, vi kan ikke sette CSS variabler inne i den gjentakende-ikpsoniske-gradient() (fordi Firefox fortsatt faller tilbake på polyfill for det og polyfill ikke støtter CSS variabel bruk). Men vi kan sette dem inn i den gjentakende-radial-gradient (), og selv om vi ikke kan animere dem med CSS keyframe animasjoner, kan vi gjøre det ved hjelp av JavaScript!

Fordi vi er nå å sette CSS variabler inne i den gjentakende-radial-gradient(), men ikke innenfor den gjentakende-ikpsoniske-gradient() (som XOR-effekt fungerer bare via maske-kompositt, som støttes bare i Firefox for nå og Firefox støtter ikke ikpsoniske graderinger problemfritt, så det faller tilbake på polyfill, som ikke støtter CSS variabel bruk), vi kan ikke bruke den samme $stop-liste for både gradient lag av vår masken lenger.

Men hvis vi er nødt til å skrive om våre maske uten en felles $stop-listen uansett, kan vi ta denne muligheten til å bruke ulike stop posisjoner for to graderinger:

// for ikpsoniske gradient
$nc: 20;
$pc: 100%/$nc;
// for radial gradient
$nr: 10;
$pr: 100%/$nr;

CSS variabel vi animere er en alfa-en og en, akkurat som for første animasjon i stråler tilfelle. Vi vil også introdusere –c0 og c1 –variabler fordi vi her kan ikke ha flere stillinger per stopp, og vi ønsker å unngå gjentakelse så mye som mulig:

$m: gjenta-ikpsoniske-gradient(#000 .5*$pc, gjennomsiktig 0% $pc) utelate,
gjenta-radial-gradient(nærmest-side,
var(–c0), var(–c0) .5*$pr,
var(–c1) 0, var(–c1) $pr);

body {
–a: 0;
/* layout, bakgrunner og andre irrelevante ting */
}

.xor {
/* samme som før */
–c0: #{rgba(#000, var (- en))};
–c1: #{rgba(#000, calc(1 – var (- en)))};
maske: $m;
}

Alpha-variabel-en er den vi animere frem og tilbake (fra 0 til 1, og deretter tilbake til 0 igjen) med en liten bit av vanilje JavaScript. Vi starter med å angi totalt antall bilder NF animasjonen skjer over en nåværende ramme stikkordregister f og en nåværende animasjon retning dir:

const NF = 50;

la f = 0, dir = 1;

I en oppdatering () – funksjonen, vi vil oppdatere gjeldende ramme indeks f og så setter vi den gjeldende fremgang verdi (f/NF) til den aktuelle alpha-en. Hvis f har nådd enten er 0 av NF, vi endre retning. Deretter update () – funksjonen blir kalt igjen på neste oppdatering.

(funksjonen update() {
f += dir;

dokumentet.kroppen.stil.setProperty (‘–‘, (f/NF).toFixed(2));

if(!(f%NF)) dir *= -1;

requestAnimationFrame(update)
})();

Og det er alt for JavaScript! Vi har nå en animert resultat:

Ripple alpha animasjon, lineær (live demo, fungerer bare i Firefox 53+).

Dette er en lineær animasjon, alfa verdi-en blir satt til fremdriften f/NF. Men vi kan endre timingen funksjonen til noe annet, som beskrevet i en tidligere artikkel skrev jeg på å simulere CSS timing funksjoner med JavaScript.

For eksempel, hvis vi ønsker en letthet-i form av timing funksjon, satte vi i alpha-verdien til easeIn(f/NF) i stedet for bare f/NF, hvor vi har som easeIn() er:

funksjonen easeIn(k, e = 1.675) {
tilbake Matematikk.pow(k, e)
}

Resultatet ved bruk av en letthet-timing-funksjonen kan sees i denne Pennen (fungerer bare i Firefox 53+). Hvis du er interessert i hvordan vi fikk denne funksjonen, det er forklart i tidligere knyttet artikkelen på timing funksjoner.

Nøyaktig samme tilnærming som fungerer for easeOut() eller easeInOut():

funksjonen easeOut(k, e = 1.675) {
avkastning 1 – Matematikk.pow(1 – k, e)
};

funksjonen easeInOut(k) {
tilbake .5*(Matematikk.synd((k – .5)*Matematikk.PI) + 1)
}

Siden vi bruker JavaScript uansett, vi kan gjøre hele interaktive, slik at animasjonen bare skjer på klikk/tap, for eksempel.

For å gjøre dette, vil vi legge til en forespørsel om ID-variabel (rID), som er utgangspunktet null, men så tar den verdien som returneres av requestAnimationFrame() i oppdater () – funksjonen. Dette gjør oss i stand til å stoppe animasjonen med en stopAni () – funksjonen når vi ønsker å:

/* samme som før */

la rID = null;

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

funksjonen update() {
/* samme som før */

if(!(f%NF)) {
stopAni();
tilbake
}

rID = requestAnimationFrame(update)
};

Klikk på, vi stopper noen animasjon som kjører, reversere animasjon retning dir og kaller update () – funksjonen:

addEventListener(‘click’, e => {
hvis(rID) stopAni();
dir *= -1;
update()
}, false);

Siden vi begynner med aktuelle framen stikkordregister f være 0, ønsker vi å gå i positiv retning, mot NF på den første klikk. Og siden vi er reversere retningen på hvert klikk, er det resultater som startverdi for den retning må være -1 nå, slik at det blir reversert til +1 på den første klikk.

Resultatet av alle de ovenfor kan sees i denne interaktive Pennen (fungerer bare i Firefox 53+).

Vi kan også bruke en annen alpha variabel for hvert stopp, akkurat som vi gjorde i tilfelle av stråler:

$m: gjenta-ikpsoniske-gradient(#000 .5*$pc, gjennomsiktig 0% $pc) utelate,
gjenta-radial-gradient(nærmest-side,
rgba(#000, var(–a0)), rgba(#000, var(–a1)) .5*$pr,
rgba(#000, var(–a2)) 0, rgba(#000, var(–a3)) $pr);

I JavaScript, vi har letthet og enkelhet ut timing funksjoner:

const TFN = {
‘letthet-i’: function(k, e = 1.675) {
tilbake Matematikk.pow(k, e)
},
‘letthet-out”: function(k, e = 1.675) {
avkastning 1 – Matematikk.pow(1 – k, e)
}
};

I oppdateringen () – funksjonen, den eneste forskjellen fra den første animerte demo er at vi ikke endre verdien for bare en CSS-variabel—vi har nå fire for å ta vare på: –a0, –a1, –a2, –a3. Vi gjør dette i en loop, ved hjelp av enkel-i funksjon for de på selv indekser og letthet-ut-funksjon for de andre. For de to første, og fremdriften er gitt ved f/NF, mens det for de to siste, fremgangen er gitt ved 1 – f/NF. Å sette alt dette i én formel, vi har:

(funksjonen update() {
f += dir;

for(var i = 0; i < 4; i++) {
la j = ~~(jeg/2);

dokumentet.kroppen.stil.setProperty(
`–en${i}`,
TFN[i%2 ? ‘letthet-ut’ : ‘letthet-i’](j + Matematikk.pow(-1, j)*f/NF).toFixed(2)
)
}

if(!(f%NF)) dir *= -1;

requestAnimationFrame(update)
})();

Resultatet kan sees nedenfor:

Flere ringvirkninger alpha-animasjoner (live demo, fungerer bare i Firefox 53+).

Akkurat som for ikpsoniske graderinger, kan vi også animere stopp posisjon mellom det ugjennomsiktige og gjennomsiktige delen av maskering radial gradient. Vi gjør dette ved å bruke en CSS-variabel –p for fremdriften av denne stopp-posisjonen:

$m: gjenta-ikpsoniske-gradient(#000 .5*$pc, gjennomsiktig 0% $pc) utelate,
gjenta-radial-gradient(nærmest-side,
#000, #000 calc(var(–p)*#{$pr}),
gjennomsiktig 0, gjennomsiktig $pr);

JavaScript er nesten identisk med den som for første alpha animasjon, bortsett fra at vi ikke vil oppdatere en alpha-en variabel, men en stopp fremgang –p variabel, og vi bruker en enkel-i-utsjekking slags funksjon:

/* samme som før */

funksjonen easeInOut(k) {
tilbake .5*(Matematikk.synd((k – .5)*Matematikk.PI) + 1)
};

(funksjonen update() {
f += dir;

dokumentet.kroppen.stil.setProperty(‘–p’, easeInOut(f/NF).toFixed(2));

/* samme som før */
})();

Vekslende ripple størrelse animasjon (live demo, fungerer bare i Firefox 53+).

Vi kan gjøre effekten mer interessant hvis vi legge til en gjennomsiktig stripe før ugjennomsiktig, og at vi også animere fremdriften av stopp-posisjonen –p0 hvor vi går fra dette gjennomsiktig stripe til en ugjennomsiktig:

$m: gjenta-ikpsoniske-gradient(#000 .5*$pc, gjennomsiktig 0% $pc) utelate,
gjenta-radial-gradient(nærmest-side,
gjennomsiktig, gjennomsiktig calc(var(–p0)*#{$pr}),
#000, #000 calc(var(–p1)*#{$pr}),
gjennomsiktig 0, gjennomsiktig $pr);

I JavaScript, har vi nå behov for å animere to CSS variabler: –p0 og –p1. Vi bruker en enkel timing funksjon for første og enkel ut for den andre. Vi har heller ikke reversere animasjon retning lenger:

const NF = 120,
TFN = {
‘letthet-i’: function(k, e = 1.675) {
tilbake Matematikk.pow(k, e)
},
‘letthet-out”: function(k, e = 1.675) {
avkastning 1 – Matematikk.pow(1 – k, e)
}
};

la f = 0;

(funksjonen update() {
f = (f + 1)%NF;

for(var i = 0; i < 2; i++)
dokumentet.kroppen.stil.setProperty(`–p${i}`, TFN[jeg ? “lette-ut’ : ‘letthet-i’](f/NF);

requestAnimationFrame(update)
})();

Dette gir oss et ganske interessant resultat:

Dobbeltrom ripple størrelse animasjon (live demo, fungerer bare i Firefox 53+).