CSS3 والتصميم المتجاوب

التحويلات ثلاثية الأبعاد والمنظور

25 دقيقة الدرس 40 من 60

مقدمة في الفضاء ثلاثي الأبعاد في CSS

توسّع تحويلات CSS ثلاثية الأبعاد نموذج التحويل ثنائي الأبعاد بإضافة محور ثالث: المحور Z، الذي يمثل العمق. بينما تعمل التحويلات ثنائية الأبعاد على المحورين X (أفقي) و Y (عمودي)، تتيح لك التحويلات ثلاثية الأبعاد تدوير العناصر وإزاحتها وتحجيمها على طول المحاور الثلاثة جميعها، مما يخلق وهم الفضاء ثلاثي الأبعاد مباشرة في المتصفح. هذا يفتح الباب أمام تأثيرات غامرة مثل قلب البطاقات والمكعبات الدوارة والعروض الدائرية والواجهات متعددة الطبقات التي تبدو ملموسة وتفاعلية.

لجعل التحويلات ثلاثية الأبعاد مرئية، تحتاج إلى إنشاء سياق مشاهدة باستخدام perspective. بدون المنظور، تبدو الدورات ثلاثية الأبعاد مسطحة لأن المتصفح يعرضها باستخدام إسقاط متعامد (متوازي) بدلاً من إسقاط منظوري. فكر في المنظور على أنه يحدد أين يقف المشاهد بالنسبة للشاشة -- فهو يتحكم في مدى دراماتيكية إدراك العمق.

مثال: التحويل ثلاثي الأبعاد مقابل التحويل المسطح

/* بدون منظور: rotateY يبدو مثل scaleX */
.flat {
    transform: rotateY(45deg);
    /* هذا يبدو مثل ضغط أفقي، وليس دوران */
}

/* مع منظور على الأب: مظهر ثلاثي الأبعاد حقيقي */
.parent {
    perspective: 800px;
}
.parent .child {
    transform: rotateY(45deg);
    /* الآن هذا يبدو مثل بطاقة مُدوَّرة في فضاء ثلاثي الأبعاد */
}

خاصية perspective مقابل دالة perspective()

هناك طريقتان لتطبيق المنظور في CSS، وكلتاهما تتصرفان بشكل مختلف. فهم هذا التمييز أمر بالغ الأهمية لبناء تأثيرات ثلاثية الأبعاد صحيحة.

خاصية perspective (على العنصر الأب)

عندما تطبق خاصية perspective على عنصر أب، يتشارك جميع العناصر الفرعية نفس نقطة المنظور. هذا ينشئ مشهدًا ثلاثي الأبعاد متسقًا حيث تبدو العناصر الأقرب للحافة ذات تقصير منظوري أكثر دراماتيكية، تمامًا مثل الأشياء في العالم الحقيقي. تمثل القيمة المسافة من المشاهد إلى مستوى z=0.

مثال: خاصية perspective على الأب

.scene {
    perspective: 1000px;
    /* جميع العناصر الفرعية تتشارك هذا المنظور */
    /* قيم أقل = تأثير ثلاثي الأبعاد أكثر دراماتيكية */
    /* قيم أعلى = تأثير ثلاثي الأبعاد أكثر دقة */
}

.scene .card-1 {
    transform: rotateY(30deg);
    /* تبدو وكأنها تدور في فضاء ثلاثي الأبعاد مشترك */
}

.scene .card-2 {
    transform: rotateY(-30deg);
    /* تدور من نفس نقطة المشاهدة مثل card-1 */
}

دالة perspective() (على العنصر نفسه)

عندما تستخدم perspective() كدالة تحويل على العنصر نفسه، يحصل كل عنصر على منظوره الفردي الخاص. هذا يعني أن كل عنصر يُشاهد من أمامه مباشرة، بغض النظر عن موضعه على الصفحة. العناصر القريبة من حواف الحاوية لا تظهر تقصيرًا منظوريًا إضافيًا.

مثال: دالة perspective() على العناصر

/* كل عنصر لديه منظوره الخاص */
.card-1 {
    transform: perspective(1000px) rotateY(30deg);
    /* يُشاهد من أمام هذه البطاقة مباشرة */
}

.card-2 {
    transform: perspective(1000px) rotateY(-30deg);
    /* يُشاهد من أمام هذه البطاقة أيضًا */
    /* كلتا البطاقتين تبدوان متشابهتين رغم اختلاف المواضع */
}

/* مهم: perspective() يجب أن تأتي أولاً في سلسلة التحويل */
.correct {
    transform: perspective(800px) rotateY(45deg);
}

.incorrect {
    transform: rotateY(45deg) perspective(800px);
    /* المنظور بعد الدوران ينتج نتائج غير متوقعة */
}
ملاحظة: استخدم خاصية perspective على الأب عندما تريد أن يتواجد عدة عناصر فرعية في نفس المشهد ثلاثي الأبعاد (مثل شبكة بطاقات أو عرض دائري ثلاثي الأبعاد). استخدم دالة perspective() عندما تريد أن يكون لكل عنصر تأثيره ثلاثي الأبعاد المستقل (مثل تأثيرات تحويم البطاقات الفردية). نهج الخاصية أكثر شيوعًا للتركيبات ثلاثية الأبعاد المعقدة.

اختيار قيمة المنظور

تحدد قيمة المنظور مدى دراماتيكية التأثير ثلاثي الأبعاد. تمثل المسافة الافتراضية بين المشاهد ومستوى z=0 بالبكسل. القيم الأصغر تنشئ تأثيرًا ثلاثي الأبعاد أكثر دراماتيكية ومبالغًا فيه (مثل حمل شيء قريبًا جدًا من عينيك)، بينما القيم الأكبر تنشئ تأثيرًا أكثر دقة وطبيعية (مثل المشاهدة من مسافة بعيدة).

مثال: مقارنة قيم المنظور

/* دراماتيكي جدًا -- قريب من العنصر */
.dramatic {
    perspective: 200px;
}

/* معتدل -- افتراضي جيد لمعظم التأثيرات */
.moderate {
    perspective: 800px;
}

/* دقيق -- نقطة مشاهدة بعيدة */
.subtle {
    perspective: 2000px;
}

/* النطاقات الموصى بها للتأثيرات الشائعة: */
/* قلب البطاقات: 600px - 1200px */
/* المكعبات ثلاثية الأبعاد: 800px - 1500px */
/* تأثيرات التحويم الدقيقة: 1000px - 2000px */
/* انتقالات الصفحات الدراماتيكية: 300px - 600px */

perspective-origin -- تحريك نقطة المشاهدة

تحدد خاصية perspective-origin من أين ينظر المشاهد. بشكل افتراضي، تكون مضبوطة على مركز العنصر (50% 50%). تغييرها يزيح نقطة التلاشي، مما يجعل العناصر تبدو كما لو كانت مُشاهَدة من زاوية مختلفة. تُطبَّق على نفس العنصر الأب الذي يحمل خاصية perspective.

مثال: قيم perspective-origin

.scene {
    perspective: 1000px;
    perspective-origin: center center;
    /* الافتراضي: النظر مباشرة إلى المركز */
}

/* المشاهدة من أعلى اليسار */
.scene-top-left {
    perspective: 1000px;
    perspective-origin: left top;
    /* العناصر تدور كما لو شوهدت من أعلى اليسار */
}

/* المشاهدة من الجانب الأيمن */
.scene-right {
    perspective: 1000px;
    perspective-origin: 100% 50%;
    /* العناصر تدور كما لو شوهدت من اليمين */
}

/* موضع مخصص */
.scene-custom {
    perspective: 1000px;
    perspective-origin: 25% 75%;
}

/* تفاعلي: تغيير perspective-origin مع حركة الماوس */
/* (عادة يتم عبر JavaScript) */
.scene-interactive {
    perspective: 1000px;
    perspective-origin: var(--mouse-x, 50%) var(--mouse-y, 50%);
}

دوال الدوران ثلاثي الأبعاد

يوفر CSS دوال دوران لكل محور من المحاور الثلاثة، بالإضافة إلى دالة مدمجة للدوران حول محور عشوائي.

rotateX() -- الدوران حول المحور الأفقي

تدور دالة rotateX() عنصرًا حول المحور الأفقي X، مثل باب ينقلب للأمام أو للخلف. القيم الموجبة تميل الجزء العلوي من العنصر بعيدًا عن المشاهد (للخلف)، بينما القيم السالبة تميل الجزء العلوي نحو المشاهد (للأمام).

مثال: عروض rotateX()

.scene {
    perspective: 800px;
}

/* إمالة للخلف -- الأعلى يبتعد */
.tilt-back {
    transform: rotateX(30deg);
}

/* إمالة للأمام -- الأعلى يقترب من المشاهد */
.tilt-forward {
    transform: rotateX(-30deg);
}

/* قلب كامل على المحور الأفقي */
.flip-vertical {
    transition: transform 0.6s ease;
}

.flip-vertical:hover {
    transform: rotateX(180deg);
}

rotateY() -- الدوران حول المحور العمودي

تدور دالة rotateY() عنصرًا حول المحور العمودي Y، مثل باب دوار أو صفحة تُقلَب في كتاب. القيم الموجبة تدور الجانب الأيمن بعيدًا عن المشاهد، بينما القيم السالبة تدور الجانب الأيسر بعيدًا.

مثال: عروض rotateY()

.scene {
    perspective: 800px;
}

/* تدوير الجانب الأيمن بعيدًا */
.rotate-right {
    transform: rotateY(45deg);
}

/* تدوير الجانب الأيسر بعيدًا */
.rotate-left {
    transform: rotateY(-45deg);
}

/* قلب بطاقة كلاسيكي على المحور Y */
.card-flip {
    transition: transform 0.8s ease;
}

.card-flip:hover {
    transform: rotateY(180deg);
}

rotateZ() -- الدوران حول محور العمق

تدور دالة rotateZ() عنصرًا حول المحور Z، وهو نفس دالة rotate() ثنائية الأبعاد. تدور العنصر في مستوى الشاشة. مدرجة هنا للاكتمال، كونها جزءًا من عائلة الدوران ثلاثي الأبعاد.

مثال: rotateZ()

/* هذه متكافئة */
.spin-2d {
    transform: rotate(45deg);
}

.spin-z {
    transform: rotateZ(45deg);
}

/* دمج محاور الدوران الثلاثة */
.complex-rotation {
    transform: rotateX(20deg) rotateY(30deg) rotateZ(10deg);
}

rotate3d() -- الدوران حول محور عشوائي

تتيح لك دالة rotate3d() تحديد محور دوران مخصص باستخدام متجه (x, y, z) وزاوية. لا يحتاج متجه المحور إلى أن يكون مُطبَّعًا (المتصفح يطبعه داخليًا). هذا مفيد عندما تحتاج دورانًا لا يتوافق مع أي من المحاور القياسية.

مثال: rotate3d() محور مخصص

/* rotate3d(x, y, z, angle) */

/* مثل rotateX(45deg) */
.around-x {
    transform: rotate3d(1, 0, 0, 45deg);
}

/* مثل rotateY(45deg) */
.around-y {
    transform: rotate3d(0, 1, 0, 45deg);
}

/* دوران حول محور مائل (X و Y مدمجان) */
.diagonal {
    transform: rotate3d(1, 1, 0, 45deg);
    /* يدور حول محور مائل 45 درجة */
}

/* محور مخصص بمكوّن Z */
.custom-axis {
    transform: rotate3d(1, 2, 0.5, 60deg);
}

الإزاحة ثلاثية الأبعاد: translateZ() و translate3d()

تنقل دالة translateZ() عنصرًا على طول المحور Z، نحو المشاهد أو بعيدًا عنه. القيم الموجبة تحرك العنصر أقرب (مما يجعله يبدو أكبر عند تطبيق المنظور)، بينما القيم السالبة تدفعه أبعد (مما يجعله يبدو أصغر). تجمع دالة translate3d() المحاور الثلاثة في استدعاء دالة واحد.

مثال: translateZ() و translate3d()

.scene {
    perspective: 1000px;
}

/* التحريك نحو المشاهد (يبدو أكبر) */
.closer {
    transform: translateZ(200px);
}

/* التحريك بعيدًا عن المشاهد (يبدو أصغر) */
.further {
    transform: translateZ(-200px);
}

/* دمج المحاور الثلاثة */
.move-3d {
    transform: translate3d(50px, -20px, 100px);
    /* X: 50 بكسل لليمين, Y: 20 بكسل للأعلى, Z: 100 بكسل أقرب */
}

/* تأثير تحويم: العنصر يبرز نحو المشاهد */
.pop-card {
    transition: transform 0.3s ease;
}

.pop-card:hover {
    transform: translateZ(50px);
    /* البطاقة تبدو وكأنها تخرج من الشاشة */
}

/* مهم: translateZ مع النسب المئوية */
/* على عكس translateX/Y، لا تقبل translateZ قيم % */
/* يجب استخدام وحدات طول مطلقة (px, em, rem, إلخ.) */
نصيحة احترافية: غالبًا ما تُستخدم دالة translateZ() لترقية العناصر إلى طبقة تركيب GPU خاصة بها (عبر translateZ(0) أو translate3d(0, 0, 0))، مما يمكن أن يحسن أداء الرسوم المتحركة. ومع ذلك، هذه حيلة حلّ محلها إلى حد كبير will-change: transform، وهي الطريقة الصحيحة للتلميح بترقية الطبقة للمتصفح.

التحجيم ثلاثي الأبعاد: scale3d()

تحجم دالة scale3d() عنصرًا على طول المحاور الثلاثة في آنٍ واحد. تحجيم المحور Z له تأثير مرئي فقط على العناصر المحوَّلة بالفعل في فضاء ثلاثي الأبعاد (لديها عناصر فرعية موضوعة على أعماق Z مختلفة).

مثال: scale3d()

/* تحجيم المحاور الثلاثة */
.scale-all {
    transform: scale3d(1.5, 1.5, 1.5);
}

/* scaleZ مهم فقط للعناصر ذات الأبناء ثلاثيي الأبعاد */
.cube-container {
    transform-style: preserve-3d;
    transform: scale3d(1, 1, 2);
    /* يمد المكعب على طول محور Z */
}

transform-style: preserve-3d مقابل flat

تحدد خاصية transform-style ما إذا كانت العناصر الفرعية لعنصر محوَّل موجودة في فضاء ثلاثي الأبعاد أو مسطحة في مستوى العنصر الأب. هذه واحدة من أهم الخصائص لبناء التركيبات ثلاثية الأبعاد.

مثال: مقارنة transform-style

/* الافتراضي: العناصر الفرعية مسطحة */
.flat-parent {
    transform-style: flat;
    transform: rotateY(30deg);
}
.flat-parent .child {
    transform: rotateX(45deg);
    /* دوران الابن مسطح في مستوى الأب */
    /* لا يدور بشكل مستقل في الفضاء ثلاثي الأبعاد */
}

/* preserve-3d: الأبناء يحتفظون بموضعهم ثلاثي الأبعاد */
.preserve-parent {
    transform-style: preserve-3d;
    transform: rotateY(30deg);
}
.preserve-parent .child {
    transform: rotateX(45deg);
    /* الابن يدور في فضاء ثلاثي الأبعاد حقيقي نسبة للأب */
    /* كلا التحويلين يُركَّبان في نفس المشهد ثلاثي الأبعاد */
}
مهم: عدة خصائص CSS تجبر transform-style على التصرف كـ flat حتى عندما تحدد preserve-3d. هذه تشمل overflow: hidden (أو auto أو scroll)، و opacity بقيمة أقل من 1، و filter، و clip-path، و isolation: isolate. إذا بدا تأثيرك ثلاثي الأبعاد مسطحًا فجأة، تحقق مما إذا كانت أي من هذه الخصائص مطبقة على الأب أو عنصر سلف.

backface-visibility -- إخفاء الجانب الخلفي

عندما يُدوَّر عنصر أكثر من 90 درجة، يصبح وجهه الخلفي مرئيًا. بشكل افتراضي، يعرض المتصفح نسخة معكوسة من محتوى الوجه الأمامي. تتيح لك خاصية backface-visibility إخفاء الوجه الخلفي، وهو أمر ضروري لتأثيرات قلب البطاقات حيث تريد عرض محتوى مختلف تمامًا على الظهر.

مثال: backface-visibility

/* الافتراضي: الوجه الخلفي مرئي (محتوى معكوس) */
.show-back {
    backface-visibility: visible;
}

/* إخفاء الوجه الخلفي */
.hide-back {
    backface-visibility: hidden;
    /* عند الدوران بعد 90 درجة، يختفي العنصر */
}

/* هذا ضروري لنمط قلب البطاقة */
.card-front {
    backface-visibility: hidden;
    /* يُخفى عندما تنقلب البطاقة لتظهر الخلف */
}

.card-back {
    backface-visibility: hidden;
    transform: rotateY(180deg);
    /* مُدوَّر مسبقًا، مخفي بشكل افتراضي */
    /* يصبح مرئيًا عندما يدور الأب 180 درجة */
}

بناء تأثير قلب البطاقة ثلاثي الأبعاد

قلب البطاقة هو واحد من أكثر تأثيرات CSS ثلاثية الأبعاد شيوعًا. ينشئ بطاقة ذات وجهين تنقلب لتكشف محتوى مختلفًا على كل جانب. يتطلب هذا النمط جميع المفاهيم ثلاثية الأبعاد المغطاة حتى الآن: المنظور، preserve-3d، backface-visibility، و rotateY.

مثال: قلب بطاقة ثلاثي الأبعاد كامل

<style>
.card-container {
    width: 320px;
    height: 420px;
    perspective: 1000px;
}

.card {
    position: relative;
    width: 100%;
    height: 100%;
    transform-style: preserve-3d;
    transition: transform 0.8s cubic-bezier(0.4, 0, 0.2, 1);
}

/* القلب عند التحويم */
.card-container:hover .card {
    transform: rotateY(180deg);
}

.card-front,
.card-back {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    backface-visibility: hidden;
    border-radius: 16px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 32px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
}

.card-front {
    background: linear-gradient(135deg, var(--primary), var(--primary-light));
    color: white;
}

.card-back {
    background: var(--bg-white);
    color: var(--text-dark);
    transform: rotateY(180deg);
    /* مُدوَّر مسبقًا ليكون مخفيًا في البداية */
    /* عندما يدور الأب 180 درجة، هذا يصبح مرئيًا */
}
</style>

<div class="card-container">
    <div class="card">
        <div class="card-front">
            <h3>الجانب الأمامي</h3>
            <p>مرر للقلب</p>
        </div>
        <div class="card-back">
            <h3>الجانب الخلفي</h3>
            <p>محتوى مخفي يُكشَف</p>
        </div>
    </div>
</div>
ملاحظة: لإمكانية الوصول، تأكد من أن محتوى الجانب الخلفي لا يُقرأ بواسطة قارئات الشاشة عندما لا يكون مرئيًا. يمكنك استخدام aria-hidden="true" على الوجه الخلفي والتبديل عبر JavaScript عند قلب البطاقة. فكر أيضًا في توفير قلب قائم على النقر لأجهزة اللمس، حيث أن التحويم لا يعمل على الأجهزة المحمولة.

القلب على المحور X

يعمل نفس النمط للقلب العمودي بتغيير rotateY إلى rotateX. الوجه الخلفي يكون مُدوَّرًا مسبقًا على نفس المحور.

مثال: قلب بطاقة عمودي (المحور X)

.card-container:hover .card {
    transform: rotateX(180deg);
}

.card-back {
    transform: rotateX(180deg);
    /* مُدوَّر مسبقًا على X بدلاً من Y */
}

بناء مكعب ثلاثي الأبعاد بـ CSS

المكعب في CSS هو تمرين كلاسيكي يوضح إتقان التحويلات ثلاثية الأبعاد. كل وجه هو عنصر بتموضع مطلق مُدوَّر ومُزاح إلى مكانه. حاوية المكعب تستخدم transform-style: preserve-3d للحفاظ على جميع الأوجه في نفس الفضاء ثلاثي الأبعاد.

مثال: مكعب ثلاثي الأبعاد كامل

<style>
.cube-scene {
    width: 200px;
    height: 200px;
    perspective: 600px;
    margin: 100px auto;
}

.cube {
    width: 200px;
    height: 200px;
    position: relative;
    transform-style: preserve-3d;
    transform: rotateX(-25deg) rotateY(30deg);
    transition: transform 1s ease;
}

.cube:hover {
    transform: rotateX(-25deg) rotateY(210deg);
}

.cube-face {
    position: absolute;
    width: 200px;
    height: 200px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    font-weight: bold;
    border: 2px solid rgba(0, 0, 0, 0.1);
    opacity: 0.85;
}

/* الوجه الأمامي: بدون دوران، فقط تحريك للأمام */
.cube-front {
    background: rgba(255, 0, 0, 0.6);
    transform: translateZ(100px);
}

/* الوجه الخلفي: دوران 180 درجة، تحريك للأمام */
.cube-back {
    background: rgba(0, 255, 0, 0.6);
    transform: rotateY(180deg) translateZ(100px);
}

/* الوجه الأيمن: دوران 90 درجة على Y، تحريك للأمام */
.cube-right {
    background: rgba(0, 0, 255, 0.6);
    transform: rotateY(90deg) translateZ(100px);
}

/* الوجه الأيسر: دوران -90 درجة على Y، تحريك للأمام */
.cube-left {
    background: rgba(255, 255, 0, 0.6);
    transform: rotateY(-90deg) translateZ(100px);
}

/* الوجه العلوي: دوران -90 درجة على X، تحريك للأمام */
.cube-top {
    background: rgba(255, 0, 255, 0.6);
    transform: rotateX(90deg) translateZ(100px);
}

/* الوجه السفلي: دوران 90 درجة على X، تحريك للأمام */
.cube-bottom {
    background: rgba(0, 255, 255, 0.6);
    transform: rotateX(-90deg) translateZ(100px);
}
</style>

<div class="cube-scene">
    <div class="cube">
        <div class="cube-face cube-front">أمام</div>
        <div class="cube-face cube-back">خلف</div>
        <div class="cube-face cube-right">يمين</div>
        <div class="cube-face cube-left">يسار</div>
        <div class="cube-face cube-top">أعلى</div>
        <div class="cube-face cube-bottom">أسفل</div>
    </div>
</div>

الفكرة الأساسية لبناء المكعب هي أن كل وجه يُزاح على طول المحور Z بمقدار نصف عرض المكعب بعد أن يُدوَّر ليواجه الاتجاه الصحيح. translateZ(100px) تدفع كل وجه للخارج من المركز بمقدار 100 بكسل (نصف المكعب بحجم 200 بكسل). الدوران قبل الإزاحة يضمن أن الوجه موجه بشكل صحيح قبل دفعه للخارج.

نصيحة احترافية: عند بناء المكعب، فكر في كل وجه على أنه يبدأ في المركز، ثم يدور ليواجه الاتجاه الصحيح، ثم يُزاح للخارج. الإزاحة تحدث دائمًا على طول محور Z المحلي للوجه (العمودي على الوجه)، وهذا سبب عمل translateZ لكل وجه بعد أن يوجهه الدوران بشكل صحيح.

مفهوم العرض الدائري ثلاثي الأبعاد

ينظم العرض الدائري ثلاثي الأبعاد العناصر في دائرة باستخدام rotateY و translateZ. يُدوَّر كل عنصر حول المحور Y بجزء متساوٍ من 360 درجة، ثم يُدفع للخارج بـ translateZ. تدور الحاوية لعرض عناصر مختلفة.

مثال: تخطيط العرض الدائري ثلاثي الأبعاد

<style>
.carousel-scene {
    width: 250px;
    height: 180px;
    perspective: 1200px;
    margin: 100px auto;
}

.carousel {
    width: 100%;
    height: 100%;
    position: relative;
    transform-style: preserve-3d;
    transition: transform 1s ease;
}

/* 6 عناصر: كل واحد مُدوَّر 60 درجة (360 / 6) */
.carousel-item {
    position: absolute;
    width: 250px;
    height: 180px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    font-weight: bold;
    background: var(--bg-white);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}

/* حساب translateZ بناءً على عدد العناصر والعرض */
/* لـ 6 عناصر: translateZ = width / (2 * tan(pi/6)) تقريبًا 217px */
.carousel-item:nth-child(1) { transform: rotateY(0deg)   translateZ(217px); }
.carousel-item:nth-child(2) { transform: rotateY(60deg)  translateZ(217px); }
.carousel-item:nth-child(3) { transform: rotateY(120deg) translateZ(217px); }
.carousel-item:nth-child(4) { transform: rotateY(180deg) translateZ(217px); }
.carousel-item:nth-child(5) { transform: rotateY(240deg) translateZ(217px); }
.carousel-item:nth-child(6) { transform: rotateY(300deg) translateZ(217px); }

/* التدوير لعرض عنصر محدد */
.carousel.show-1 { transform: rotateY(0deg); }
.carousel.show-2 { transform: rotateY(-60deg); }
.carousel.show-3 { transform: rotateY(-120deg); }
.carousel.show-4 { transform: rotateY(-180deg); }

/* رسم متحرك للتدوير التلقائي */
@keyframes spin-carousel {
    from { transform: rotateY(0deg); }
    to   { transform: rotateY(-360deg); }
}

.carousel.auto-spin {
    animation: spin-carousel 20s linear infinite;
}
</style>

<div class="carousel-scene">
    <div class="carousel">
        <div class="carousel-item">1</div>
        <div class="carousel-item">2</div>
        <div class="carousel-item">3</div>
        <div class="carousel-item">4</div>
        <div class="carousel-item">5</div>
        <div class="carousel-item">6</div>
    </div>
</div>

معادلة نصف القطر لـ translateZ في عرض دائري بـ n عناصر بعرض w هي: radius = w / (2 * tan(PI / n)). لـ 6 عناصر بعرض 250 بكسل، هذا يعطي تقريبًا 217 بكسل. تعديل هذه القيمة يغير مدى انتشار العناصر.

اعتبارات الأداء للتحويلات ثلاثية الأبعاد

التحويلات ثلاثية الأبعاد مُسرَّعة بالـ GPU، مما يجعلها عالية الأداء للرسوم المتحركة. ومع ذلك، هناك اعتبارات مهمة يجب وضعها في الاعتبار.

  • إنشاء الطبقات. العناصر ذات التحويلات ثلاثية الأبعاد تُرقَّى إلى طبقات تركيب خاصة بها. كل طبقة تستهلك ذاكرة GPU. الكثير من الطبقات (خاصة الكبيرة) يمكن أن يقلل الأداء على الأجهزة المحمولة ذات ذاكرة GPU المحدودة.
  • preserve-3d والرسم. العناصر ضمن سياق preserve-3d لا يمكن تحسينها بشكل مستقل من قبل المتصفح. المشهد ثلاثي الأبعاد بأكمله يجب أن يُركَّب معًا، وهو ما يمكن أن يكون أكثر تكلفة من العرض المسطح.
  • تجنب تحريك perspective. تحريك خاصية perspective يجبر المتصفح على إعادة حساب المشهد ثلاثي الأبعاد بأكمله كل إطار. بدلاً من ذلك، حرّك تحويلات العناصر الفرعية مع إبقاء المنظور ثابتًا.
  • قلل ثلاثي الأبعاد غير الضروري. إذا لم يكن العنصر بحاجة للمشاركة في الفضاء ثلاثي الأبعاد، لا تطبق preserve-3d على أبيه. القيمة الافتراضية flat أكثر كفاءة.
  • استخدم will-change بحكمة. طبّق will-change: transform فقط على العناصر التي ستُحرَّك فعلاً، وأزلها بعد اكتمال الرسم المتحرك لتحرير موارد GPU.

مثال: رسم متحرك ثلاثي الأبعاد محسّن الأداء

/* التحضير للرسم المتحرك فقط عند الحاجة */
.card-container {
    perspective: 1000px;
}

.card {
    transform-style: preserve-3d;
    transition: transform 0.6s ease;
    /* will-change غير مضبوط افتراضيًا */
}

/* إضافة will-change قبل تشغيل الرسم المتحرك مباشرة */
.card-container:hover .card {
    will-change: transform;
}

/* بعد انتهاء الانتقال، أزل will-change عبر JS:
   element.addEventListener('transitionend', () => {
       element.style.willChange = 'auto';
   });
*/

دعم المتصفحات والبدائل

التحويلات ثلاثية الأبعاد مدعومة جيدًا في جميع المتصفحات الحديثة. ومع ذلك، هناك بعض الدقائق التي يجب مراعاتها للتوافق بين المتصفحات.

مثال: تحسين تدريجي للتأثيرات ثلاثية الأبعاد

/* الأنماط الأساسية تعمل بدون ثلاثي الأبعاد */
.card-front,
.card-back {
    position: absolute;
    width: 100%;
    height: 100%;
    transition: opacity 0.3s ease;
}

.card-back {
    opacity: 0;
}

.card-container:hover .card-front {
    opacity: 0;
}

.card-container:hover .card-back {
    opacity: 1;
}

/* قلب ثلاثي الأبعاد محسّن للمتصفحات الداعمة */
@supports (transform-style: preserve-3d) {
    .card {
        transform-style: preserve-3d;
        transition: transform 0.8s ease;
    }

    .card-front,
    .card-back {
        backface-visibility: hidden;
        transition: none;
        opacity: 1;
    }

    .card-back {
        transform: rotateY(180deg);
        opacity: 1;
    }

    .card-container:hover .card {
        transform: rotateY(180deg);
    }

    .card-container:hover .card-front,
    .card-container:hover .card-back {
        opacity: 1;
    }
}

أمثلة عملية

زر ثلاثي الأبعاد تفاعلي

زر يبدو وكأنه يميل في الفضاء ثلاثي الأبعاد عند التحويم، مما يخلق شعورًا ملموسًا وقابلاً للضغط.

مثال: زر مائل ثلاثي الأبعاد

.button-scene {
    perspective: 600px;
    display: inline-block;
}

.button-3d {
    padding: 16px 40px;
    font-size: 18px;
    font-weight: 600;
    color: white;
    background: var(--primary);
    border: none;
    border-radius: 8px;
    cursor: pointer;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
    transform-origin: center bottom;
}

.button-3d:hover {
    transform: rotateX(-15deg);
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
}

.button-3d:active {
    transform: rotateX(-5deg) translateY(2px);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

كشف لوحة ثلاثية الأبعاد

لوحة تنفتح مثل الباب لتكشف المحتوى خلفها.

مثال: لوحة تُفتح كالباب

.panel-scene {
    perspective: 1500px;
    width: 400px;
    height: 300px;
    position: relative;
}

.panel-door {
    width: 100%;
    height: 100%;
    background: var(--primary);
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transform-origin: left center;
    transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
    position: absolute;
    z-index: 2;
    border-radius: 12px;
}

.panel-door:hover {
    transform: rotateY(-110deg);
}

.panel-content {
    width: 100%;
    height: 100%;
    background: var(--bg-light);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    border-radius: 12px;
    position: absolute;
    z-index: 1;
}

رزمة بطاقات متراكمة

بطاقات متعددة متراكمة في فضاء ثلاثي الأبعاد تنفتح مروحيًا عند التحويم.

مثال: انفتاح مروحي لرزمة بطاقات ثلاثية الأبعاد

.stack-scene {
    perspective: 1000px;
    width: 300px;
    height: 200px;
    position: relative;
}

.stack-scene:hover .stack-card:nth-child(1) {
    transform: translateZ(0px) rotateY(0deg);
}

.stack-scene:hover .stack-card:nth-child(2) {
    transform: translateZ(-30px) rotateY(10deg) translateX(30px);
}

.stack-scene:hover .stack-card:nth-child(3) {
    transform: translateZ(-60px) rotateY(20deg) translateX(60px);
}

.stack-card {
    position: absolute;
    width: 100%;
    height: 100%;
    background: var(--bg-white);
    border-radius: 12px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    transition: transform 0.5s ease;
    transform-style: preserve-3d;
}

دمج التحويلات ثنائية وثلاثية الأبعاد

يمكنك الخلط بحرية بين دوال التحويل ثنائية وثلاثية الأبعاد في إعلان transform واحد. الدوال ثنائية الأبعاد مثل translateX() و scale() و skew() تعمل جنبًا إلى جنب مع الدوال ثلاثية الأبعاد مثل rotateY() و translateZ().

مثال: تحويلات مختلطة ثنائية وثلاثية الأبعاد

.combined {
    transform: perspective(800px)
               translateY(-10px)
               rotateX(5deg)
               rotateY(15deg)
               scale(1.02);
    /* جميع الدوال تتركب في مصفوفة تحويل واحدة */
}

/* عملي: بطاقة مائلة مع رفع عند التحويم */
.tilt-card {
    transition: transform 0.4s ease;
}

.tilt-card:hover {
    transform: perspective(1000px)
               translateY(-8px)
               rotateX(3deg)
               rotateY(-3deg)
               scale(1.03);
}

تصحيح التحويلات ثلاثية الأبعاد

يمكن أن يكون تصحيح CSS ثلاثي الأبعاد تحديًا لأن العناصر قد تصبح غير مرئية أو تبدو مشوهة. إليك نصائح عملية لاستكشاف الأخطاء.

  • تحقق من المنظور. إذا بدت تحويلاتك ثلاثية الأبعاد مسطحة، تأكد من أن الأب لديه قيمة perspective. بدونها، تنتج rotateX و rotateY نتائج مسطحة المظهر.
  • تحقق من preserve-3d. إذا لم تظهر العناصر الفرعية في فضاء ثلاثي الأبعاد نسبة لأبيها، تأكد من ضبط transform-style: preserve-3d على الأب.
  • ابحث عن تعارضات الخصائص. تذكر أن overflow: hidden، و opacity < 1، و filter، و clip-path على عنصر سلف ستجبر transform-style على flat.
  • استخدم خلفيات شبه شفافة. عند بناء كائنات ثلاثية الأبعاد مثل المكعبات، استخدم ألوانًا شبه شفافة حتى تتمكن من الرؤية عبر الأوجه والتحقق من موضعها.
  • قلل المنظور مؤقتًا. استخدم قيمة منظور منخفضة جدًا (مثل 200 بكسل) لتضخيم التأثير ثلاثي الأبعاد وجعل أخطاء التموضع واضحة.
  • أدوات المطور في المتصفح. يحتوي Chrome DevTools على ميزة عرض ثلاثي الأبعاد (في لوحة الطبقات) تتيح لك فحص طبقات التركيب ورؤية عناصرك ثلاثية الأبعاد من زوايا مختلفة.

تمرين عملي

ابنِ مشروعًا يوضح كل مفهوم رئيسي من هذا الدرس. (1) أنشئ مكوّن قلب بطاقة ثلاثي الأبعاد يكشف محتوى مختلفًا على كل جانب. الأمام يجب أن يعرض صورة وعنوانًا، والخلف يجب أن يعرض وصفًا ورابطًا. استخدم perspective على الأب، و preserve-3d على البطاقة، و backface-visibility: hidden على كلا الوجهين. (2) ابنِ مكعب CSS ثلاثي الأبعاد كاملاً يمكن تدويره لعرض أوجه مختلفة باستخدام أزرار الراديو أو فئات CSS تُبدَّل عند التحويم. كل وجه يجب أن يكون له لون وتسمية مميزين. (3) أنشئ عرضًا دائريًا ثلاثي الأبعاد بخمسة عناصر على الأقل. احسب نصف قطر translateZ الصحيح لعدد عناصرك وعرضها. أضف رسمًا متحركًا للدوران التلقائي البطيء بـ @keyframes. (4) ابنِ لوحة تنفتح مثل الباب باستخدام rotateY مع transform-origin على اليسار. (5) استخدم قاعدة @supports لتوفير بديل قائم على الشفافية لقلب البطاقة في حال لم يدعم المتصفح preserve-3d. اختبر جميع المكونات عبر Chrome و Firefox و Safari. افحص طبقات التركيب في DevTools لفهم كيف يتعامل المتصفح مع عناصرك ثلاثية الأبعاد.