ยอมรับความท้าทายส่วนหน้าแล้ว: CSS 3D Cube
เผยแพร่แล้ว: 2022-03-10คุณชอบความท้าทายหรือไม่? คุณยินดีที่จะทำงานที่คุณไม่เคยเจอมาก่อนและทำมันภายใต้เส้นตายหรือไม่? จะเป็นอย่างไรหากในการปฏิบัติงาน คุณพบปัญหาที่ดูเหมือนแก้ไม่ได้ ฉันต้องการ แบ่งปันประสบการณ์ของฉันในการใช้เอฟเฟกต์ CSS 3D เป็นครั้งแรกในโครงการจริง และเพื่อเป็นแรงบันดาลใจให้คุณรับมือกับความท้าทาย
เป็นวันธรรมดาที่ Eugene ผู้จัดการของ CreativePeople เขียนถึงฉัน เขาส่งวิดีโอมาให้ฉันและอธิบายว่าเขากำลังพัฒนาแนวคิดสำหรับโปรเจ็กต์ใหม่ และสงสัยว่าเป็นไปได้ไหมที่ฉันจะพัฒนาบางอย่างที่เหมือนกับในวิดีโอ
อ่านเพิ่มเติม เกี่ยวกับ SmashingMag:
- Beercamp: การทดลองกับ CSS 3D
- การสร้างรูปร่างที่ตอบสนองด้วยคลิปพาธและการแยกส่วนออกจากกล่อง
- มาเล่นกับ CSS ที่เร่งด้วยฮาร์ดแวร์กันเถอะ
มันเป็นวัตถุ 3 มิติ (ถ้าจะแม่นยำกว่าทรงลูกบาศก์) ที่หมุนรอบแกนใดแกนหนึ่ง ฉันมีประสบการณ์ในการทำงานกับ CSS 3D แล้ว และเริ่มมีวิธีแก้ปัญหาในใจ ฉันใช้คำหลัก Googled เช่น "CSS 3D cube" เพื่อยืนยันแนวคิดของฉันและตอบ Eugene ว่าเป็นไปได้
คำถามต่อไปของยูจีนคือฉันจะทำโครงการนี้หรือไม่? ฉันชอบงานที่ยุ่งยาก ฉันเลยปฏิเสธไม่ได้ ตอนนั้นฉันไม่รู้ว่าฉันกำลังทำอะไรอยู่ แต่ฉันก็ตัดสินใจไม่ถูก
ลับขวานของคุณ
มาเตือนตัวเองเกี่ยวกับแกน — ไม่ใช่แกนสงคราม แต่เส้นจำนวน แกนเดียวกับในระบบพิกัดคาร์ทีเซียนสามมิติที่เราศึกษาในโรงเรียน ตามที่ Wikipedia บอกเรา:
ระบบพิกัดคาร์ทีเซียนสำหรับปริภูมิสามมิติคือชุดของเส้นตรง (แกน) ที่เรียงกันเป็นสามเท่าซึ่งตั้งฉากกันเป็นคู่ มีหน่วยความยาวหน่วยเดียวสำหรับทั้งสามแกน และมีการวางแนวสำหรับแต่ละแกน
รูปภาพด้านล่างแสดงการวางแกนในเว็บเบราว์เซอร์

แกน x เป็นแนวนอน แกน y เป็นแนวตั้ง และแกน z ดูเหมือนจะออกมาจากหน้าจอเข้าหาคุณ ค่าศูนย์ของแกน z คือระนาบของหน้าจอ จำสิ่งนี้ไว้
การล้างมุมมอง
ในการสร้างวัตถุ 3 มิติ ฉันต้องการองค์ประกอบ (เรียกว่า "ฉาก") ที่มีเปอร์สเปคทีฟ เปอร์สเปคทีฟคือความลึกของฉาก และขึ้นอยู่กับขนาดของวัตถุที่อยู่ในนั้น
.scene { perspective: 800px; }
หากเปอร์สเปคทีฟมีขนาดเล็กเกินไป วัตถุอาจบิดเบี้ยวได้ ถ้ามันใหญ่เกินไป เอฟเฟกต์ 3D จะลดลงจนไม่มีเลย
ดูปากกา jqgMvL โดย Anna Selezniova (@askd) บน CodePen
นอกจากนี้ยังมีมุมมองภาพเพียงมุมเดียวสำหรับวัตถุทั้งหมดในฉาก และเอฟเฟกต์ 3D ขึ้นอยู่กับตำแหน่งของมุมมอง
ดู Pen oxKzKv โดย Anna Selezniova (@askd) บน CodePen
แล้วเราจะคำนวณเปอร์สเปคทีฟอย่างไร? ฉันพบว่ามันขึ้นอยู่กับแกนหมุน สำหรับแกน x ค่าความสูงคูณด้วย 4 จะพอดี สำหรับแกน y มันจะเป็นค่าความกว้างคูณด้วย 4 นี่คือสูตรเวทย์มนตร์ของฉัน:
const perspective = dimension * 4;
พิจารณาจากทุกด้าน
หลังจากกำหนดเปอร์สเปคทีฟแล้ว ฉันก็เริ่มสร้างวัตถุ 3 มิติ ฉันเลือกลูกบาศก์เพราะมันตรงไปตรงมาและคาดเดาได้ องค์ประกอบคิวบ์ถูกสร้างขึ้นเป็น div ปกติ อยู่ในตำแหน่งที่ค่อนข้างมีการกำหนดความกว้างและความสูง (เช่น 200px
) มันแปลงร่างเป็นวัตถุ 3 มิติผ่านคุณสมบัติ transform-style
ด้วยค่า preserve-3d
มันบอกให้เบราว์เซอร์แสดงองค์ประกอบที่ซ้อนกันทั้งหมดตามกฎของโลก 3 มิติ
ในกรณีของฉัน คิวบ์มีหก divs (หรือ "ด้าน") ซึ่งอยู่ในตำแหน่งที่แน่นอน ชื่อคลาสตรงกับตำแหน่งเริ่มต้นของด้านข้าง ( back
, left
, right
, top
, bottom
, front
) นี่คือมาร์กอัป:
<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>
โดยค่าเริ่มต้น ด้านทั้งหมดจะอยู่บนระนาบเดียว ดังนั้นฉันจึงต้องจัดเรียงใหม่ นี่คือลักษณะที่ปรากฏ:
ดูปากกา mPNwPx โดย Anna Selezniova (@askd) บน CodePen
และนี่คือผลลัพธ์ 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); }
ในการหมุนลูกบาศก์ ฉันตั้งค่าคุณสมบัติการ transform
บนองค์ประกอบคิวบ์ให้เป็นมุมการหมุนตามอำเภอใจตามแกน x:
.cube { transform: rotateX(42deg); }
การเอาชนะข้อบกพร่อง
ตามที่ได้รับมอบหมาย ฉันต้องหมุนลูกบาศก์ตามแกน x เท่านั้น ดังนั้นฉันจึงไม่ต้องการด้านซ้ายหรือด้านขวา ฉันเพิ่มคำบรรยายเพื่อให้สอดคล้องกับตำแหน่งเริ่มต้นของด้านที่เหลือ
ฉันเริ่มหมุนลูกบาศก์และพบว่าคำบรรยายด้านล่างและด้านหลังแสดงกลับหัวกลับหาง:
ดู Pen GZVvMR โดย Anna Selezniova (@askd) บน CodePen
เพื่อแก้ปัญหานี้ ฉันหมุนแต่ละด้านเหล่านี้ตามแกน x 180 องศา:
.back { transform: translateZ(-100px) rotateX(180deg); } .bottom { transform: translateY(100px) rotateX(270deg); }
ก้าวข้ามหน้าจอ
ฉันเริ่มเติมเนื้อหาจริงด้านข้างและพบปัญหาอื่นทันที ฉันต้องแสดงเส้นประขนาด 1 พิกเซล แต่เส้นนั้นพร่ามัวและดูไม่ดี

ดู Pen VjeBPg โดย Anna Selezniova (@askd) บน CodePen
ในไม่ช้าฉันก็รู้ว่าปัญหาคืออะไร คุณจำโฆษณาทีวี 3 มิติที่ภาพขยายออกไปนอกหน้าจอได้หรือไม่? มันเป็นแบบนั้นกับลูกบาศก์ของฉัน
หากคุณสามารถมองดูลูกบาศก์จากด้านซ้ายหรือด้านขวาได้ คุณจะเห็นว่าจุดศูนย์กลางอยู่บนระนาบของหน้าจอ (ศูนย์บนแกน z) และด้านหน้าอยู่เลยหน้าจอ ดังนั้นมันจึงเพิ่มขึ้นทางสายตาและเบลอ
ดูปากกา WwVEMR โดย Anna Selezniova (@askd) บน CodePen
ในการแก้ปัญหานี้ ฉันเลื่อนลูกบาศก์ไปตามแกน z เพื่อจัดแนวด้านหน้าให้ตรงกับระนาบของหน้าจอ:
.cube { transform:translateZ(-100px); }
นี่คือคิวบ์ตอนนี้ เกือบจะพร้อมแล้ว:
ดู Pen Xdvery โดย Anna Selezniova (@askd) บน CodePen
การใช้ตัวเลขมหัศจรรย์
ฉันเดาว่าคุณคงสังเกตเห็นว่าฉันใช้เลขมหัศจรรย์ 100
เพื่อเลื่อนด้านข้างตามแนวแกน ค่า 100
เท่ากับความสูงครึ่งหนึ่งของคิวบ์ทดสอบของฉันพอดี ทำไมสูงครึ่งหนึ่ง? เพราะนั่นจะเป็นรัศมีของวงกลมที่ถูกจารึกไว้ที่ด้านข้างของลูกบาศก์
const offset = dimension / 2;
ถ้าฉันต้องหมุนปริซึมสามเหลี่ยม วงกลมก็จะถูกจารึกเป็นรูปสามเหลี่ยม ในกรณีนี้ สูตรออฟเซ็ตจะเป็นดังนี้:
const offset = dimension / (2 * Math.sqrt(3));
Blowing Away The Cube
เพื่อให้งานเสร็จสมบูรณ์ ฉันต้องทดสอบผลลัพธ์ในเบราว์เซอร์ต่างๆ
ภาพที่ฉันเห็นใน Internet Explorer ทำให้ฉันตกต่ำ หากต้องการทราบว่าฉันกำลังพูดถึงอะไร ให้ดูการสาธิตด้านล่างในเบราว์เซอร์ที่คุณชื่นชอบ ฉันเปลี่ยนคุณสมบัติหนึ่งที่ทำให้คิวบ์แสดงอย่างไม่ถูกต้องใน Internet Explorer อย่างไรก็ตาม อย่าดูซอร์สโค้ดจนกว่าคุณจะอ่านย่อหน้าภายใต้การสาธิตด้านล่าง
ดู Pen XKWMwV โดย Anna Selezniova (@askd) บน CodePen
ความจริงก็คือ Internet Explorer ไม่สนับสนุนคุณสมบัติ transform-style
ด้วยค่าที่ preserve-3d
ฉันเรียนรู้เกี่ยวกับสิ่งนี้โดยดูจากแหล่งข้อมูลที่เชื่อถือได้ ฉันใช้ได้ไหม (ดูหมายเหตุ 1) ในการสาธิตด้านบน ฉันแทนที่ preserve-3d
ด้วย flat
คุณรู้อยู่แล้วว่า? ก็บอกแล้วไงว่าอย่าแอบดู!
ฉันเสียใจ แต่ฉันจะไม่ยอมแพ้ ปัญหาคือโอกาสในการเรียนรู้สิ่งใหม่ นอกจากนี้ ฉันยังยอมรับคำท้า
ค้นหาศูนย์กลาง
ฉันกำลังมองหาวิธีสร้างวัตถุ 3 มิติโดยไม่ใช้ transform-style: preserve-3d
และในที่สุดฉันก็ค้นพบคุณสมบัติที่มีประโยชน์: transform-origin
กำหนดจุดศูนย์กลางของการเปลี่ยนแปลงขององค์ประกอบ ฉันได้สร้างการสาธิตเชิงโต้ตอบด้านล่าง ซึ่งจะช่วยให้คุณเข้าใจวิธีการทำงาน:
ดูปากกา rLNmBp โดย Anna Selezniova (@askd) บน CodePen
การหมุนองค์ประกอบ 3 มิติในตัวอย่างจะคล้ายกับด้านหน้าของลูกบาศก์มากใช่ไหม นั่นคือสิ่งที่ฉันใช้
(อย่างไรก็ตาม คุณพยายามเลือกช่องทำเครื่องหมาย backface-visibility: hidden
ในระหว่างการหมุน 3D หรือไม่ คุณสมบัตินี้ใช้เพื่อซ่อนด้านหลังขององค์ประกอบระหว่างการแปลง 3D)
เริ่มต้นใหม่อีกครั้ง
ฉันเริ่มทำลูกบาศก์ใหม่ ฉันไม่ต้องโต้ตอบกับฉากโดยรวม ดังนั้นฉันจึงลบคุณสมบัติ perspective
เปคทีฟขององค์ประกอบ scene
และเพิ่มลงในการแปลง 3 มิติแต่ละครั้ง เพื่อให้ตอนนี้องค์ประกอบทั้งหมดแปลงอย่างอิสระ นอกจากนี้ ฉันตั้งค่าคุณสมบัติใหม่สำหรับแต่ละด้าน: transform-origin
ด้วยค่าเท่ากับตำแหน่งของจุดศูนย์กลางของคิวบ์ และ backface-visibility: hidden
สไตล์มีการเปลี่ยนแปลงดังนี้:
.scene { } .cube { position: relative; width: 200px; height: 200px; transform: perspective(800px) translateZ(-100px); } .side { position: absolute; transform-origin: 50% 50% -100px; backface-visibility: hidden; }
ฉันต้องวางด้านข้างในสถานที่ที่เหมาะสม เนื่องจากคุณสมบัติ transform-origin
ฉันไม่จำเป็นต้องเลื่อนมัน หมุนพวกมันรอบแกนเท่านั้น มันเหมือนกับเวทมนตร์! เรามาดูกันว่ามันมีลักษณะอย่างไร:
ดูปากกา zBYwEm โดย Anna Selezniova (@askd) บน CodePen
นี่คือ CSS สำหรับการจัดวางด้านข้าง:
.back { transform: perspective(800px) rotateY(180deg); } .top { transform: perspective(800px) rotateX(90deg); } .bottom { transform: perspective(800px) rotateX(-90deg); } .front { transform: perspective(800px); }
และที่นี่คุณสามารถเห็นการทำงานของคิวบ์ใหม่:
ดูปากกา WWvdXd โดย Anna Selezniova (@askd) บน CodePen
ให้กลับไปซีซาร์ ของซีซาร์คืออะไร
ลูกบาศก์ที่สองมีลักษณะและหมุนเหมือนกับก้อนแรก แต่ในกรณีนี้ คุณต้องแปลงแต่ละด้านแยกกัน การดำเนินการนี้อาจไม่ใช่เรื่องง่าย โดยเฉพาะอย่างยิ่งหากคุณต้องการควบคุมมุมกลางของการหมุน
นอกจากนี้ หากคุณเปิดการสาธิตใน Chrome คุณจะเห็นว่าด้านข้างกะพริบระหว่างการหมุน ซึ่งน่าหงุดหงิดมาก
ในท้ายที่สุด ฉันใช้ทั้งสองวิธีโดยใช้การทดสอบอย่างง่ายของ transform-style: preserve-3d
คิวบ์แรกคือคิวบ์เริ่มต้น คิวบ์ที่สองมีไว้สำหรับ Internet Explorer และเบราว์เซอร์ที่ไม่สนับสนุน preserve-3d
การใช้พลังแห่งคณิตศาสตร์
สุดท้าย ฉันต้องใช้เอฟเฟกต์พารัลแลกซ์ โดยปกติ เอฟเฟกต์นี้จะตอบสนองต่อการกระทำของผู้ใช้ ไม่ว่าจะเป็นตำแหน่งของเคอร์เซอร์ของเมาส์หรือแถบเลื่อน ในกรณีนี้ เอฟเฟกต์จะขึ้นอยู่กับมุมของการหมุน
ดูปากกา QENyqm โดย Anna Selezniova (@askd) บน CodePen
แล้วฉันมีข้อมูลอะไรบ้าง? อันดับแรก ฉันมีจุดเริ่มต้นและจุดสิ้นสุดของตำแหน่งของคำอธิบายภาพ หรือพูดง่ายๆ ก็คือ offset
ขึ้นและลงจากกึ่งกลางด้านข้าง ประการที่สอง ฉันมี angle
การหมุนของลูกบาศก์
ฉันใช้เวลาหลายชั่วโมงในการพัฒนาสูตร ทันใดนั้น ฉันก็นึกขึ้นได้ นี่คือสิ่งที่เข้ามาในหัว:

ด้วยความช่วยเหลือของไซน์และโคไซน์ ฉันสามารถคำนวณออฟเซ็ตของแต่ละคำอธิบายภาพตามมุมได้อย่างง่ายดาย นี่คือสูตรที่ฉันคิดขึ้นมา:
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;
สรุป
ตอนนี้งานเสร็จแล้ว ฉันสามารถสนุกกับผลลัพธ์และแชร์กับคุณได้ ดูด้วยตัวคุณเองว่ามันทำงานอย่างไร ใช้ปุ่มเลื่อนหรือลูกศรเพื่อหมุนบล็อกโปรโมชั่น นอกจากนี้ ให้พยายามดึงสามเหลี่ยมสีดำทางด้านขวาขึ้นและลงเพื่อควบคุมมุมการหมุนด้วยตนเอง (ขออภัย คุณลักษณะนี้ใช้ไม่ได้ใน Internet Explorer) ดูดีทีเดียวใช่มั้ย? และประสิทธิภาพค่อนข้างสูง (ประมาณ 60 เฟรมต่อวินาที)
ฉันดีใจมากที่ได้มีส่วนร่วมในการพัฒนาเว็บไซต์นี้ ฉันได้รับประสบการณ์ที่เป็นประโยชน์ในการทำงานกับ CSS 3D และได้ค้นพบคุณสมบัติที่น่าสนใจมากมาย ที่สำคัญกว่านั้น ฉันได้เรียนรู้ว่าไม่ควรยอมแพ้ เป็นไปได้มากว่าคุณจะพบวิธีการทำงานให้สำเร็จ
ฉันหวังว่าคุณจะสนุกกับเรื่องราวของฉันและตอนนี้พร้อมที่จะเผชิญกับความท้าทายใหม่ๆ