تم قبول تحدي الواجهة الأمامية: CSS 3D Cube

نشرت: 2022-03-10
ملخص سريع ↬ هل تحب التحديات؟ هل أنت على استعداد لتولي مهمة لم تصادفها من قبل ، وتقوم بها ضمن موعد نهائي؟ ماذا لو واجهت أثناء قيامك بالمهمة مشكلة تبدو غير قابلة للحل؟ أرغب في مشاركة تجربتي في استخدام تأثيرات CSS ثلاثية الأبعاد لأول مرة في مشروع حقيقي وإلهامك لمواجهة التحديات. كان يومًا عاديًا عندما كتب لي يوجين ، مدير في CreativePeople . أرسل لي مقطع فيديو وأوضح أنه كان يطور مفهومًا لمشروع جديد وكان يتساءل عما إذا كان من الممكن بالنسبة لي تطوير شيء مثل ما هو موجود في الفيديو.

هل تحب التحديات؟ هل أنت على استعداد لتولي مهمة لم تصادفها من قبل ، وتقوم بها ضمن موعد نهائي؟ ماذا لو واجهت أثناء قيامك بالمهمة مشكلة تبدو غير قابلة للحل؟ أرغب في مشاركة تجربتي في استخدام تأثيرات CSS ثلاثية الأبعاد لأول مرة في مشروع حقيقي وإلهامك لمواجهة التحديات.

كان يومًا عاديًا عندما كتب لي يوجين ، مدير في CreativePeople. أرسل لي مقطع فيديو وأوضح أنه كان يطور مفهومًا لمشروع جديد وكان يتساءل عما إذا كان من الممكن بالنسبة لي تطوير شيء مثل ما هو موجود في الفيديو.

مزيد من القراءة على SmashingMag:

  • Beercamp: تجربة باستخدام CSS 3D
  • إنشاء أشكال مستجيبة مع مسار المقطع وكسر خارج الصندوق
  • هيا نلعب باستخدام CSS المعجل بالأجهزة

لقد كان كائنًا ثلاثي الأبعاد (متوازي المستطيلات ، على وجه الدقة) يدور حول أحد المحاور. لدي بالفعل بعض الخبرة في العمل مع CSS 3D ، وبدأ الحل يتشكل في ذهني. لقد بحثت في Google عن كلمات رئيسية مثل "CSS 3D cube" لتأكيد أفكاري وأجبت على Eugene بأن ذلك ممكن.

المزيد بعد القفز! أكمل القراءة أدناه ↓

كان السؤال التالي الذي طرحه يوجين هو ما إذا كنت سأقوم بالمشروع؟ أحب المهام الصعبة ، لذا لا يمكنني الرفض. في ذلك الوقت ، لم أكن أدرك ما كنت بصدد الانخراط فيه ، لكنني كنت أعجز عن تصميمي.

شحذ فؤوسك

دعونا نذكر أنفسنا بالمحاور - ليس محاور الحرب ، ولكن خطوط الأرقام ، نفس المحاور كما في نظام الإحداثيات الديكارتية ثلاثي الأبعاد الذي درسناه في المدرسة. كما تخبرنا ويكيبيديا:

نظام الإحداثيات الديكارتية لمساحة ثلاثية الأبعاد عبارة عن مجموعة ثلاثية مرتبة من الخطوط (المحاور) متعامدة على أساس الزوج ، ولها وحدة طول واحدة لجميع المحاور الثلاثة ولها اتجاه لكل محور.

توضح الصورة أدناه كيفية توجيه المحاور في متصفح الويب.

نظام إحداثيات ديكارتي ثلاثي الأبعاد يميني مع المحور Z باتجاه العارض.
نظام إحداثيات ديكارتي ثلاثي الأبعاد يميني مع المحور z باتجاه العارض. (الصورة: ويكيميديا ​​كومنز) (عرض النسخة الكبيرة)

المحور x أفقي ، والمحور y عمودي ، والمحور z يبدو وكأنه يخرج من الشاشة نحوك. القيمة الصفرية للمحور z هي مستوى الشاشة. تذكر هذا.

مسح المنظور

لإنشاء كائن ثلاثي الأبعاد ، كنت بحاجة إلى عنصر (دعنا نطلق عليه "مشهد") بمنظور. المنظور هو عمق المشهد ، ويعتمد على أحجام العناصر التي يحتوي عليها.

 .scene { perspective: 800px; }

إذا كان المنظور صغيرًا جدًا ، فقد تتشوه الكائنات. إذا كانت كبيرة جدًا ، فسيتم تقليل التأثير ثلاثي الأبعاد إلى لا شيء.

شاهد القلم jqgMvL بواسطة Anna Selezniova (askd) على CodePen.

علاوة على ذلك ، توجد زاوية رؤية واحدة فقط لجميع الكائنات في المشهد. ويعتمد التأثير ثلاثي الأبعاد على موضع وجهة النظر.

شاهد القلم oxKzKv بواسطة Anna Selezniova (askd) على CodePen.

إذن ، كيف نحسب المنظور؟ اكتشفت أن ذلك يعتمد على محور الدوران. بالنسبة للمحور x ، فإن قيمة الارتفاع مضروبة في 4 ستكون مناسبة. بالنسبة للمحور الصادي ، ستكون قيمة العرض مضروبة في 4. ها هي الصيغة السحرية الخاصة بي:

 const perspective = dimension * 4;

تعتبر من كل الجهات

بعد تحديد المنظور ، بدأت في إنشاء كائن ثلاثي الأبعاد. اخترت المكعب لأنه مباشر ويمكن التنبؤ به. يتم إنشاء عنصر مكعب على هيئة div عادي ، وموضع نسبيًا ، مع تحديد العرض والارتفاع (على سبيل المثال ، 200px ). يتحول إلى كائن ثلاثي الأبعاد من خلال خاصية transform-style بقيمة preserve-3d . يخبر المتصفح بعرض جميع العناصر المتداخلة وفقًا لقواعد العالم ثلاثي الأبعاد.

في حالتي ، يحتوي المكعب على ستة أجزاء (أو "جوانب") موضوعة تمامًا. تتوافق أسماء الفئات مع المواضع الأولية للجوانب ( 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 فقط ، لذلك لم أكن بحاجة إلى الجانب الأيسر أو الأيمن. أضفت تسميات توضيحية لتتماشى مع المواضع الأولية للجوانب المتبقية.

بدأت في تدوير المكعب ووجدت أن التسميات التوضيحية على الجانبين السفلي والخلفي معروضة بالمقلوب:

شاهد القلم GZVvMR بواسطة Anna Selezniova (askd) على CodePen.

لحل هذه المشكلة ، قمت بتدوير كل جانب على المحور x بمقدار 180 درجة:

 .back { transform: translateZ(-100px) rotateX(180deg); } .bottom { transform: translateY(100px) rotateX(270deg); }

تجاوز الشاشة

بدأت في ملء الجوانب بمحتوى حقيقي وواجهت مشكلة أخرى على الفور. كنت بحاجة لعرض خطوط منقطة بدقة 1 بكسل ، لكنها كانت ضبابية وبدت سيئة.

شاهد القلم VjeBPg بواسطة Anna Selezniova (askd) على CodePen.

سرعان ما أدركت ما هي المشكلة. هل تتذكر الإعلان التلفزيوني ثلاثي الأبعاد الذي تمتد فيه الصورة إلى ما وراء الشاشة؟ كان شيئًا من هذا القبيل مع المكعب الخاص بي.

إذا كان بإمكانك النظر إلى المكعب من الجانب الأيسر أو الأيمن ، فسترى أن مركزه كان على مستوى الشاشة (صفر على المحور z) وأن الجانب الأمامي كان خلف الشاشة. لذلك ، زاد بشكل مرئي وغير واضح.

شاهد القلم WwVEMR بواسطة Anna Selezniova (askd) على CodePen.

لحل هذه المشكلة ، قمت بإزاحة المكعب على طول المحور z لمحاذاة الجانب الأمامي لمستوى الشاشة:

 .cube { transform:translateZ(-100px); }

ها هو المكعب الآن جاهز تقريبًا:

شاهد القلم Xdvery بواسطة Anna Selezniova (askd) على CodePen.

باستخدام الأرقام السحرية

أعتقد أنك لاحظت أنني أستخدم الرقم السحري 100 لتغيير الأضلاع على طول المحور. القيمة 100 هي بالضبط نصف ارتفاع مكعب الاختبار الخاص بي. لماذا نصف الارتفاع؟ لأن هذا سيكون نصف قطر دائرة منقوشة في جانب من المكعب (وهو مربع ، على ما يبدو).

 const offset = dimension / 2;

إذا كنت بحاجة إلى تدوير منشور مثلثي ، فسيتم نقش الدائرة في مثلث. في هذه الحالة ، ستكون صيغة الإزاحة كما يلي:

 const offset = dimension / (2 * Math.sqrt(3));

تهب المكعب

لاعتبار المهمة مكتملة ، كان علي اختبار النتيجة في متصفحات مختلفة.

الصورة التي رأيتها في Internet Explorer أغرقتني بالاكتئاب. للحصول على فكرة عما أتحدث عنه ، انظر إلى العرض التوضيحي أدناه في متصفحك المفضل. لقد قمت بتغيير خاصية واحدة أدت إلى عرض المكعب بشكل غير صحيح في Internet Explorer. ومع ذلك ، لا تلقي نظرة خاطفة على الكود المصدري حتى تقرأ الفقرة الموجودة أسفل العرض التوضيحي أدناه.

شاهد القلم XKWMwV بواسطة Anna Selezniova (askd) على CodePen.

الحقيقة هي أن Internet Explorer لا يدعم خاصية transform-style بقيمة preserve-3d . لقد تعلمت عن هذا من خلال النظر إلى المورد الموثوق به الذي يمكنني استخدامه (انظر الملاحظة 1). في العرض التوضيحي أعلاه ، قمت باستبدال preserve-3d بـ flat . هل كنت تعرف ذلك بالفعل؟ مهلا ، قلت لك لا تختلس النظر!

كنت مستاءً ، لكنني لن أستسلم. المشكلة هي فرصة لتعلم شيء جديد. علاوة على ذلك ، لقد قبلت التحدي.

البحث عن نقطة ارتكاز

كنت أبحث عن طريقة لإنشاء كائن ثلاثي الأبعاد دون استخدام transform-style: preserve-3d ، واكتشفت في النهاية خاصية مفيدة: transform-origin . يحدد النقطة المركزية لتحول العنصر. لقد قمت بإنشاء عرض توضيحي تفاعلي أدناه ، والذي سيساعدك على فهم كيفية عمله:

شاهد القلم rLNmBp بواسطة Anna Selezniova (askd) على CodePen.

الدوران ثلاثي الأبعاد للعنصر في العرض التوضيحي مشابه جدًا للجانب الأمامي للمكعب ، أليس كذلك؟ هذا ما استخدمته.

(بالمناسبة ، هل حاولت تحديد خانة الاختيار backface-visibility: hidden أثناء الدوران ثلاثي الأبعاد؟ تُستخدم هذه الخاصية لإخفاء الجزء الخلفي من العنصر أثناء التحويل ثلاثي الأبعاد.)

البدء من جديد

بدأت في إعادة المكعب. لم أكن مضطرًا للتفاعل مع المشهد ككل ، لذلك أزلت خاصية perspective لعنصر scene وأضفتها إلى كل تحويل ثلاثي الأبعاد ، بحيث يتحول كل عنصر الآن بشكل مستقل. أيضًا ، قمت بتعيين خصائص جديدة لكل جانب: 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 واكتشفت العديد من الخصائص المثيرة للاهتمام. والأهم من ذلك ، لقد تعلمت أنه لا ينبغي لأحد أن يستسلم ؛ على الأرجح ستجد طريقة لإنجاز المهمة.

أتمنى أن تكون قد استمتعت بقصتي وأن تكون مستعدًا الآن لمواجهة تحديات جديدة.