Glisor de imagine CSS infinit și circular rotativ

Nodul sursă: 1765846

Glisoarele de imagine (numite și carusele) sunt peste tot. Sunt o mulțime de trucuri CSS pentru a crea glisorul comun unde imaginile alunecă de la stânga la dreapta (sau invers). Este aceeași afacere cu numeroasele biblioteci JavaScript de acolo care creează glisoare fanteziste cu animații complexe. Nu vom face nimic din toate astea în această postare.

Printr-o mică serie de articole, vom explora câteva glisoare CSS fanteziste și neobișnuite. Dacă te-ai săturat să vezi aceleași glisoare clasice, atunci ești în locul potrivit!

Seria CSS Sliders

Pentru acest prim articol, vom începe cu ceva pe care eu îl numesc „glisor de imagine circulară rotativă”:

Cool nu? hai sa disecam codul!

Marcajul HTML

Dacă ai urmărit seria mea de decorațiuni de imagine fanteziste or Grilă CSS și forme personalizate, atunci știi că prima mea regulă este să lucrez cu cel mai mic HTML posibil. Întotdeauna încerc din greu să găsesc soluții CSS înainte de a-mi aglomera codul cu multe

s și alte chestii.

Aceeași regulă se aplică aici - codul nostru nu este altceva decât o listă de imagini într-un container.

Să presupunem că lucrăm cu patru imagini:

Asta e! Acum să trecem la partea interesantă a codului. Dar mai întâi, ne vom scufunda în asta pentru a înțelege logica modului în care funcționează glisorul nostru.

Cum functioneaza?

Iată un videoclip în care îl elimin overflow: hidden din CSS, astfel încât să putem înțelege mai bine cum se mișcă imaginile:

Este ca și cum cele patru imagini ale noastre sunt plasate pe un cerc mare care se rotește în sens invers acelor de ceasornic.

Toate imaginile au aceeași dimensiune (notate cu S în figură). Observați cercul albastru care este cercul care se intersectează cu centrul tuturor imaginilor și are o rază (R). Vom avea nevoie de această valoare mai târziu pentru animația noastră. R este egal cu 0.707 * S. (Voi sări peste geometria care ne oferă acea ecuație.)

Să scriem niște CSS!

Vom folosi Grila CSS pentru a plasa toate imaginile în aceeași zonă una deasupra celeilalte:

.gallery  {
  --s: 280px; /* control the size */

  display: grid;
  width: var(--s);
  aspect-ratio: 1;
  padding: calc(var(--s) / 20); /* we will see the utility of this later */
  border-radius: 50%;
}
.gallery > img {
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}

Nimic prea complex până acum. Partea dificilă este animația.

Am vorbit despre rotirea unui cerc mare, dar, în realitate, vom roti fiecare imagine individual, creând iluzia unui cerc mare rotativ. Deci, să definim o animație, mși aplicați-l elementelor imaginii:

.gallery > img {
  /* same as before */
  animation: m 8s infinite linear;
  transform-origin: 50% 120.7%;
}

@keyframes m {
  100% { transform: rotate(-360deg); }
}

Trucul principal se bazează pe acea linie evidențiată. În mod implicit, CSS transform-origin proprietatea este egală cu center (Sau 50% 50%) care face ca imaginea să se rotească în jurul centrului său, dar nu avem nevoie de ea pentru a face asta. Avem nevoie ca imaginea să se rotească în jurul centrului cerc mare care contine imaginile noastre de unde noua valoare pentru transform-origin.

Deoarece R este egal cu 0.707 * S, putem spune că R este egal cu 70.7% de dimensiunea imaginii. Iată o cifră pentru a ilustra cum am obținut 120.7% valoare:

Să rulăm animația și să vedem ce se întâmplă:

Știu, știu. Rezultatul este departe de ceea ce ne dorim, dar în realitate suntem foarte apropiați. Poate părea că există o singură imagine acolo, dar nu uitați că am stivuit toate imaginile una peste alta. Toate se rotesc în același timp și doar imaginea de sus este vizibilă. Ceea ce avem nevoie este să întârziem animația fiecărei imagini pentru a evita această suprapunere.

.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 8s / 4 */
.gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 8s / 4 */
.gallery > img:nth-child(4) { animation-delay: -6s; } /* -3 * 8s / 4 */

Lucrurile sunt deja din ce în ce mai bune!

Dacă ascundem preaplinul pe container, putem vedea deja un glisor, dar vom actualiza puțin animația, astfel încât fiecare imagine să rămână vizibilă pentru o perioadă scurtă înainte de a se deplasa.

Vom actualiza cadrele cheie de animație pentru a face exact asta:

@keyframes m {
  0%, 3% { transform: rotate(0); }
  22%, 27% { transform: rotate(-90deg); }
  47%, 52% { transform: rotate(-180deg); }
  72%, 77% { transform: rotate(-270deg); }
  98%, 100% { transform: rotate(-360deg); }
}

Pentru fiecare 90deg (360deg/4, În cazul în care 4 este numărul de imagini) vom adăuga o mică pauză. Fiecare imagine va rămâne vizibilă pentru 5% din durata totală înainte de a trece la următoarea (27%-22%, 52%-47%, etc.). Am de gând să actualizez animation-timing-function folosind un cubic-bezier() funcție pentru a face animația un pic mai elegantă:

Acum cursorul nostru este perfect! Ei bine, aproape perfect pentru că încă ne lipsește atingerea finală: chenarul circular colorat care se rotește în jurul imaginilor noastre. Putem folosi un pseudo-element pe .gallery ambalaj pentru a o face:

.gallery {
  padding: calc(var(--s) / 20); /* the padding is needed here */
  position: relative;
}
.gallery::after {
  content: "";
  position: absolute;
  inset: 0;
  padding: inherit; /* Inherits the same padding */
  border-radius: 50%;
  background: repeating-conic-gradient(#789048 0 30deg, #DFBA69 0 60deg);
  mask: 
    linear-gradient(#fff 0 0) content-box, 
    linear-gradient(#fff 0 0);
  mask-composite: exclude;
}
.gallery::after,
.gallery >img {
  animation: m 8s infinite cubic-bezier(.5, -0.2, .5, 1.2);
}

Am creat un cerc cu a gradient conic repetat pentru fundal în timpul utilizării a truc de mascare care arată doar zona căptușită. Apoi îi aplic aceeași animație pe care am definit-o pentru imagini.

Am terminat! Avem un glisor circular cool:

Să adăugăm mai multe imagini

Lucrul cu patru imagini este bun, dar ar fi mai bine dacă îl putem scala la orice număr de imagini. La urma urmei, acesta este scopul unui glisor de imagine. Ar trebui să putem lua în considerare N imagini.

Pentru aceasta, vom face codul mai generic prin introducerea Sass. Mai întâi, definim o variabilă pentru numărul de imagini ($n) și vom actualiza fiecare parte în care am codificat numărul de imagini (4).

Să începem cu întârzierile:

.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 8s / 4 */
.gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 8s / 4 */
.gallery > img:nth-child(4) { animation-delay: -6s; } /* -3 * 8s / 4 */

Formula pentru întârziere este (1 - $i)*duration/$n, care ne oferă următoarea buclă Sass:

@for $i from 2 to ($n + 1) {
  .gallery > img:nth-child(#{$i}) {
    animation-delay: calc(#{(1 - $i) / $n} * 8s);
  }
}

Putem face și durata o variabilă dacă vrem cu adevărat. Dar să trecem la animație:

@keyframes m {
  0%, 3% { transform: rotate(0); }
  22%, 27% { transform: rotate(-90deg); }
  47%, 52% { transform: rotate(-180deg); }
  72%, 77% { transform: rotate(-270deg); }
  98%, 100% {transform: rotate(-360deg); }
}

Să o simplificăm pentru a obține o imagine mai bună a modelului:

@keyframes m {
  0% { transform: rotate(0); }
  25% { transform: rotate(-90deg); }
  50% { transform: rotate(-180deg); }
  75% { transform: rotate(-270deg); }
  100% { transform: rotate(-360deg); }
}

Pasul dintre fiecare stare este egal cu 25% - care este 100%/4 — și adăugăm a -90deg unghi — care este -360deg/4. Asta înseamnă că ne putem scrie bucla astfel:

@keyframes m {
  0% { transform: rotate(0); }
  @for $i from 1 to $n {
    #{($i / $n) * 100}% { transform: rotate(#{($i / $n) * -360}deg); }  
  }
  100% { transform: rotate(-360deg); }
}

Din moment ce fiecare imagine ia 5% din animație, schimbăm asta:

#{($i / $n) * 100}%

…cu asta:

#{($i / $n) * 100 - 2}%, #{($i / $n) * 100 + 3}%

Ar trebui remarcat faptul că 5% este o valoare arbitrară pe care o aleg pentru acest exemplu. De asemenea, putem face din aceasta o variabilă pentru a controla cât timp trebuie să rămână vizibilă fiecare imagine. Voi sări peste asta de dragul simplității, dar pentru teme, puteți încerca să o faceți și să vă împărtășiți implementarea în comentarii!

@keyframes m {
  0%,3% { transform: rotate(0); }
  @for $i from 1 to $n {
    #{($i / $n) * 100 - 2}%, #{($i / $n) * 100 + 3}% { transform: rotate(#{($i / $n) * -360}deg); }  
  }
  98%,100% { transform: rotate(-360deg); }
}

Ultimul bit este actualizarea transform-origin. Vom avea nevoie de niște trucuri de geometrie. Indiferent de numărul de imagini, configurația este întotdeauna aceeași. Avem imaginile noastre (cercuri mici) plasate în interiorul unui cerc mare și trebuie să găsim valoarea razei, R.

Probabil că nu doriți o explicație plictisitoare despre geometrie, așa că iată cum găsim R:

R = S / (2 * sin(180deg / N))

Dacă exprimăm asta ca procent, asta ne dă:

R = 100% / (2 * sin(180deg / N)) = 50% / sin(180deg / N)

… ceea ce înseamnă transform-origin valoarea este egala cu:

transform-origin: 50% (50% / math.sin(180deg / $n) + 50%);

Au fost efectuate! Avem un glisor care funcționează cu orice număr de imagini!

Să aruncăm nouă imagini acolo:

Adăugați câte imagini doriți și actualizați $n variabilă cu numărul total de imagini.

La finalul

Cu câteva trucuri folosind transformări CSS și geometrie standard, am creat un glisor circular drăguț care nu necesită mult cod. Ce este tare la acest glisor este că nu trebuie să ne obosim să dublăm imaginile pentru a păstra animația infinită, deoarece avem un cerc. După o rotație completă, vom reveni la prima imagine!

Timestamp-ul:

Mai mult de la CSS Trucuri