Zaakceptowano wyzwanie front-endowe: Kostka CSS 3D

Opublikowany: 2022-03-10
Szybkie podsumowanie ↬ Lubisz 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, manager 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.

Lubisz 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.

Więcej po skoku! Kontynuuj czytanie poniżej ↓

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.

Praworęczny trójwymiarowy kartezjański układ współrzędnych z osią Z skierowaną w stronę widza.
Praworęczny trójwymiarowy kartezjański układ współrzędnych z osią z skierowaną w stronę widza. (Zdjęcie: Wikimedia Commons) (Zobacz w dużej wersji)

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:

Wykresy funkcji sinus i cosinus
Wykresy funkcji sinus i cosinus (Zdjęcie: Wikimedia Commons) (Zobacz w dużej wersji)

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.