Cursore immagine rotante infinito e circolare CSS

Nodo di origine: 1765846

I cursori di immagini (chiamati anche caroselli) sono ovunque. Ci sono molti trucchi CSS per creare lo slider comune dove le immagini scorrono da sinistra a destra (o viceversa). È lo stesso affare con le numerose librerie JavaScript là fuori che creano fantasiosi cursori con animazioni complesse. Non faremo nulla di tutto ciò in questo post.

Attraverso una piccola serie di articoli, esploreremo alcuni dispositivi di scorrimento CSS fantasiosi e non comuni. Se sei stanco di vedere sempre le solite ciabatte classiche, allora sei nel posto giusto!

Serie di cursori CSS

Per questo primo articolo, inizieremo con qualcosa che chiamo il "cursore circolare dell'immagine rotante":

Bello vero? analizziamo il codice!

Il codice HTML

Se hai seguito la mia serie di decorazioni di immagini fantasiose or Griglia CSS e forme personalizzate, allora sai che la mia prima regola è lavorare con il codice HTML più piccolo possibile. Mi sforzo sempre di trovare soluzioni CSS prima di ingombrare molto il mio codice

se altre cose.

La stessa regola si applica qui: il nostro codice non è altro che un elenco di immagini in un contenitore.

Diciamo che stiamo lavorando con quattro immagini:

Questo è tutto! Ora passiamo alla parte interessante del codice. Ma prima, ci immergeremo in questo per capire la logica di come funziona il nostro dispositivo di scorrimento.

Come funziona?

Ecco un video in cui rimuovo overflow: hidden dal CSS così possiamo capire meglio come si muovono le immagini:

È come se le nostre quattro immagini fossero posizionate su un grande cerchio che ruota in senso antiorario.

Tutte le immagini hanno la stessa dimensione (indicata da S nella figura). Nota il cerchio blu che è il cerchio che si interseca con il centro di tutte le immagini e ha un raggio (R). Avremo bisogno di questo valore in seguito per la nostra animazione. R è uguale a 0.707 * S. (Salterò la geometria che ci dà quell'equazione.)

Scriviamo un po' di CSS!

Useremo Griglia CSS per posizionare tutte le immagini nella stessa area una sopra l'altra:

.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;
}

Niente di troppo complesso finora. La parte difficile è l'animazione.

Abbiamo parlato della rotazione di un grande cerchio, ma in realtà ruoteremo ogni immagine individualmente creando l'illusione di un grande cerchio rotante. Quindi, definiamo un'animazione, me applicalo agli elementi dell'immagine:

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

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

Il trucco principale si basa su quella linea evidenziata. Per impostazione predefinita, il CSS transform-origin la proprietà è uguale a center (o 50% 50%) che fa ruotare l'immagine attorno al suo centro, ma non ne abbiamo bisogno per farlo. Abbiamo bisogno che l'immagine ruoti attorno al centro del file grande cerchio che contiene le nostre immagini da cui il nuovo valore for transform-origin.

Poiché R è uguale a 0.707 * S, possiamo dire che R è uguale a 70.7% della dimensione dell'immagine. Ecco una figura per illustrare come abbiamo ottenuto il 120.7% valore:

Eseguiamo l'animazione e vediamo cosa succede:

Lo so, lo so. Il risultato è lontano da quello che vogliamo, ma in realtà ci siamo molto vicini. Potrebbe sembrare che ci sia solo un'immagine lì, ma non dimenticare che abbiamo impilato tutte le immagini una sopra l'altra. Tutti ruotano contemporaneamente e solo l'immagine in alto è visibile. Ciò di cui abbiamo bisogno è ritardare l'animazione di ciascuna immagine per evitare questa sovrapposizione.

.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 */

Le cose stanno già migliorando!

Se nascondiamo l'overflow sul contenitore possiamo già vedere uno slider, ma aggiorneremo un po' l'animazione in modo che ogni immagine rimanga visibile per un breve periodo prima che si sposti.

Aggiorneremo i nostri fotogrammi chiave di animazione per fare proprio questo:

@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); }
}

Per ciascun 90deg (360deg/4, Dove 4 è il numero di immagini) aggiungeremo una piccola pausa. Ogni immagine rimarrà visibile per 5% della durata complessiva prima di passare a quella successiva (27%-22%, 52%-47%, eccetera.). vado ad aggiornare il animation-timing-function utilizzando un cubic-bezier() funzione per rendere l'animazione un po' più elaborata:

Ora il nostro cursore è perfetto! Beh, quasi perfetto perché ci manca ancora il tocco finale: il colorato bordo circolare che ruota attorno alle nostre immagini. Possiamo usare uno pseudo-elemento su .gallery involucro per realizzarlo:

.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);
}

Ho creato un cerchio con a gradiente conico ripetuto per lo sfondo durante l'utilizzo di a trucco di mascheramento che mostra solo l'area imbottita. Quindi applico ad esso la stessa animazione che abbiamo definito per le immagini.

Abbiamo chiuso! Abbiamo un bel cursore circolare:

Aggiungiamo altre immagini

Lavorare con quattro immagini va bene, ma sarebbe meglio se potessimo ridimensionarlo a qualsiasi numero di immagini. Dopotutto, questo è lo scopo di un dispositivo di scorrimento dell'immagine. Dovremmo essere in grado di considerare N immagini.

Per questo, renderemo il codice più generico introducendo Sass. Innanzitutto, definiamo una variabile per il numero di immagini ($n) e aggiorneremo ogni parte in cui abbiamo codificato il numero di immagini (4).

Partiamo dai ritardi:

.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 */

La formula per il ritardo è (1 - $i)*duration/$n, che ci dà il seguente ciclo Sass:

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

Possiamo rendere variabile anche la durata, se lo vogliamo davvero. Ma passiamo all'animazione:

@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); }
}

Semplifichiamolo per avere una visione migliore del modello:

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

Il passo tra ogni stato è uguale a 25% - che è 100%/4 - e aggiungiamo a -90deg angolo - che è -360deg/4. Ciò significa che possiamo invece scrivere il nostro ciclo in questo modo:

@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); }
}

Dal momento che ogni immagine prende 5% dell'animazione, cambiamo questo:

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

…con questo:

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

Essendo un sito serio, 5% è un valore arbitrario scelto per questo esempio. Possiamo anche renderlo una variabile per controllare per quanto tempo ciascuna immagine deve rimanere visibile. Lo salterò per semplicità, ma per i compiti, puoi provare a farlo e condividere la tua implementazione nei commenti!

@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); }
}

L'ultimo bit è aggiornare transform-origin. Avremo bisogno di alcuni trucchi geometrici. Qualunque sia il numero di immagini, la configurazione è sempre la stessa. Abbiamo le nostre immagini (piccoli cerchi) posizionate all'interno di un grande cerchio e dobbiamo trovare il valore del raggio, R.

Probabilmente non vuoi una noiosa spiegazione della geometria, quindi ecco come la troviamo R:

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

Se lo esprimiamo in percentuale, otteniamo:

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

…che significa il transform-origin il valore è uguale a:

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

Sono stati fatti! Abbiamo un dispositivo di scorrimento che funziona con qualsiasi numero di immagini!

Mettiamoci dentro nove immagini:

Aggiungi tutte le immagini che vuoi e aggiorna il file $n variabile con il numero totale di immagini.

Concludendo

Con alcuni accorgimenti usando le trasformazioni CSS e la geometria standard, abbiamo creato un bel dispositivo di scorrimento circolare che non richiede molto codice. La cosa interessante di questo dispositivo di scorrimento è che non abbiamo bisogno di preoccuparci di duplicare le immagini per mantenere l'animazione infinita poiché abbiamo un cerchio. Dopo una rotazione completa, torneremo alla prima immagine!

Timestamp:

Di più da Trucchi CSS