Zaakceptowano wyzwanie front-endowe: Kostka CSS 3D
Opublikowany: 2022-03-10Lubisz wyzwania? Czy jesteś gotów podjąć się zadania, którego nigdy wcześniej nie spotkałeś i zrobić to w terminie? Co się stanie, jeśli podczas wykonywania zadania napotkasz problem, który wydaje się nierozwiązywalny? Chcę podzielić się moimi doświadczeniami z wykorzystania efektów CSS 3D po raz pierwszy w realnym projekcie i zainspirować Cię do podejmowania wyzwań.
To był zwyczajny dzień, kiedy napisał do mnie Eugene, menedżer w CreativePeople. Wysłał mi film i wyjaśnił, że rozwija koncepcję nowego projektu i zastanawiał się, czy mogę stworzyć coś takiego jak to, co jest na filmie.
Dalsze czytanie na SmashingMag:
- Beercamp: eksperyment z CSS 3D
- Tworzenie responsywnych kształtów za pomocą Clip-Path i wychodzenie z pudełka
- Zagrajmy z CSS z akceleracją sprzętową
Był to obiekt 3D (a dokładnie prostopadłościan), który obracał się wokół jednej z osi. Miałem już pewne doświadczenie w pracy z CSS 3D i w mojej głowie zaczęło formować się rozwiązanie. Wygooglowałem słowa kluczowe, takie jak „CSS 3D cube”, aby potwierdzić moje pomysły i odpowiedziałem Eugene'owi, że to możliwe.
Następne pytanie Eugene'a brzmiało: czy podejmę się projektu? Lubię trudne zadania, więc nie mogłem odmówić. Wtedy nie zdawałem sobie sprawy, w co się pakuję, ale byłem zbyt zdeterminowany.
Wyostrz swoje osie
Przypomnijmy sobie o osiach — nie o osiach wojennych, ale o liniach liczbowych, tych samych osiach, co w trójwymiarowym kartezjańskim układzie współrzędnych, którego uczyliśmy się w szkole. Jak mówi nam Wikipedia:
Kartezjański układ współrzędnych dla przestrzeni trójwymiarowej jest uporządkowaną trójką linii (osi), które są prostopadłe parami, mają pojedynczą jednostkę długości dla wszystkich trzech osi i mają orientację dla każdej osi.
Poniższy rysunek przedstawia orientację osi w przeglądarce internetowej.

Oś x jest pozioma, oś y jest pionowa, a oś z wydaje się wychodzić z ekranu w twoim kierunku. Zerowa wartość osi Z to płaszczyzna ekranu. Pamiętaj to.
Rozjaśnianie perspektywy
Do stworzenia obiektu 3D potrzebowałem elementu (nazwijmy to „sceną”) z perspektywą. Perspektywa to głębia sceny i zależy od rozmiarów zawartych w niej obiektów.
.scene { perspective: 800px; }
Jeśli perspektywa jest zbyt mała, obiekty mogą ulec zniekształceniu. Jeśli jest za duży, efekt 3D zostanie zredukowany do zera.
Zobacz Pen jqgMvL autorstwa Anny Selezniova (@askd) na CodePen.
Ponadto istnieje tylko jeden kąt widzenia dla wszystkich obiektów w scenie. A efekt 3D zależy od pozycji punktu widzenia.
Zobacz Pen oxKzKv autorstwa Anny Selezniova (@askd) na CodePen.
Jak więc obliczyć perspektywę? Odkryłem, że zależy to od osi obrotu. Dla osi x pasowałaby wartość wysokości pomnożona przez 4. Dla osi y byłaby to wartość szerokości pomnożona przez 4. Oto moja magiczna formuła:
const perspective = dimension * 4;
Rozważany ze wszystkich stron
Po określeniu perspektywy zacząłem tworzyć obiekt 3D. Wybrałem kostkę, ponieważ jest prosta i przewidywalna. Element sześcienny jest tworzony jako zwykły div, pozycjonowany względnie, ze zdefiniowaną szerokością i wysokością (powiedzmy 200px
). Przekształca się w obiekt 3D poprzez właściwość transform-style
o wartości preserve-3d
. Nakazuje przeglądarce renderowanie wszystkich zagnieżdżonych elementów zgodnie z zasadami świata 3D.
W moim przypadku sześcian ma sześć divów (lub „boków”), pozycjonowanych absolutnie. Nazwy klas odpowiadają początkowym pozycjom boków ( back
, left
, right
, top
, bottom
, front
). Oto znacznik:
<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>
Domyślnie wszystkie boki będą na jednej płaszczyźnie. Więc musiałem je zmienić. Oto jak to wygląda:
Zobacz Pen mPNwPx autorstwa Anny Selezniova (@askd) na CodePen.
A oto wynikowy CSS:
.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); }
Aby obrócić sześcian, ustawiam właściwość transform
elementu sześcianu na dowolny kąt obrotu wzdłuż osi x:
.cube { transform: rotateX(42deg); }
Pokonywanie niedociągnięć
Zgodnie z zadaniem miałem obrócić sześcian tylko wzdłuż osi x, więc nie potrzebowałem lewej ani prawej strony. Dodałem podpisy, aby dopasować się do początkowych pozycji pozostałych boków.
Zacząłem obracać kostkę i stwierdziłem, że napisy na dole i odwrocie są wyświetlane do góry nogami:
Zobacz Pen GZVvMR autorstwa Anny Selezniova (@askd) na CodePen.
Aby rozwiązać ten problem, obróciłem każdą z tych stron wzdłuż osi x o 180 stopni:

.back { transform: translateZ(-100px) rotateX(180deg); } .bottom { transform: translateY(100px) rotateX(270deg); }
Wyjście poza ekran
Zacząłem wypełniać strony prawdziwą treścią i od razu napotkałem kolejny problem. Musiałem wyświetlić 1-pikselowe kropkowane linie, ale były rozmazane i wyglądały źle.
Zobacz Pen VjeBPg autorstwa Anny Selezniova (@askd) na CodePen.
Wkrótce zdałem sobie sprawę, na czym polega problem. Pamiętacie tę reklamę telewizyjną 3D, w której obraz wychodzi poza ekran? Tak było z moją kostką.
Gdybyś mógł spojrzeć na sześcian z lewej lub prawej strony, zobaczyłbyś, że jego środek znajdował się na płaszczyźnie ekranu (zero na osi z), a przednia strona znajdowała się poza ekranem. Dlatego zwiększył się wizualnie i rozmazał.
Zobacz pióro WwVEMR autorstwa Anny Selezniova (@askd) na CodePen.
Aby rozwiązać ten problem, przesunąłem sześcian wzdłuż osi Z, aby wyrównać przednią stronę do płaszczyzny ekranu:
.cube { transform:translateZ(-100px); }
Oto kostka już prawie gotowa:
Zobacz Pen Xdvery autorstwa Anny Selezniova (@askd) na CodePen.
Korzystanie z magicznych liczb
Chyba zauważyłeś, że używam magicznej liczby 100
, aby przesuwać boki wzdłuż osi. Wartość 100
to dokładnie połowa wysokości mojej kostki testowej. Dlaczego połowa wysokości? Bo to byłby promień okręgu wpisanego w bok sześcianu (podobno jest to kwadrat).
const offset = dimension / 2;
Gdybym musiał obrócić trójkątny pryzmat, okrąg byłby wpisany w trójkąt. W tym przypadku wzór na przesunięcie byłby następujący:
const offset = dimension / (2 * Math.sqrt(3));
Zdmuchnąć kostkę
Aby uznać zadanie za wykonane, musiałem przetestować wynik w różnych przeglądarkach.
Obraz, który zobaczyłem w Internet Explorerze, pogrążył mnie w depresji. Aby zorientować się, o czym mówię, spójrz na poniższe demo w swojej ulubionej przeglądarce. Zmieniłem jedną właściwość, która powodowała, że kostka wyświetlała się niepoprawnie w Internet Explorerze. Jednak nie zaglądaj do kodu źródłowego, dopóki nie przeczytasz akapitu pod poniższym demo.
Zobacz Pen XKWMwV autorstwa Anny Selezniova (@askd) na CodePen.
Faktem jest, że Internet Explorer nie obsługuje właściwości transform-style
o wartości preserve-3d
. Dowiedziałem się o tym, patrząc na mój zaufany zasób Czy mogę użyć (patrz uwaga 1). W powyższym demie zamieniłem preserve-3d
na flat
. Czy już to wiedziałeś? Hej, mówiłem, żebyś nie podglądał!
Byłem zdenerwowany, ale nie zamierzałem się poddać. Problem jest okazją do nauczenia się czegoś nowego. Poza tym przyjąłem wyzwanie.
W poszukiwaniu punktu podparcia
Szukałem sposobu na stworzenie obiektu 3D bez użycia transform-style: preserve-3d
, iw końcu odkryłem użyteczną właściwość: transform-origin
. Określa centralny punkt transformacji elementu. Poniżej stworzyłem interaktywne demo, które pomoże Ci zrozumieć, jak to działa:
Zobacz Pen rLNmBp autorstwa Anny Selezniova (@askd) na CodePen.
Obrót 3D elementu w demie jest bardzo podobny do przedniej strony sześcianu, prawda? Tego właśnie użyłem.
(Przy okazji, czy próbowałeś zaznaczyć pole wyboru backface-visibility: hidden
podczas obrotu 3D? Ta właściwość służy do ukrywania tyłu elementu podczas transformacji 3D.)
Zaczynać od nowa
Zacząłem przerabiać kostkę. Nie musiałem wchodzić w interakcje ze sceną jako całością, więc usunąłem właściwość perspective
elementu scene
i dodałem ją do każdej transformacji 3D, dzięki czemu teraz każdy element przekształca się niezależnie. Ustawiłem również nowe właściwości dla każdej strony: transform-origin
z wartością równą położeniu środka sześcianu oraz backface-visibility: hidden
. Oto jak zmieniły się style:
.scene { } .cube { position: relative; width: 200px; height: 200px; transform: perspective(800px) translateZ(-100px); } .side { position: absolute; transform-origin: 50% 50% -100px; backface-visibility: hidden; }
Musiałem umieścić boki we właściwych miejscach. Ze względu na właściwość transform-origin
nie musiałem ich przesuwać, tylko obracać wokół osi. To jak magia! Zobaczmy jak to wygląda:
Zobacz Pen zBYwEm autorstwa Anny Selezniova (@askd) na CodePen.
Oto CSS dla rozmieszczenia boków:
.back { transform: perspective(800px) rotateY(180deg); } .top { transform: perspective(800px) rotateX(90deg); } .bottom { transform: perspective(800px) rotateX(-90deg); } .front { transform: perspective(800px); }
A tutaj możesz zobaczyć nową kostkę w akcji:
Zobacz Pen wWvdXd autorstwa Anny Selezniova (@askd) na CodePen.
Oddanie Cezarowi tego, co Cezara
Druga kostka wygląda i obraca się tak samo jak pierwsza. Ale w tym przypadku musisz indywidualnie przekształcić każdą stronę. Może to nie być łatwe, zwłaszcza jeśli chcesz kontrolować pośredni kąt obrotu.
Co więcej, jeśli otworzysz demo w Chrome, zobaczysz, że boki migają podczas obracania - bardzo frustrujące.
W końcu zastosowałem oba podejścia, używając prostego testu transform-style: preserve-3d
. Pierwsza kostka jest domyślna. Druga kostka jest przeznaczona dla Internet Explorera i przeglądarek, które nie obsługują preserve-3d
.
Korzystanie z potęgi matematyki
W końcu musiałem zaimplementować efekt paralaksy. Zwykle ten efekt reaguje na akcję użytkownika, niezależnie od tego, czy pozycja kursora myszy, czy pasek przewijania. W tym przypadku efekt zależy od kąta obrotu.
Zobacz Pen QENyqm autorstwa Anny Selezniova (@askd) na CodePen.
Więc jakie mam dane? Po pierwsze, miałem punkt początkowy i końcowy pozycji podpisu lub, mówiąc prościej, jego offset
w górę iw dół od środka boku. Po drugie miałem angle
obrotu sześcianu.
Spędziłem godziny próbując opracować formułę. Wówczas mnie olśniło. Oto, co przyszło mi do głowy:

Za pomocą sinusów i cosinusów z łatwością obliczyłem przesunięcie każdego podpisu w zależności od kąta. Oto formuły, które wymyśliłem:
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;
Podsumowując
Zadanie zostało zakończone, mogę cieszyć się rezultatem i podzielić się nim z wami. Przekonaj się, jak to działa. Użyj przewijania lub klawiszy strzałek, aby obrócić blok promocyjny. Spróbuj także pociągnąć czarny trójkąt po prawej stronie w górę iw dół, aby ręcznie kontrolować kąt obrotu (niestety ta funkcja nie działa w Internet Explorerze). Wygląda całkiem nieźle, prawda? A wydajność jest dość wysoka (około 60 klatek na sekundę).
Bardzo się cieszę, że uczestniczyłem w rozwoju tej strony. Zdobyłem przydatne doświadczenie w pracy z CSS 3D i odkryłem wiele interesujących właściwości. Co ważniejsze, nauczyłem się, że nigdy nie należy się poddawać; najprawdopodobniej znajdziesz sposób na wykonanie zadania.
Mam nadzieję, że spodobała Ci się moja historia i jesteś teraz gotowy na nowe wyzwania.