1 Element + 5 CSS-Egenskaper = Magi!

0
8

Låt oss säga att jag berättade för er att vi kan få de resultat som presenteras nedan med bara ett HTML-element och fem CSS-egenskaper för varje. Ingen SVG, inga bilder (med undantag för bakgrunden på rot är det bara att göra klart att våra ett HTML-element har några genomskinliga delar), nr JavaScript. Vad skulle du tror att det innebär?

De önskade resultaten.

Tja, denna artikel kommer att förklara precis hur man gör detta och sedan också visa hur man kan göra roliga saker genom att lägga till några animeringar.

CSS-ing Lutning Strålar

HTML är bara en <div>.

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

I CSS, vi måste ställa in måtten på denna del och vi måste ge den en bakgrund så att vi kan se det. Vi också göra det cirkulär med hjälp av border-radius:

.strålar {
bredd: 80vmin; height: 80vmin;
border-radius: 50%;
bakgrund: linear-gradient(#b53, #f90);
}

Och… vi har redan använt upp fyra av fem fastigheter för att få resultatet nedan:

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

Så vad är den femte? mask med ett upprepande-konisk-gradient () – värde!

Låt oss säga att vi vill ha 20 strålar. Detta innebär att vi behöver avsätta $p: 100%/20 i full cirkel för en ray och gapet efter det.

Dela skivan i-strålar och luckor (live).

Här kan vi hålla luckor i mellan strålar lika strålar (så det är .5*$s för antingen en strimma eller ett utrymme), men vi kan göra dem bredare eller smalare. Vi vill att en plötslig förändring efter den avslutande-stopp-position i ogenomskinlig del (ray), så att start-stopp-position för den transparenta delen (gap) bör vara lika med eller mindre än det. Så om avslutande-stopp-position för ray är .5*$p, då start-stopp-position för gap kan inte vara större. Men det kan vara mindre och som hjälper oss att hålla det enkelt, eftersom det innebär att vi kan helt enkelt noll.

Hur upprepa-konisk-gradient() fungerar (live).

$nr: 20; // antal strålar
$p: 100%/$nr; // procent av cirkeln som tilldelats en ray och gap efter

.strålar {
/* samma som innan */
mask: upprepa-konisk-gradient(#000 0% .5*$s, transparent 0% $s);
}

Observera att, till skillnad från för linjära och radiella gradienter, stopp-positioner för koniska gradienter kan inte vara unitless. De måste vara antingen procentsatser eller kantiga värden. Detta innebär att man använder något som transparenta 0 $p inte fungerar, vi behöver transparenta 0% $p (eller 0deg istället för 0%, det spelar ingen roll vilken vi väljer, det kan bara inte vara unitless).

Gradient strålar (demo, ingen Edge-stöd).

Det finns ett par saker att notera här när det kommer till stöd:

  • Edge inte har stöd för maskering på HTML-element på denna punkt, men detta är listade I Utveckling och en flagga för att det (som inte gör någonting för nu) har redan visat upp i ca:flaggor.
    Den Aktiverar CSS Maskering flaggan i Kanten.
  • kon-gradient() stöds endast direkt genom att Blinka webbläsare bakom Experimentell Plattform för Webben har flaggan (som kan aktiveras från chrome://flaggor eller opera://flaggor). Stöd kommer att Safari också, men tills det händer, Safari bygger fortfarande på polyfill, precis som Firefox.
    Den Experimentella webbplattform funktioner flagga som aktiveras i Chrome.
  • WebKit webbläsare behöver fortfarande -webkit – prefix för mask egenskaper för HTML-element. Du skulle tror att det är inga problem eftersom vi använder polyfill som förlitar sig på -prefix-gratis hur som helst, så om vi använder polyfill, vi behöver inkludera -prefix före det ändå. Tyvärr, det är lite mer komplicerat än så. Det beror på att -prefix-gratis-fungerar via funktionen för upptäckt, som misslyckas i detta fall eftersom alla webbläsare har stöd för mask unprefixed… på SVG-element! Men vi använder mask på ett HTML-element här, så vi är i en situation där WebKit webbläsare behöver -webkit – prefix, men -prefix-gratis kommer inte att lägga den. Så jag antar att det betyder att vi måste lägga till den manuellt:
    $nr: 20; // antal strålar
    $p: 100%/$nr; // procent av cirkeln som tilldelats en ray och gap efter
    $m-upprepa-konisk-gradient(#000 0% .5*$s, transparent 0% $s); // masken

    .strålar {
    /* samma som innan */
    -webkit-mask: $m;
    mask: $m;
    }

    Jag antar att vi kan också använda Autoprefixer, även om vi måste inkludera -prefix-gratis hur som helst, men använder bara för att det känns lite som att använda ett hagelgevär för att döda en fluga.

Lägger till i Animation

En cool sak om kon-gradient() som stöd inbyggt i Blink webbläsare är att vi kan använda CSS-variabler i dem (vi kan inte göra det när du använder polyfill). Och CSS variabler kan nu också vara animerad Blinka webbläsare med lite Houdini magi (vi behöver Experimentell Plattform för Webben har flaggan för att vara aktiverat för att, men vi måste också vara aktiverad för infödda kon-gradient () – stöd, så det bör inte vara ett problem).

För att förbereda vår kod för animation, vi ändra våra maskering gradient så att den använder variabeln alpha värden:

$m-upprepa-konisk-gradient(
rgba(#000, var (–)) 0% .5*$p,
rgba(#000, calc(1 – var (–))) 0% $s);

Vi därefter registrera alpha-en anpassad egenskap:

CSS.registerProperty({
namn: ‘–‘,
syntax: ‘<nummer>’,
initialValue: 1;
})

Och slutligen, vi lägger in en animering i CSS:

.strålar {
/* samma som innan */
animation: 2s linjär oändliga alternativ;
}

@keyframes en {{ –a: 0 } }

Detta ger oss följande resultat:

Ray alpha-animation (demo, fungerar bara i Blink webbläsare med Experimentell Plattform för Webben har flaggan aktiverad).

Meh. Inte ser det så bra. Vi kan dock göra saker mer intressant genom att använda flera alpha värden:

$m-upprepa-konisk-gradient(
rgba(#000, var(–a0)) 0%, rgba(#000, var(–a1)) .5*$p,
rgba(#000, var(–a2)) 0%, rgba(#000, var(–a3)) $s);

Nästa steg är att registrera var och en av dessa anpassade egenskaper:

för(låt i = 0; i < 4; i++) {
CSS.registerProperty({
namn: `–en${i}`,
syntax: ‘<nummer>’,
initialValue: 1 – ~~(i/2)
})
}

Och slutligen, att lägga till animationer i CSS:

.strålar {
/* samma som innan */
animation: 2s oändliga alternativ;
animation-namn: a0, a1, a2, a3.
animation-timing-funktion:
/* easings från easings.net */
cubic-bezier(.57, .05, .67, .19) /* easeInCubic */,
cubic-bezier(.21, .61, .35, 1); /* easeOutCubic */
}

@$jag från 0 till 4 {
@keyframes a#{$i} {{ –a#{$i}: #{golvet($i/2)} } }
}

Observera att eftersom vi är inställda värden till anpassade egenskaper vi behöver för att interpolera golv () – funktionen.

Flera ray alpha-animationer (demo, fungerar bara i Blink webbläsare med Experimentell Plattform för Webben har flaggan aktiverad).

Det ser nu lite mer intressant, men säkert kan vi göra bättre?

Låt oss prova att använda en CSS-variabel för stopp-position mellan ray och klyftan:

$m-upprepa-konisk-gradient(#000 0% var (- p), transparent 0% $s);

Vi därefter registrera denna variabel:

CSS.registerProperty({
namn: ‘- p’,
syntax: ‘<procentsats>’,
initialValue: ‘0%’
})

Och vi animera det från CSS med hjälp av en keyframe animation:

.strålar {
/* samma som innan */
animation: p .5s linjär oändliga alternativ
}

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

Resultatet är mer intressant i det här fallet:

Växlande ray storlek animation (demo, fungerar bara i Blink webbläsare med Experimentell Plattform för Webben har flaggan aktiverad).

Men vi kan fortfarande krydda upp det lite mer genom att vända det hela horisontellt mellan varje iteration så att det är alltid vänt för det omvända dem. Detta innebär inte vänt när –p går från 0% till $p och vänt när –p går tillbaka från $p till 0%.

Hur vi vända en del horisontellt är genom att tillämpa en transform: scalex(-1) till det. Eftersom vi vill att de här flip tillämpas vid slutet av den första iterationen och sedan tas bort i slutet av den andra (omvänd), vi använder det i en keyframe animation som väl i och en med ett steg() tidpunkten funktion och dubbla animation-varaktighet.

$t: .5s;

.strålar {
/* samma som innan */
animation: p $t linjär oändliga suppleant,
s 2*$t steg(1) oändligt;
}

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

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

Nu har vi äntligen ett resultat som faktiskt ser ganska cool ut:

Växlande ray storlek animation med horisontella flip mellan iterationer (demo, fungerar bara i Blink webbläsare med Experimentell Plattform för Webben har flaggan aktiverad).

CSS-ing Lutning Strålar och Vågor

För att få strålar och vågor resultat, behöver vi lägga till en andra lutning för att masken, den här gången ett upprepande-radial gradient().

Hur upprepa-radial gradient() fungerar (live).

$nr: 20;
$p: 100%/$nr;
$stop-lista: #000 0% .5*$s, transparent 0% $p;
$m-upprepa-konisk-gradient($stop-lista),
upprepa-radial gradient(närmast sida, $stop-listan);

.strålar-ringar {
/* samma som innan */
mask: $m;
}

Tyvärr, med flera stopp positioner fungerar bara Blinka webbläsare med samma Experimentella Web-Plattform och har flaggan aktiverad. Och medan kon-gradient() polyfill täcker detta för att upprepa-konisk-gradient() del i webbläsare som stöder CSS maskering på HTML-element men inte stödja koniska övertoningar direkt (Firefox, Safari, webbläsare utan att Blinka flaggan aktiverad), inget löser problem för att upprepa-radial gradient() del i dessa webbläsare.

Detta innebär att vi är tvungna att ha vissa upprepningar i vår kod:

$nr: 20;
$p: 100%/$nr;
$stop-lista: #000, #000 .5*$s, transparent 0%, transparent $p;
$m-upprepa-konisk-gradient($stop-lista),
upprepa-radial gradient(närmast sida, $stop-listan);

.strålar-ringar {
/* samma som innan */
mask: $m;
}

Vi är givetvis att komma närmare, men vi är inte riktigt där ännu:

Mellanhand resultat med två mask lager (demo, ingen Edge-stöd).

För att få det resultat vi vill, vi måste använda mask-komposit egendom och ställa in den för att utesluta:

$m-upprepa-konisk-gradient($stop-listan) utesluta,
upprepa-radial gradient(närmast sida, $stop-listan);

Observera att mask-komposit stöds endast i Firefox 53+ för nu, även om Kanten ska gå med i när det slutligen stöder CSS maskering på HTML-element.

XOR strålar och vågor (demo, Firefox 53+).

Om du tror att det ser ut som om den strålar och klyftorna mellan de strålar är inte lika, du har rätt. Detta är på grund av att en polyfill fråga.

Lägger till i Animation

Eftersom mask-komposit bara fungerar i Firefox nu och Firefox stödjer ännu inte kon-gradient() inbyggt, vi kan inte sätta CSS variabler inuti upprepa-konisk-gradient() (eftersom Firefox fortfarande faller tillbaka på den polyfill för det och polyfill inte har stöd för CSS användning av variabler). Men vi kan lägga dem i den upprepande-radial gradient() och även om vi inte kan animera dem med CSS keyframe animationer, vi kan göra så med JavaScript!

Eftersom vi är nu att sätta CSS variabler inuti upprepa-radial gradient(), men inte inne på att upprepa-konisk-gradient() (som XOR effekt endast fungerar via mask-composite, som stöds bara i Firefox nu och Firefox inte har stöd för koniska övertoningar direkt, så det faller tillbaka på den polyfill, som inte stöder CSS användning av variabler), vi kan inte använda samma $stop-listan för både lutning lager av vår mask längre.

Men om vi skriva om vår mask utan en gemensam $stop-lista i alla fall, vi kan ta detta tillfälle i akt att använda olika stopp-positioner för de två gradienter:

// för koniska gradient
$nc: 20;
$pc: 100%/$nc;
// för radiell övertoning
$nr: 10;
sek pr: 100%/$nr;

CSS-variabel vi animera är en alpha-en och en, precis som för första animation i strålar fall. Vi också införa –c0 och –c1 variabler för här kan vi inte ha flera poster per sluta och vi vill undvika upprepning så mycket som möjligt:

$m-upprepa-konisk-gradient(#000 .5*$pc, transparent 0% $pc) utesluta,
upprepa-radial gradient(närmast sida,
var(–c0), var(–c0) .5*$pr,
var(–c1) 0, var(–c1) sek pr);

body {
–a: 0;
/* layout, bakgrunder och andra irrelevanta saker */
}

.xor {
/* samma som innan */
–c0: #{rgba(#000, var (–))};
–c1: #{rgba(#000, calc(1 – var (–)))};
mask: $m;
}

Alpha variabel –en är den vi animera fram och tillbaka (från 0 till 1 och sedan tillbaka till 0 igen) med lite vanilj JavaScript. Vi börjar med att ange totalt antal bilder NF animation som händer över en aktuell ram index f och en aktuell animation riktning dir:

const NF = 50;

låt f = 0, dir = 1;

Inom en update() funktion, vi uppdaterar den aktuella bildrutan index f och sedan satte vi igång med den nuvarande utvecklingen värde (f/NF) till den aktuella alpha-en. Om f har nått antingen 0 av NF, vi ändra riktning. Då update () – funktionen blir kallad igen på nästa uppdatering.

(function update() {
f += dir;

dokumentet.organ.style.setProperty (‘–‘, (f/NF).toFixed(2));

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

requestAnimationFrame(uppdatering)
})();

Och det är allt för JavaScript! Vi har nu en animerad resultat:

Ripple alpha animation, linjär (live demo, bara fungerar i Firefox 53+).

Detta är en linjär animation, alfa-värde –en vara inställd till de framsteg som f/NF. Men vi kan ändra tidpunkten funktionen till något annat, som förklaras i en tidigare artikel skrev jag om att emulera CSS timing funktioner med JavaScript.

Till exempel, om vi vill ha en lätthet-i form av timing funktion, som vi på alpha-värdet för att easeIn(f/NF) istället för att bara f/NF, där vi har som easeIn() är:

funktion easeIn(k, e = 1.675) {
tillbaka Matematik.pow(k, e)
}

Resultatet vid användning av en enkel-och timing funktion kan ses i denna Penna (fungerar endast i Firefox 53+). Om du är intresserad av hur vi fått det att fungera, det är allt förklarat i tidigare länkad artikel om timing funktioner.

Exakt samma tillvägagångssätt som fungerar för easeOut() eller easeInOut():

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

funktion easeInOut(k) {
tillbaka .5*(Matematik.synd((k – .5)*Matematik.PI) + 1)
}

Eftersom vi använder JavaScript hur som helst, vi kan göra det hela interaktiva, så att animeringen sker endast på klicka/peka på, till exempel.

För att göra detta lägger vi till en begäran ID-variabel (rID), vilket är ursprungligen null, men sedan tar det värde som returneras av requestAnimationFrame() update() funktion. Detta gör det möjligt för oss att stoppa animeringen med en stopAni() funktion, närhelst vi vill:

/* samma som innan */

låt rID = null;

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

function update() {
/* samma som innan */

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

rID = requestAnimationFrame(uppdatering)
};

På klicka, vi stannar en animation som kan köras i omvänd animation riktning dir och kallar update() funktion:

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

Eftersom vi börjar med den aktuella bildrutan index f som 0, vi vill gå i positiv riktning, mot NF på det första klicket. Och eftersom vi är vända riktning på varje klick, det leder till att det ursprungliga värdet för riktningen måste -1 nu så att det blir omvänd till +1 på det första klicket.

Resultatet av allt detta kan ses i denna interaktiva Pennan (endast arbetar i Firefox 53+).

Vi skulle också kunna använda ett annat alpha variabel för varje stopp, precis som vi gjorde i fallet med strålar:

$m-upprepa-konisk-gradient(#000 .5*$pc, transparent 0% $pc) utesluta,
upprepa-radial gradient(närmast sida,
rgba(#000, var(–a0)), rgba(#000, var(–a1)) .5*$pr,
rgba(#000, var(–a2)) 0, rgba(#000, var(–a3)) $pr);

I JavaScript, vi har enkel och lätt-out-tiden funktioner:

const TFN = {
‘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)
}
};

I update() funktion, den enda skillnaden från de första animerade demo är att vi inte ändra värdet på bara en CSS-variabel—vi har nu fyra för att ta hand om: –a0, –a1, –a2, –a3. Detta gör vi inom en slinga, med enkel funktion för dem på jämna index och lätt-out ” – funktion för andra. För de två första, de framsteg som ges av f/NF, medan det för de två sista, de framsteg som ges av 1 – f/NF. Att sätta allt detta i en enda formel, vi har:

(function update() {
f += dir;

for(var i = 0; i < 4; i++) {
låt j = ~~(i/2);

dokumentet.organ.style.setProperty(
`–en${i}`,
TFN[i%2 ? ‘enkel’ : ‘enkel’](j + Matematik.pow(-1, j)*f/NF).toFixed(2)
)
}

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

requestAnimationFrame(uppdatering)
})();

Resultatet kan ses nedan:

Flera rippel alpha-animationer (live demo, bara fungerar i Firefox 53+).

Precis som för koniska gradienter, vi kan också animera-stopp-position mellan den opaka och transparenta delen av maskering radiell övertoning. För att göra detta använder vi en CSS-variabel –p för utvecklingen av denna stop-läge:

$m-upprepa-konisk-gradient(#000 .5*$pc, transparent 0% $pc) utesluta,
upprepa-radial gradient(närmast sida,
#000, #000 calc(var (- p)*#{$pr}),
transparent 0, transparent sek pr);

JavaScript är nästan densamma som för den första alfa-animation, förutom att vi inte uppdaterar en alpha-en variabel, men ett stopp framsteg –p rörliga och kan vi använda en lätthet-i-out typ av funktion:

/* samma som innan */

funktion easeInOut(k) {
tillbaka .5*(Matematik.synd((k – .5)*Matematik.PI) + 1)
};

(function update() {
f += dir;

dokumentet.organ.style.setProperty (‘- p’, easeInOut(f/NF).toFixed(2));

/* samma som innan */
})();

Växlande rippel storlek animation (live demo, bara fungerar i Firefox 53+).

Vi kan göra effekten mer intressant om vi lägger till en transparent band innan de ogenomskinliga, och vi också att animera framsteg i stop-läge-p0 där vi går från denna öppna strip till en ogenomskinlig:

$m-upprepa-konisk-gradient(#000 .5*$pc, transparent 0% $pc) utesluta,
upprepa-radial gradient(närmast sida,
öppet, transparent calc(var (p0–)*#{$pr}),
#000, #000 calc(var(–p1)*#{$pr}),
transparent 0, transparent sek pr);

I JavaScript, så har vi nu behov av att animera två CSS-variabler: p0 –och –p1. Vi använder en lätthet-i tidtagningen under den första och en lätthet ut för den andra. Vi behöver inte vända animation riktning längre:

const NF = 120,
TFN = {
‘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)
}
};

låt f = 0;

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

for(var i = 0; i < 2; i++)
dokumentet.organ.style.setProperty(`–p${i}`, TFN[jag ? ‘enkel’ : ‘enkel’](f/NF);

requestAnimationFrame(uppdatering)
})();

Detta ger oss en ganska intressant resultat:

Dubbel rippel storlek animation (live demo, bara fungerar i Firefox 53+).