Sfida front-end accettata: CSS 3D Cube

Pubblicato: 2022-03-10
Riassunto veloce ↬ Ti piacciono le sfide? Sei disposto ad affrontare un compito che non hai mai incontrato prima e farlo entro una scadenza? Cosa succede se, nell'esecuzione dell'attività, si riscontra un problema che sembra irrisolvibile? Voglio condividere la mia esperienza di utilizzo degli effetti CSS 3D per la prima volta in un progetto reale e ispirarti ad affrontare le sfide. Era un giorno qualunque quando Eugene, un manager di CreativePeople , mi scriveva. Mi ha inviato un video e mi ha spiegato che stava sviluppando un concetto per un nuovo progetto e si chiedeva se fosse possibile per me sviluppare qualcosa di simile a quello che c'era nel video.

Ti piacciono le sfide? Sei disposto ad affrontare un compito che non hai mai incontrato prima e farlo entro una scadenza? Cosa succede se, nell'esecuzione dell'attività, si riscontra un problema che sembra irrisolvibile? Voglio condividere la mia esperienza di utilizzo degli effetti CSS 3D per la prima volta in un progetto reale e ispirarti ad affrontare le sfide.

Era un giorno qualunque quando Eugene, un manager di CreativePeople, mi scriveva. Mi ha inviato un video e mi ha spiegato che stava sviluppando un concetto per un nuovo progetto e si chiedeva se fosse possibile per me sviluppare qualcosa di simile a quello che c'era nel video.

Ulteriori letture su SmashingMag:

  • Beercamp: un esperimento con CSS 3D
  • Creazione di forme reattive con Clip-Path e rottura fuori dagli schemi
  • Giochiamo con CSS con accelerazione hardware

Era un oggetto 3D (un cuboide, per la precisione) che ruotava attorno a uno degli assi. Avevo già una certa esperienza con il lavoro con CSS 3D e una soluzione ha iniziato a formarsi nella mia mente. Ho cercato su Google parole chiave come "CSS 3D cube" per confermare le mie idee e ho risposto a Eugene che era possibile.

Altro dopo il salto! Continua a leggere sotto ↓

La domanda successiva di Eugene era se avrei accettato il progetto? Mi piacciono i compiti difficili, quindi non potevo rifiutare. A quel tempo, non mi rendevo conto in cosa mi stavo cacciando, ma ero oltre determinato.

Affila le tue asce

Ricordiamoci degli assi, non degli assi di guerra, ma delle linee dei numeri, gli stessi assi del sistema di coordinate cartesiane tridimensionale che abbiamo studiato a scuola. Come ci dice Wikipedia:

Il sistema di coordinate cartesiane per uno spazio tridimensionale è una tripletta ordinata di linee (assi) che sono perpendicolari a coppie, hanno una singola unità di lunghezza per tutti e tre gli assi e hanno un orientamento per ciascun asse.

L'immagine seguente mostra come sono orientati gli assi in un browser web.

Un sistema di coordinate cartesiane tridimensionale destrorso con l'asse Z rivolto verso lo spettatore.
Un sistema di coordinate cartesiane tridimensionale destrorso con l'asse z rivolto verso lo spettatore. (Immagine: Wikimedia Commons) (Visualizza versione grande)

L'asse x è orizzontale, l'asse y è verticale e l'asse z sembra uscire dallo schermo verso di te. Il valore zero dell'asse z è il piano dello schermo. Ricorda questo.

Schiarire la prospettiva

Per creare un oggetto 3D, avevo bisogno di un elemento (chiamiamola “scena”) con una prospettiva. La prospettiva è la profondità della scena e dipende dalle dimensioni degli oggetti che contiene.

 .scene { perspective: 800px; }

Se la prospettiva è troppo piccola, gli oggetti potrebbero essere distorti. Se è troppo grande, l'effetto 3D sarà ridotto a zero.

Guarda la penna jqgMvL di Anna Selezniova (@askd) su CodePen.

Inoltre, c'è un solo angolo di visuale per tutti gli oggetti nella scena. E l'effetto 3D dipende dalla posizione del punto di vista.

Vedi la penna oxKzKv di Anna Selezniova (@askd) su CodePen.

Quindi, come calcoliamo la prospettiva? Ho scoperto che dipende dall'asse di rotazione. Per l'asse x, il valore dell'altezza moltiplicato per 4 si adatterebbe. Per l'asse y, sarebbe il valore della larghezza moltiplicato per 4. Ecco la mia formula magica:

 const perspective = dimension * 4;

Considerato da tutti i lati

Dopo aver determinato la prospettiva, ho iniziato a creare un oggetto 3D. Ho scelto un cubo perché è semplice e prevedibile. Un elemento cubo viene creato come un div regolare, relativamente posizionato, con la larghezza e l'altezza definite (diciamo, 200px ). Si trasforma in un oggetto 3D attraverso la proprietà transform-style con un valore di preserve-3d . Indica al browser di eseguire il rendering di tutti gli elementi nidificati secondo le regole del mondo 3D.

Nel mio caso, il cubo ha sei div (o “lati”), assolutamente posizionati. I nomi delle classi corrispondono alle posizioni iniziali dei lati ( back , left , right , top , bottom , front ). Ecco il markup:

 <div class="scene"> <div class="cube"> <div class="side back"></div> <div class="side left"></div> <div class="side right"></div> <div class="side top"></div> <div class="side bottom"></div> <div class="side front"></div> </div> </div>

Per impostazione predefinita, tutti i lati saranno su un piano. Quindi, dovevo riorganizzarli. Ecco come appare:

Guarda la penna mPNwPx di Anna Selezniova (@askd) su CodePen.

Ed ecco il CSS risultante:

 .cube { position:relative; width: 200px; height: 200px; transform-style: preserve-3d; } .side { position: absolute; width: 200px; height: 200px; } .back { transform: translateZ(-100px); } .left { transform: translateX(-100px) rotateY(90deg); } .right { transform: translateX(100px) rotateY(90deg); } .top { transform: translateY(-100px) rotateX(90deg); } .bottom { transform: translateY(100px) rotateX(90deg); } .front { transform: translateZ(100px); }

Per ruotare il cubo, ho impostato la proprietà di transform sull'elemento cubo su un angolo di rotazione arbitrario lungo l'asse x:

 .cube { transform: rotateX(42deg); }

Superare le carenze

Secondo il compito, dovevo ruotare il cubo solo lungo l'asse x, quindi non avevo bisogno del lato sinistro o destro. Ho aggiunto didascalie per allineare le posizioni iniziali dei lati rimanenti.

Ho iniziato a ruotare il cubo e ho scoperto che le didascalie sul fondo e sul retro erano visualizzate capovolte:

Guarda la penna GZVvMR di Anna Selezniova (@askd) su CodePen.

Per risolvere questo problema, ho ruotato ciascuno di questi lati lungo l'asse x di 180 gradi:

 .back { transform: translateZ(-100px) rotateX(180deg); } .bottom { transform: translateY(100px) rotateX(270deg); }

Andare oltre lo schermo

Ho iniziato a riempire i lati di contenuti reali e ho subito riscontrato un altro problema. Avevo bisogno di visualizzare linee tratteggiate da 1 pixel, ma erano sfocate e sembravano brutte.

Vedi la penna VjeBPg di Anna Selezniova (@askd) su CodePen.

Presto ho capito qual era il problema. Ricordi quella pubblicità televisiva in 3D in cui l'immagine si estende oltre lo schermo? Era qualcosa del genere con il mio cubo.

Se potessi guardare il cubo dal lato sinistro o destro, vedresti che il suo centro era sul piano dello schermo (zero sull'asse z) e che il lato anteriore era oltre lo schermo. Pertanto, è aumentato visivamente e sfocato.

Guarda la penna WwVEMR di Anna Selezniova (@askd) su CodePen.

Per risolvere questo problema, ho spostato il cubo lungo l'asse z per allineare il lato anteriore al piano dello schermo:

 .cube { transform:translateZ(-100px); }

Ecco ora il cubo, quasi pronto:

Guarda la Pen Xdvery di Anna Selezniova (@askd) su CodePen.

Usando i numeri magici

Immagino tu abbia notato che sto usando il numero magico 100 per spostare i lati lungo l'asse. Il valore 100 è esattamente la metà dell'altezza del mio cubo di prova. Perché metà dell'altezza? Perché quello sarebbe il raggio di un cerchio inscritto in un lato del cubo (che è un quadrato, a quanto pare).

 const offset = dimension / 2;

Se avessi bisogno di ruotare un prisma triangolare, il cerchio sarebbe inscritto in un triangolo. In questo caso, la formula per l'offset sarebbe la seguente:

 const offset = dimension / (2 * Math.sqrt(3));

Soffiando via il cubo

Per considerare completata l'attività, ho dovuto testare il risultato in diversi browser.

L'immagine che ho visto in Internet Explorer mi ha fatto precipitare in depressione. Per avere un'idea di cosa sto parlando, guarda la demo qui sotto nel tuo browser preferito. Ho modificato una proprietà che causava la visualizzazione errata del cubo in Internet Explorer. Tuttavia, non dare un'occhiata al codice sorgente prima di aver letto il paragrafo sotto la demo qui sotto.

Guarda la penna XKWMwV di Anna Selezniova (@askd) su CodePen.

Il fatto è che Internet Explorer non supporta la proprietà transform-style con un valore di preserve-3d . L'ho appreso guardando la mia fidata risorsa Posso usare (vedi nota 1). Nella demo sopra, ho sostituito preserve-3d con flat . Lo sapevi già? Ehi, ti avevo detto di non sbirciare!

Ero arrabbiato, ma non avevo intenzione di arrendermi. Un problema è un'opportunità per imparare qualcosa di nuovo. Inoltre, avevo accettato la sfida.

Alla ricerca del fulcro

Stavo cercando un modo per creare un oggetto 3D senza usare transform-style: preserve-3d , e alla fine ho scoperto una proprietà utile: transform-origin . Determina il punto centrale della trasformazione di un elemento. Ho creato una demo interattiva qui sotto, che ti aiuterà a capire come funziona:

Vedi la penna rLNmBp di Anna Selezniova (@askd) su CodePen.

La rotazione 3D dell'elemento nella demo è molto simile al lato anteriore di un cubo, vero? Questo è quello che ho usato.

(A proposito, hai provato a selezionare la casella di controllo backface-visibility: hidden durante la rotazione 3D? Questa proprietà viene utilizzata per nascondere la parte posteriore dell'elemento durante la trasformazione 3D.)

Ricominciare da capo

Ho iniziato a rifare il cubo. Non dovevo interagire con la scena nel suo insieme, quindi ho rimosso la proprietà perspective dell'elemento scene e l'ho aggiunta a ogni trasformazione 3D, in modo che ora ogni elemento si trasformi in modo indipendente. Inoltre, ho impostato nuove proprietà per ciascun lato: transform-origin con un valore uguale alla posizione del centro del cubo e backface-visibility: hidden . Ecco come sono cambiati gli stili:

 .scene { } .cube { position: relative; width: 200px; height: 200px; transform: perspective(800px) translateZ(-100px); } .side { position: absolute; transform-origin: 50% 50% -100px; backface-visibility: hidden; }

Ho dovuto mettere i lati al posto giusto. A causa della proprietà transform-origin , non avevo bisogno di spostarli, ma ruotarli solo attorno agli assi. È come per magia! Vediamo come appare:

Guarda la penna zBYwEm di Anna Selezniova (@askd) su CodePen.

Ecco il CSS per il posizionamento dei fianchi:

 .back { transform: perspective(800px) rotateY(180deg); } .top { transform: perspective(800px) rotateX(90deg); } .bottom { transform: perspective(800px) rotateX(-90deg); } .front { transform: perspective(800px); }

E qui puoi vedere il nuovo cubo in azione:

Guarda la penna wWvdXd di Anna Selezniova (@askd) su CodePen.

Restituire a Cesare ciò che è di Cesare

Il secondo cubo ha lo stesso aspetto e la rotazione del primo. Ma in questo caso, devi trasformare ogni lato individualmente. Questo potrebbe non essere molto facile, soprattutto se si desidera controllare l'angolo di rotazione intermedio.

Inoltre, se apri la demo in Chrome, vedrai che i lati lampeggiano durante la rotazione, molto frustrante.

Alla fine, ho applicato entrambi gli approcci usando il semplice test di transform-style: preserve-3d . Il primo cubo è quello predefinito. Il secondo cubo è per Internet Explorer e browser che non supportano preserve-3d .

Usando il potere della matematica

Infine, ho dovuto implementare un effetto di parallasse. Di solito, questo effetto risponde all'azione dell'utente, che si tratti della posizione del cursore del mouse o della barra di scorrimento. In questo caso, l'effetto dipende dall'angolo di rotazione.

Guarda la penna QENyqm di Anna Selezniova (@askd) su CodePen.

Allora, che dati ho? Innanzitutto, avevo i punti di inizio e fine della posizione della didascalia o, per dirla semplicemente, il suo offset su e giù dal centro di un lato. In secondo luogo, avevo l' angle di rotazione del cubo.

Ho passato ore a cercare di sviluppare una formula. Poi, mi è venuto in mente. Ecco cosa mi è venuto in mente:

Grafici delle funzioni seno e coseno
Grafici delle funzioni seno e coseno (Immagine: Wikimedia Commons) (Visualizza versione grande)

Con l'aiuto di seno e coseno, ho calcolato facilmente l'offset di ogni didascalia in base all'angolo. Ecco le formule che mi sono inventato:

 const front_offset = offset * sin(angle) * -1; const bottom_offset = offset * cos(angle); const back_offset = offset * sin(angle); const top_offset = offset * cos(angle) * -1;

Riassumendo

Il compito è ora completo e posso godermi il risultato e condividerlo con te. Guarda tu stesso come funziona. Usa i tasti di scorrimento o freccia per ruotare il blocco promozionale. Inoltre, prova a tirare su e giù il triangolo nero a destra per controllare manualmente l'angolo di rotazione (purtroppo, questa funzione non funziona in Internet Explorer). Sembra abbastanza buono, vero? E le prestazioni sono piuttosto elevate (circa 60 fotogrammi al secondo).

Sono molto felice di aver partecipato allo sviluppo di questo sito web. Ho acquisito un'utile esperienza nel lavorare con CSS 3D e ho scoperto molte proprietà interessanti. Ancora più importante, ho imparato che non bisogna mai arrendersi; molto probabilmente troverai un modo per portare a termine il compito.

Spero che la mia storia vi sia piaciuta e che ora siate pronti per affrontare nuove sfide.