Ön Uç Mücadelesi Kabul Edildi: CSS 3D Küp
Yayınlanan: 2022-03-10Zorlukları sever misin? Daha önce hiç karşılaşmadığınız bir görevi üstlenmeye ve bunu bir süre içinde yapmaya istekli misiniz? Ya görevi yerine getirirken çözülemez görünen bir sorunla karşılaşırsanız? Gerçek bir projede ilk kez CSS 3D efektlerini kullanma deneyimimi paylaşmak ve zorlukların üstesinden gelmeniz için size ilham vermek istiyorum.
CreativePeople'da yönetici olan Eugene'in bana yazdığı sıradan bir gündü. Bana bir video gönderdi ve yeni bir proje için bir konsept geliştirdiğini ve videodaki gibi bir şey geliştirmemin mümkün olup olmadığını merak ettiğini açıkladı.
SmashingMag'de Daha Fazla Okuma :
- Beercamp: CSS 3D ile Bir Deney
- Clip-Path ile Duyarlı Şekiller Oluşturma ve Kutudan Çıkma
- Donanım Hızlandırılmış CSS ile Oynayalım
Eksenlerden birinin etrafında dönen bir 3B nesneydi (kesin olarak bir küboid). CSS 3D ile çalışma konusunda zaten biraz deneyimim vardı ve aklımda bir çözüm oluşmaya başladı. Fikirlerimi doğrulamak için Google'da "CSS 3D küp" gibi anahtar kelimeler aradım ve Eugene'e bunun mümkün olduğunu söyledim.
Eugene'in bir sonraki sorusu projeyi üstlenip üstlenmeyeceğimdi. Zor işleri severim, bu yüzden reddedemezdim. O zaman, neye bulaştığımın farkında değildim, ama azimliydim.
Eksenlerinizi Keskinleştirin
Kendimize eksenleri hatırlatalım - savaş eksenleri değil, okulda incelediğimiz üç boyutlu Kartezyen koordinat sistemindekiyle aynı eksenler olan sayı doğruları. Wikipedia'nın bize söylediği gibi:
Üç boyutlu bir uzay için Kartezyen koordinat sistemi, çiftler halinde dik olan, üç eksenin tümü için tek bir uzunluk birimine ve her eksen için bir yönelime sahip sıralı bir çizgi (eksen) üçlüsüdür.
Aşağıdaki resim, eksenlerin bir web tarayıcısında nasıl yönlendirildiğini göstermektedir.

X ekseni yatay, y ekseni dikey ve z ekseni ekrandan size doğru çıkıyormuş gibi görünüyor. Z ekseninin sıfır değeri ekranın düzlemidir. Hatırla bunu.
Perspektifin Temizlenmesi
Bir 3B nesne oluşturmak için, perspektifli bir öğeye (buna "sahne" diyelim) ihtiyacım vardı. Perspektif, sahnenin derinliğidir ve içerdiği nesnelerin boyutlarına bağlıdır.
.scene { perspective: 800px; }
Perspektif çok küçükse, nesneler bozulabilir. Çok büyükse, 3D efekti hiçbir şeye indirgenmez.
CodePen'de Anna Selezniova (@askd) tarafından kaleme alınan jqgMvL'ye bakın.
Ayrıca, sahnedeki tüm nesneler için yalnızca bir görüş açısı vardır. Ve 3D efekti, bakış açısının konumuna bağlıdır.
CodePen'de Anna Selezniova (@askd) tarafından kaleme alınan Pen oxKzKv'ye bakın.
Peki, perspektifi nasıl hesaplayacağız? Dönme eksenine bağlı olduğunu keşfettim. X ekseni için, 4 ile çarpılan yükseklik değeri sığacaktır. Y ekseni için genişlik değeri 4 ile çarpılır. İşte sihirli formülüm:
const perspective = dimension * 4;
Her Yönden İncelendi
Perspektifi belirledikten sonra 3 boyutlu bir obje oluşturmaya başladım. Basit ve tahmin edilebilir olduğu için bir küp seçtim. Bir küp öğesi, genişlik ve yükseklik tanımlı (örneğin, 200px
) görece konumlandırılmış normal bir div olarak oluşturulur. preserve-3d
değeriyle transform-style
özelliği aracılığıyla bir 3B nesneye dönüşür. Tarayıcıya tüm iç içe öğeleri 3B dünyanın kurallarına göre oluşturmasını söyler.
Benim durumumda, küpün kesinlikle konumlandırılmış altı div'i (veya "yanları") vardır. Sınıf adları, kenarların başlangıç konumlarına karşılık gelir ( back
, left
, right
, top
, bottom
, front
). İşte işaretleme:
<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>
Varsayılan olarak, tüm taraflar tek bir düzlemde olacaktır. Bu yüzden onları yeniden düzenlemem gerekiyordu. İşte nasıl göründüğü:
CodePen'de Anna Selezniova (@askd) tarafından kaleme alınan mPNwPx'e bakın.
Ve işte ortaya çıkan 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); }
Küpü döndürmek için, küp öğesindeki transform
özelliğini x ekseni boyunca keyfi bir dönüş açısına ayarladım:
.cube { transform: rotateX(42deg); }
Eksikliklerin Üstesinden Gelmek
Ödeve göre, küpü yalnızca x ekseni boyunca döndürmem gerekiyordu, bu yüzden sol veya sağ tarafa ihtiyacım yoktu. Kalan tarafların ilk konumlarıyla hizalanacak şekilde başlıklar ekledim.
Küpü döndürmeye başladım ve alt ve arka taraflardaki yazıların baş aşağı görüntülendiğini gördüm:
CodePen'de Anna Selezniova (@askd) tarafından kaleme alınan GZVvMR Kalemine bakın.
Bu sorunu çözmek için, bu kenarların her birini x ekseni boyunca 180 derece döndürdüm:

.back { transform: translateZ(-100px) rotateX(180deg); } .bottom { transform: translateY(100px) rotateX(270deg); }
Ekranın Ötesine Geçmek
Kenarları gerçek içerikle doldurmaya başladım ve hemen başka bir sorunla karşılaştım. 1 piksel noktalı çizgiler göstermem gerekiyordu, ancak bunlar bulanıktı ve kötü görünüyorlardı.
CodePen'de Anna Selezniova (@askd) tarafından kaleme alınan VjeBPg'ye bakın.
Sorunun ne olduğunu çok geçmeden anladım. Görüntünün ekranın ötesine uzandığı 3D TV reklamını hatırlıyor musunuz? Benim küpümde böyle bir şeydi.
Küpe sol veya sağ tarafından bakabilseydiniz, merkezinin ekran düzleminde (z ekseninde sıfır) ve ön tarafının ekranın ötesinde olduğunu görürdünüz. Bu nedenle görsel olarak arttı ve bulanıklaştı.
CodePen'de Anna Selezniova (@askd) tarafından kaleme alınan WwVEMR'ye bakın.
Bu sorunu çözmek için, ön tarafı ekranın düzlemine hizalamak için küpü z ekseni boyunca kaydırdım:
.cube { transform:translateZ(-100px); }
İşte küp şimdi, neredeyse hazır:
CodePen'de Anna Selezniova (@askd) tarafından kaleme alınan Xdvery Kalemine bakın.
Sihirli Sayıları Kullanma
Tarafları eksen boyunca kaydırmak için sihirli sayı 100
kullandığımı fark etmişsinizdir. 100
değeri, test küpümün yüksekliğinin tam olarak yarısıdır. Neden yüksekliğin yarısı? Çünkü bu, küpün (görünüşe göre bir kare olan) bir kenarına yazılan dairenin yarıçapı olacaktır.
const offset = dimension / 2;
Üçgen bir prizmayı döndürmem gerekirse, daire bir üçgen içine yazılır. Bu durumda, ofset formülü aşağıdaki gibi olacaktır:
const offset = dimension / (2 * Math.sqrt(3));
Küpü Üflemek
Görevin tamamlandığını düşünmek için sonucu farklı tarayıcılarda test etmem gerekti.
Internet Explorer'da gördüğüm resim beni depresyona soktu. Neden bahsettiğim hakkında bir fikir edinmek için favori tarayıcınızda aşağıdaki demoya bakın. Küpün Internet Explorer'da yanlış görüntülenmesine neden olan bir özelliği değiştirdim. Ancak aşağıdaki demonun altındaki paragrafı okumadan kaynak koduna göz atmayın.
CodePen'de Anna Selezniova (@askd) tarafından kaleme alınan XKWMwV Kalemine bakın.
Gerçek şu ki, Internet Explorer transform-style
özelliğini preserve-3d
değerine sahip desteklemiyor. Bunu güvenilir kaynağım Can I Use'a bakarak öğrendim (bkz. not 1). Yukarıdaki demoda, preserve-3d
flat
ile değiştirdim. Bunu zaten biliyor muydunuz? Hey, sana bakmamanı söylemiştim!
Üzüldüm ama vazgeçmeyecektim. Sorun, yeni bir şey öğrenmek için bir fırsattır. Ayrıca, meydan okumayı kabul etmiştim.
Dayanağı Aramak
transform-style: preserve-3d
kullanmadan bir 3B nesne oluşturmanın bir yolunu arıyordum ve sonunda yararlı bir özellik keşfettim: transform-origin
. Bir elemanın dönüşümünün merkez noktasını belirler. Aşağıda, nasıl çalıştığını anlamanıza yardımcı olacak etkileşimli bir demo oluşturdum:
CodePen'de Anna Selezniova (@askd) tarafından kaleme alınan rLNmBp'ye bakın.
Demodaki öğenin 3B dönüşü, bir küpün ön tarafına çok benzer, değil mi? Ben de öyle kullandım.
(Bu arada, 3B döndürme sırasında arka backface-visibility: hidden
onay kutusunu seçmeyi denediniz mi? Bu özellik, 3B dönüştürme sırasında öğenin arkasını gizlemek için kullanılır.)
Her Şeye Yeniden Başlamak
Küpü yeniden yapmaya başladım. Sahneyle bir bütün olarak etkileşime girmek zorunda değildim, bu yüzden scene
öğesinin perspective
özelliğini kaldırdım ve onu her 3B dönüşüme ekledim, böylece artık her öğe bağımsız olarak dönüşüyor. Ayrıca, her iki taraf için yeni özellikler belirledim: küpün merkezinin konumuna eşit bir değere sahip transform-origin
ve backface-visibility: hidden
. Stillerin nasıl değiştiği aşağıda açıklanmıştır:
.scene { } .cube { position: relative; width: 200px; height: 200px; transform: perspective(800px) translateZ(-100px); } .side { position: absolute; transform-origin: 50% 50% -100px; backface-visibility: hidden; }
Yanları doğru yerlere koymam gerekiyordu. transform-origin
özelliği nedeniyle, onları kaydırmaya gerek duymadım, sadece eksenler etrafında döndürdüm. Sihir gibi! Nasıl göründüğüne bir bakalım:
CodePen'de Anna Selezniova (@askd) tarafından kaleme alınan zBYwEm'e bakın.
Kenarların yerleştirilmesi için CSS:
.back { transform: perspective(800px) rotateY(180deg); } .top { transform: perspective(800px) rotateX(90deg); } .bottom { transform: perspective(800px) rotateX(-90deg); } .front { transform: perspective(800px); }
Ve burada yeni küpü çalışırken görebilirsiniz:
CodePen'de Anna Selezniova (@askd) tarafından kaleme alınan wWvdXd'ye bakın.
Sezar'ın Hakkını Sezar'a Vermek
İkinci küp, ilkiyle aynı görünüyor ve dönüyor. Ancak bu durumda, her bir tarafı ayrı ayrı dönüştürmeniz gerekir. Özellikle ara dönüş açısını kontrol etmek istiyorsanız bu çok kolay olmayabilir.
Ayrıca, demoyu Chrome'da açarsanız, döndürme sırasında kenarların yanıp söndüğünü göreceksiniz - çok sinir bozucu.
Sonunda, transform-style: preserve-3d
. İlk küp varsayılan olandır. İkinci küp, Internet Explorer ve preserve-3d
desteklemeyen tarayıcılar içindir.
Matematiğin Gücünü Kullanmak
Son olarak, bir paralaks efekti uygulamak zorunda kaldım. Genellikle, bu efekt, ister fare imlecinin ister kaydırma çubuğunun konumu olsun, kullanıcının eylemine yanıt verir. Bu durumda, etki dönme açısına bağlıdır.
CodePen'de Anna Selezniova (@askd) tarafından kaleme alınan QENyqm Kalemine bakın.
Peki, hangi verilere sahibim? İlk olarak, başlığın konumunun başlangıç ve bitiş noktalarına veya basitçe söylemek gerekirse, bir kenarın ortasından yukarı ve aşağı offset
sahiptim. İkincisi, küpün dönme angle
sahiptim.
Bir formül geliştirmek için saatler harcadım. Sonra aklıma geldi. İşte aklıma gelenler:

Sinüs ve kosinüs yardımıyla her bir başlığın açısına göre ofsetini kolayca hesapladım. İşte bulduğum formüller:
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;
Özetliyor
Ödev şimdi tamamlandı ve sonucun tadını çıkarabilir ve sizinle paylaşabilirim. Nasıl çalıştığını kendiniz görün. Promosyon bloğunu döndürmek için kaydırma veya ok tuşlarını kullanın. Ayrıca, dönüş açısını manuel olarak kontrol etmek için sağdaki siyah üçgeni yukarı ve aşağı doğru çekmeye çalışın (maalesef bu özellik Internet Explorer'da çalışmıyor). Oldukça iyi görünüyor, değil mi? Ve performans oldukça yüksektir (saniyede yaklaşık 60 kare).
Bu web sitesinin geliştirilmesine katıldığım için çok mutluyum. CSS 3D ile çalışırken faydalı bir deneyim kazandım ve birçok ilginç özellik keşfettim. Daha da önemlisi, asla pes etmemek gerektiğini öğrendim; büyük olasılıkla görevi başarmanın bir yolunu bulacaksınız.
Umarım hikayemi beğenmişsinizdir ve şimdi yeni zorluklara göğüs germeye hazırsınızdır.