ยอมรับความท้าทายส่วนหน้าแล้ว: CSS 3D Cube

เผยแพร่แล้ว: 2022-03-10
สรุปสั้นๆ ↬ คุณชอบความท้าทายหรือไม่? คุณยินดีที่จะทำงานที่คุณไม่เคยเจอมาก่อนและทำมันภายใต้เส้นตายหรือไม่? จะเป็นอย่างไรหากในการปฏิบัติงาน คุณพบปัญหาที่ดูเหมือนแก้ไม่ได้ ฉันต้องการ แบ่งปันประสบการณ์ของฉันในการใช้เอฟเฟกต์ CSS 3D เป็นครั้งแรกในโครงการจริง และเพื่อเป็นแรงบันดาลใจให้คุณรับมือกับความท้าทาย เป็นวันธรรมดาที่ Eugene ผู้จัดการของ CreativePeople เขียนถึงฉัน เขาส่งวิดีโอมาให้ฉันและอธิบายว่าเขากำลังพัฒนาแนวคิดสำหรับโปรเจ็กต์ใหม่ และสงสัยว่าเป็นไปได้ไหมที่ฉันจะพัฒนาบางอย่างที่เหมือนกับในวิดีโอ

คุณชอบความท้าทายหรือไม่? คุณยินดีที่จะทำงานที่คุณไม่เคยเจอมาก่อนและทำมันภายใต้เส้นตายหรือไม่? จะเป็นอย่างไรหากในการปฏิบัติงาน คุณพบปัญหาที่ดูเหมือนแก้ไม่ได้ ฉันต้องการ แบ่งปันประสบการณ์ของฉันในการใช้เอฟเฟกต์ CSS 3D เป็นครั้งแรกในโครงการจริง และเพื่อเป็นแรงบันดาลใจให้คุณรับมือกับความท้าทาย

เป็นวันธรรมดาที่ Eugene ผู้จัดการของ CreativePeople เขียนถึงฉัน เขาส่งวิดีโอมาให้ฉันและอธิบายว่าเขากำลังพัฒนาแนวคิดสำหรับโปรเจ็กต์ใหม่ และสงสัยว่าเป็นไปได้ไหมที่ฉันจะพัฒนาบางอย่างที่เหมือนกับในวิดีโอ

อ่านเพิ่มเติม เกี่ยวกับ SmashingMag:

  • Beercamp: การทดลองกับ CSS 3D
  • การสร้างรูปร่างที่ตอบสนองด้วยคลิปพาธและการแยกส่วนออกจากกล่อง
  • มาเล่นกับ CSS ที่เร่งด้วยฮาร์ดแวร์กันเถอะ

มันเป็นวัตถุ 3 มิติ (ถ้าจะแม่นยำกว่าทรงลูกบาศก์) ที่หมุนรอบแกนใดแกนหนึ่ง ฉันมีประสบการณ์ในการทำงานกับ CSS 3D แล้ว และเริ่มมีวิธีแก้ปัญหาในใจ ฉันใช้คำหลัก Googled เช่น "CSS 3D cube" เพื่อยืนยันแนวคิดของฉันและตอบ Eugene ว่าเป็นไปได้

เพิ่มเติมหลังกระโดด! อ่านต่อด้านล่าง↓

คำถามต่อไปของยูจีนคือฉันจะทำโครงการนี้หรือไม่? ฉันชอบงานที่ยุ่งยาก ฉันเลยปฏิเสธไม่ได้ ตอนนั้นฉันไม่รู้ว่าฉันกำลังทำอะไรอยู่ แต่ฉันก็ตัดสินใจไม่ถูก

ลับขวานของคุณ

มาเตือนตัวเองเกี่ยวกับแกน — ไม่ใช่แกนสงคราม แต่เส้นจำนวน แกนเดียวกับในระบบพิกัดคาร์ทีเซียนสามมิติที่เราศึกษาในโรงเรียน ตามที่ Wikipedia บอกเรา:

ระบบพิกัดคาร์ทีเซียนสำหรับปริภูมิสามมิติคือชุดของเส้นตรง (แกน) ที่เรียงกันเป็นสามเท่าซึ่งตั้งฉากกันเป็นคู่ มีหน่วยความยาวหน่วยเดียวสำหรับทั้งสามแกน และมีการวางแนวสำหรับแต่ละแกน

รูปภาพด้านล่างแสดงการวางแกนในเว็บเบราว์เซอร์

ระบบพิกัดคาร์ทีเซียนสามมิติทางขวามือ โดยมีแกน Z ชี้ไปทางผู้ชม
ระบบพิกัดคาร์ทีเซียนสามมิติทางขวามือ โดยมีแกน z ชี้ไปทางผู้ชม (ภาพ: Wikimedia Commons) (ดูเวอร์ชันขนาดใหญ่)

แกน 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 การหมุนของลูกบาศก์

ฉันใช้เวลาหลายชั่วโมงในการพัฒนาสูตร ทันใดนั้น ฉันก็นึกขึ้นได้ นี่คือสิ่งที่เข้ามาในหัว:

กราฟของฟังก์ชันไซน์และโคไซน์
กราฟของฟังก์ชันไซน์และโคไซน์ (ภาพ: Wikimedia Commons) (ดูเวอร์ชันขนาดใหญ่)

ด้วยความช่วยเหลือของไซน์และโคไซน์ ฉันสามารถคำนวณออฟเซ็ตของแต่ละคำอธิบายภาพตามมุมได้อย่างง่ายดาย นี่คือสูตรที่ฉันคิดขึ้นมา:

 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 และได้ค้นพบคุณสมบัติที่น่าสนใจมากมาย ที่สำคัญกว่านั้น ฉันได้เรียนรู้ว่าไม่ควรยอมแพ้ เป็นไปได้มากว่าคุณจะพบวิธีการทำงานให้สำเร็จ

ฉันหวังว่าคุณจะสนุกกับเรื่องราวของฉันและตอนนี้พร้อมที่จะเผชิญกับความท้าทายใหม่ๆ