كيفية إنشاء تحريكات مشابهة لتلك التي في Windows 8 باستخدام CSS3 و jQuery


حسام برهان

رغم أنّ تحويلات CSS3 ثلاثية الأبعاد صار لها فترةً لا بأس بها من الزمن، إلّا أنّني اكتشفت عدم امتلاك الخبرة الكافية للعمل معها بعد. أستخدم Windows 8 منذ فترة من الوقت، ومن أوّل الأمور التي لفتت نظري كانت الانتقالات transitions والتحريكات animations المبنية ضمن لوح البداية Start Dashboard، لذلك خطرت ببالي فكرة رائعة، وهي لماذا لا تكون خبرتي الأولى مع تحويلات CSS3 ثلاثية البعد ببناء تطبيق يماثل في سلوكه تلك التحريكات والتأثيرات في Windows 8؟ وهذا ما حدث في هذا الدرس.

windows8-animation-css3-jquery.thumb.png

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

الرماز The Markup

بنية هذا التطبيق بسيطة: يتكون لوح البداية من قائمة من القطع tiles بثلاثة قياسات وهي الصغير، الكبير، والكبير جدًا. لكل قطعة من هذه القطع صفحة page مرتبطة معها. والصفحة عبارة عن تغطية overlay تظهر عند النقر على إحدى القطع في لوح البداية. سنعتبر الصفحة أنّها تُحاكي تطبيق سطح مكتب في Windows 8، فتكون القطعة كما هو واضح اختصارًا له (للصفحة).

عند النقر على قطعة ستُفتح الصفحة الموافقة لها. يوجد نوعان من الانتقالات للصفحة بعد فتحها يدعمهما اللوح في Windows 8. يفتح أحدهما الصفحة بتأثير دوران ثلاثي الأبعاد اعتبارًًا من يمين الشاشة، أمّا الآخر فتنزلق فيه الصفحة من وإلى اليسار. سنعرّف صنف CSS لكل نوع من نوعيّ الصفحة، حيث سيكون الصنف s-page للصفحات التي تنزلق من وإلى اليسار، أمّا الصنف r-page فسيكون للصفحات التي تدور من اليمين.

ينبغي علينا من أجل كل قطعة تعيين نوع الصفحة التي ستفتحها القطعة (بالاعتماد على التأثير الذي نريده من أجل هذه الصفحة). سنعرّف نوع الصفحة لكلّ قطعة باستخدام سمة attribute مخصّصة سنسميها data-type-page، ستهتم هذه الـسمة بتطبيق أسماء أصناف CSS الصحيحة التي ستفعّل التحريكات المناسبة فيما بعد.

يجب أن يكون لكل صفحة اسم. سيختلف اسم الصفحة لتطبيق معيّن عن اسم الصفحة لتطبيق آخر، فمثلًا الـقطعة المسمّاة Skype ستفتح صفحة اسمها skype-app وهكذا دواليك. لقد استخدمت اسميّ صفحة فقط في هذا المثال، وقد كرّرتهما من أجل جميع الـقطع الباقية بغرض التبسيط، كما استخدمت الاسم custom-page لآخر قطعة وذلك على سبيل المثال. ربما تحتاج أن تضيف صفحة مختلفة لكل قطعة، وهذا يعني اسم صفحة مختلف لكلٍّ منها. 

إليك الرماز الخاص بكامل اللوح (الـقطع والصفحات):

<div class="demo-wrapper"> 
  <div class="s-page random-restored-page"> 
    <div class="page-content"> 
      <h2 class="page-title">Some minimized App</h2> 
      <div class="close-button s-close-button">x</div> 
    </div> 
  </div> 

  <div class="s-page custom-page"> 
    <div class="page-content"> 
      <h2 class="page-title">Thank You!</h2> 
      <div class="close-button s-close-button">x</div> 
    </div> 
  </div> 

  <div class="r-page random-r-page"> 
    <div class="page-content"> 
      <h2 class="page-title">App Screen</h2> 
      <p>Chew iPad power cord chew iPad power cord attack feet chase mice leave dead animals as gifts and stick butt in face chew iPad power cord. Chase mice. Run in circles use lap as chair why must they do that. Intrigued by the shower destroy couch leave hair everywhere sleep on keyboard chew iPad power cord. Use lap as chair. Missing until dinner time stand in front of the computer screen, intently sniff hand. Find something else more interesting. Destroy couch play time so inspect anything brought into the house hate dog burrow under covers. Sleep on keyboard destroy couch so hate dog so hide when guests come over. Chase mice destroy couch lick butt throwup on your pillow use lap as chair yet intrigued by the shower but climb leg. Stare at ceiling make muffins or hunt anything that moves claw drapes. Intently sniff hand intrigued by the shower. Why must they do that. Cat snacks leave dead animals as gifts or inspect anything brought into the house sweet beast so stare at ceiling give attitude. Flop over claw drapes but sun bathe lick butt, and chase mice. Rub face on everything lick butt leave hair everywhere lick butt, missing until dinner time for use lap as chair lick butt. Make muffins leave dead animals as gifts play time. Chew foot intrigued by the shower stare at ceiling inspect anything brought into the house yet hopped up on goofballs. Hunt anything that moves intently sniff hand for hunt anything that moves play time. Chew foot climb leg throwup on your pillow so lick butt yet make muffins hate dog. Intrigued by the shower. Intently sniff hand shake treat bag. Cat snacks burrow under covers make muffins but all of a sudden go crazy find something else more interesting. Flop over chase mice. Give attitude. Inspect anything brought into the house. Stick butt in face sun bathe so find something else more interesting and intrigued by the shower. Rub face on everything use lap as chair. Under the bed claw drapes chase mice but leave hair everywhere yet make muffins yet claw drapes. Use lap as chair. Find something else more interesting stretch for under the bed. Nap all day intrigued by the shower, hate dog sweet beast intently sniff hand so hate dog nap all day. Swat at dog hide when guests come over and mark territory chase mice for cat snacks. Use lap as chair. Lick butt throwup on your pillow need to chase tail. Mark territory. Stick butt in face shake treat bag yet hunt anything that moves, yet hopped up on goofballs yet stare at ceiling under the bed. Give attitude chase imaginary bugs stretch so hunt anything that moves so hide when guests come over but intrigued by the shower find something else more interesting. Make muffins behind the couch for chew foot. Sweet beast flop over but throwup on your pillow. Intently sniff hand use lap as chair and missing until dinner time and chase imaginary bugs. </p> 
    </div> 
    <div class="close-button r-close-button">x</div> 
  </div> 

  <div class="dashboard clearfix"> 
    <ul class="tiles"> 
      <div class="col1 clearfix"> 
        <li class="tile tile-big tile-1 slideTextUp" data-page-type="r-page" data-page-name="random-r-page"> 
          <div><p>This tile's content slides up</p></div> 
          <div><p>View all tasks</p></div> 
        </li> 

        <li class="tile tile-small tile tile-2 slideTextRight" data-page-type="s-page" data-page-name ="random-restored-page"> 
          <div><p class="icon-arrow-right"></p></div> 
          <div><p>Tile's content slides right. Page opens from left</p></div> 
        </li> 

        <li class="tile tile-small last tile-3" data-page-type="r-page" data-page-name="random-r-page"> 
          <p class="icon-calendar-alt-fill"></p> 
        </li> 

        <li class="tile tile-big tile-4" data-page-type="r-page" data-page-name="random-r-page"> 
          <figure> 
            <img src="images/blue.jpg" /> 
            <figcaption class="tile-caption caption-left">Slide-out Caption from left</figcaption>
          </figure> 
        </li> 
      </div> 

      <div class="col2 clearfix"> 
        <li class="tile tile-big tile-5" data-page-type="r-page" data-page-name="random-r-page"> 
          <div><p><span class="icon-cloudy"></span>Weather</p></div> 
        </li> 

        <li class="tile tile-big tile-6 slideTextLeft" data-page-type="r-page" data-page-name="random-r-page"> 
          <div><p><span class="icon-skype"></span>Skype</p></div> 
          <div><p>Make a Call</p></div> 
        </li> 

        <li class="tile tile-small tile-7 rotate3d rotate3dX" data-page-type="r-page" data-page-name="random-r-page"> 
          <div class="faces"> 
            <div class="front"><span class="icon-picassa"></span></div> 
            <div class="back"><p>Launch Picassa</p></div> 
          </div> 
        </li> 

        <li class="tile tile-small last tile-8 rotate3d rotate3dY" data-page-type="r-page" data-page-name="random-r-page"> 
          <div class="faces"> 
            <div class="front"><span class="icon-instagram"></span></div>
            <div class="back"><p>Launch Instagram</p></div> 
          </div> 
        </li> 
      </div> 

      <div class="col3 clearfix"> 
        <li class="tile tile-2xbig tile-9" data-page-type="custom-page" data-page-name="random-r-page"> 
          <figure> 
            <img src="images/summer.jpg" /> 
            <figcaption class="tile-caption caption-bottom">Fixed Caption: Some Subtitle or Tile Description Goes Here with some kinda link or anything 
          </figure> 
        </li> 

        <li class="tile tile-big tile-10" data-page-type="s-page" data-page-name="custom-page"> 
          <div><p>Windows-8-like Animations with CSS3 & jQuery © Sara Soueidan. Licensed under MIT.</p></div>
        </li> 
      </div> 
    </ul> 
  </div>
</div>

حصلت على خط الأيقونة الذي استخدمته من Icomoon.

الذي سيحدث الآن: ستحصل JavaScript على اسم ونوع الصفحة المراد فتحها عند نقر القطعة، وبعد ذلك، ووفقًا لنوع الصفحة ستعمل JavaScript على تطبيق أسماء أصناف CSS المناسبة على الصفحة (والتي سنحصل على اسمها أيضًا من السمة data-page-name) لفتحها بنمط تحريك معيّن لكل صنف CSS يتم تطبيقه.

تنسيقات CSS

أرجو ملاحظة أنّني أستخدم أنماط تنسيق تراعي الأجهزة المحمولة أولًا mobile-first، والتي سنجعلها لاحقًا ذات استجابية responsive عالية ضمن قسم استعلامات الوسائط media queries في CSS.

سنتناول في البداية التنسيقات الخاصة بالحاوية (عنصر div) والتي ستحوي كامل المثال التوضيحي. سنعرّف تنسيقات عامّة ونتأكّد من ضبط الخاصية perspective بحيث يؤدي ذلك إلى تفعيل الفضاء ثلاثي البعد 3D، وإلّا سيبدو كامل المثال بشكل منبسط أي ثنائي البعد 2D.

.demo-wrapper { 
  padding: 2em .5em; 
  width: 100%; height:100%; 
  perspective: 3300px; 
  position: relative; 
}

لنبدأ الآن بتنسيقات وتحريكات لوح البداية. 

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

.dashboard { 
  margin: 0 auto; 
  width: 100%; 
  padding: 1em; 
  transform: translateX(200px); 
  opacity:0; 
  animation: start 1s ease-out forwards; 
} 

@keyframes start{   
  0%{ transform: translateX(200px); opacity:0; } 
  50%{ opacity:1; } 
  100%{ transform: translateX(0); opacity:1; } 
}

يتضاءل لوح البداية ويختفي عند نقر قطعة منه. حيث ينتقل اللوح على طول محور z، ويتضاءل حجمه، كما تنخفض قيمة الخاصية opacity له تدريجيًا حتى تصبح في النهاية تساوي الصفر مما يعطي شعورًا بأنّ اللوح يختفي بالتدريج. أمّا عندما يُغلق المستخدم الصفحة فإنّ لوح البداية يعود للظهور مرّة أخرى.

بالنسبة للأعمدة الثلاثة في لوح البداية فإنّها تظهر تدريجيًّا واحدًا تلو الآخر مع تأخير زمني طفيف بينها. عندما تُغلق الصفحة، سيُضاف اسم صنف CSS لكل عمود (بواسطة JavaScript) وكل من هذه الأصناف ستُفعّل تحريكة بتأخير زمني مُحدّد. 

فيما يلي أصناف CSS والتحريكات المطبّقة على لوح البداية عند نقر القطع tiles وإغلاق الصفحات.

.fadeOutback{ 
  animation: fadeOutBack 0.3s ease-out 1 normal forwards; 
} 

.fadeInForward-1, .fadeInForward-2, .fadeInForward-3 { 
  opacity:0; 
  transform: translateZ(-5em) scale(0.75); 
  animation: fadeInForward .5s cubic-bezier(.03,.93,.43,.77) .4s normal forwards; 
} 

.fadeInForward-2{ 
  animation-delay: .55s; 
} 

.fadeInForward-3{ 
  animation-delay: .7s; 
} 

@keyframes fadeOutBack{  
  0% {transform: translateX(-2em) scale(1); opacity:1;} 
  70% {transform: translateZ(-5em) scale(0.6); opacity:0.5;} 
  95% {transform: translateZ(-5em) scale(0.6); opacity:0.5;} 
  100% {transform: translateZ(-5em) scale(0); opacity:0;} 
} 

@keyframes fadeInForward{  
  0% {transform: translateZ(-5em) scale(0); opacity:0;} 
  100% {transform: translateZ(0) scale(1); opacity:1;} 
}

وبالنسبة لتنسيقات الصفحات:

.r-page { 
  width: 100%; height: 100%; 
  text-align: center; font-size: 2em; font-weight: 300; 
  position: absolute; 
  right: 0; top: 0; left:0; bottom:0; 
  opacity: 0; 
  color: white; 
  z-index: 10; 
  padding:10px; 
  transform-origin: 100% 0%; 
  transform: rotateY(-90deg) translateZ(5em) 
} 

.s-page { 
  color: white; 
  z-index: 10; 
  text-align: center; font-size: 2em; font-weight: 300; 
} 

.page-content{ 
  overflow-y:auto; 
  max-height:100%; 
  font-size:.6em; 
  padding:.6em; 
  text-align:left; 
} 

.s-page, .r-page{ 
  background-color: white; 
  color:black; 
} 

.page-title { 
  margin: .25em 0; 
  font-weight: 100; font-size: 3em; text-align:center; 
} 

.close-button { 
  font-size: 1.5em; 
  width: 1em; height: 1em; 
  position: absolute; 
  top: .75em; right: .75em; 
  cursor: pointer; 
  line-height: .8em; text-align: center 
}

لقد ضبطت الموقع الأصلي لكل صفحة من النوع r-page في الفضاء ثلاثي البعد بتدويرها حول محور التراتيب (محور y) بعد ذلك نقل الصفحة بمقدار 5em إلى يسار الشاشة باستخدام الخاصية translateZ (النقل على محور z). ينبغي ألّا ننسى أنّه عند تحويل (نقل – تدوير) عنصر في الفضاء ثلاثي البعد فمن الضروري تحويل نظام الإحداثيات الخاص به بنفس الصورة. الذي نريده الآن هو نقل الصفحة بمقدار 5em إلى يسار الشاشة، ولكن لاحظ أنّنا بدلًا من استخدام translateX استخدمنا translateZ، ويعود سبب ذلك إلى أنّه بعد التحويل الأوّل (الدوران حول محور y) يدور نظام الإحداثيات أيضًا، وهكذا يُشير محور z في هذه الحالة إلى اليسار وليس إلى الأعلى، أمّا محور الفواصل (محور x) فسيشير باتجاه المستخدم.

يكون لجميع الصفحات باستثناء النوع s-page نفس موقع البداية في الفضاء ثلاثي البعد. بالنسبة للصفحات من النوع s-page فإنّها تكون بموقع يبعد بما يُعادل -150% يسار الشاشة (واضح أنّها لن تكون مرئية بهذه الحالة)، بحيث أنّها تنزلق لتعود إلى الشاشة عند تفعيل التحريكة الخاصة بها.

عند نقر قطعة مرتبطة بصفحة ما، فسيُضاف صنف CSS الموافق بواسطة JavaScript إلى الصفحة، بالنتيجة ستحصل الصفحة على اسم صنف CSS يُعرّف التأثير ثلاثي البعد الواجب تطبيقه عليها.

فيما يلي أسماء أصناف CSS التي تُفعّل عملية فتح وإغلاق الصفحات، بالإضافة إلى التحريكات المعرّفة من أجل كل صنف.

.openpage{ 
  animation: rotatePageInFromRight 1s cubic-bezier(.66,.04,.36,1.03) 1 normal forwards;
} 

.slidePageLeft{ 
  transform: rotateY(0) translateZ(0); 
  opacity: 1; 
  animation:slidePageLeft .8s ease-out 1 normal forwards; 
} 

.slidePageInFromLeft{ 
  animation: slidePageInFromLeft .8s cubic-bezier(.01,1,.22,.99) 1 0.25s normal forwards; 
} 

.slidePageBackLeft{ 
  opacity: 1; 
  left: 0; 
  animation: slidePageBackLeft .8s ease-out 1 normal forwards; 
}

لاحظ أنّني أستخدم الخاصية animation بالشكل المختصر. تعود القيمة الأخيرة forward ضمن الخاصية animation إلى الخاصيّة الفرعية animation-fill-mode، وهذه القيمة ضرورية لها، وإلّا ستعود الصفحة إلى حالتها الابتدائية (المغلقة) فور انتهاء التحريكة التي ستُظهر الصفحة. إذًا لكي نُبقي الصفحة مفتوحة، ولكي نكون قادرين على إنشاء تحريكات متلاحقة، يجب على العنصر أن يبقى مُحتفظًا بحالته النهائية المعرّفة ضمن تحركية ما، ومن هذه "الحالة النهائية" يبدأ عمل التحريكة التالية، وهكذا.

فيما يلي التحريكات لأصناف CSS المطبّقة على الصفحات.

@keyframes rotatePageInFromRight{ 
  0% {transform:rotateY(-90deg) translateZ(5em);opacity:0} 
  30% {opacity:1} 
  100% {transform: rotateY(0deg) translateZ(0) ; opacity:1} 
} 

@keyframes slidePageLeft{  
  0% {left:0; transform: rotateY(0deg) translateZ(0) ; opacity:1} 
  70% {opacity:1;} 
  100% {opacity:0; left:-150%; transform: rotateY(0deg)} 
} 

@keyframes slidePageInFromLeft{   
  0% {opacity:0; } 
  30% {opacity:1} 
  100% {opacity:1; left:0;} 
} 

@keyframes slidePageBackLeft{   
  0% {opacity:1; left:0; transform: scale(0.95);} 
  10% {transform: scale(0.9);} 
  70% {opacity:1;} 
  100% {opacity:0; left:-150%;} 
}

أخيرًا وليس آخرًا، سنُنسّق قطع لوح البداية ونُعرّف الانتقالات والتحريكات المطبّقة عليها عند تحريك مؤشّر الفأرة فوقها. لاحظ أنّ التنسيقات العامّة تُعرّف حجم القطع.

.tile{ 
  float: left; 
  margin: 0 auto 1%; 
  color: white; 
  font-size: 1.3em; text-align: center; height: 8em; font-weight: 300; 
  overflow: hidden; 
  cursor: pointer;  
  background-color: #fff; 
  color: #333; 
  position:relative; 
  transition: 
  background-color 0.2s ease-out 
} 

.tile-2xbig{ 
  height:16.15em; 
  width:100%; 
} 

.tile-big { 
  width: 100% 
} 

.tile-small { 
  width: 49%; 
  margin-right: 2% 
} 

.tile-small.last { 
  margin-right: 0 
}

سيحتوي زوج من القطع على صورة مع عنوان لها، هاتان القطعتان ستحصلان على الصنف fig-tile وذلك لتمييز نوعهما في شيفرة JavaScript التي سنراها لاحقًا.

سنحصل على الألوان المستخدمة من أجل النص والخلفية لصفحة ما من الألوان المستخدمة في العنوان، لذلك لا تنسى أن تُعرّفهم. بالنسبة للعنوان إمّا أن يكون ثابتًا أو أن يُعطي شعورًا بالانزلاق عندما يتحرك فوقه مؤشّر الفأرة:

.tile-caption{ 
  position:absolute; 
  z-index:1; 
  background-color: #455962; 
  color:#fff; 
  font-size:1em; 
  padding:1em; 
  text-align: left; 
} 

.caption-bottom{ 
  left:0; bottom:0; right:0; 
  height:40%; 
} 

.caption-left{ 
  left:-100%; top:0; bottom:0; 
  width:40%; 
  transition: left .3s linear; 
} 

.tile:hover .caption-left{ 
  left:0; 
}

بالنسبة للقطع النظامية regular tiles التي لا تملك أي نوع من التحريك، فستغيّر لون خلفيّتها ولون النص عند تحريك الفأرة فوقها. لكي نتأكّد من أنّ النص مُوسّط عموديًا vertically centered في كل قطعة، سنضع في كل قطعة عنصر div يحوي بدوره عنصر فقرة (العنصر <p>) يحوي النص. سنستخدم القيمة table-cell للخاصية display وذلك لتوسيط النص عموديًا ضمن الفقرة:

.tile div{ 
  position:absolute; 
  top:0; left:0; right:0; bottom:0; 
  width:100%; height:100%; 
  text-align:center; 
  display:table; 
  padding:0 1em; 
  transition: all .3s ease; 
} 

.tile div p{ 
  display:table-cell; 
  vertical-align:middle; 
}

سنترك الحديث عن أنماط التنسيق العامّة للقطع بهدف الاختصار، ولكن ينبغي علينا التأكّد من أنّنا سنعيّن لون النص والخلفية لجميع القطع، حتى تلك التي ستُغطّى بصورة، لأنّ هذه الألوان التي سنحصل عليها باستخدام JavaScript ستُستخدَم لضبط الألوان في الصفحة المرتبطة بها كما أشرنا قبل قليل. دعونا الآن نتحدّث عن التحريكات والانتقالات التي تحدث على قطعة ما.

ستحتوي القطع المزوّدة بنص منزلق على عنصري div، سيبدو كل عنصر div كوجه أو كتلة منفصلة داخل القطعة. سيكون تموضعًا عنصريًا div مطلقًا positioned absolutely وسيتحرّكان عند تحرّك الفأرة فوقهما وذلك وفقًا لاتجاه الانزلاق المطلوب. فلكي ينزلق نص القطعة إلى الأعلى عندما يحوم مؤشّر الفأرة فوقه سنطبّق الصنف slideTextUp:

.slideTextUp div:nth-child(2){ 
  top:100%; 
} 

.slideTextUp:hover div{ 
  transform: translateY(-100%); 
} 

.tile-1 p{ 
  font-size:1.3em; 
}

وبشكل مماثل ولكي ينزلق نص القطعة إلى اليسار وإلى اليمين سنطبّق الصنفين slideTextLeft و slideTextRight على الترتيب.

.slideTextRight div:first-child{ 
  left:-100%; 
} 

.slideTextRight:hover div{ 
  transform: translateX(100%); 
} 

.slideTextLeft div:nth-child(2){ 
  left:100%; 
} 

.slideTextLeft:hover div{ 
  transform: translateX(-100%); 
}

أمّا بالنسبة لزوج القطع التي ستنقلب، فسيكون لهما تأثير مختلف عندما يحوم مؤشّر الفأرة فوقهما، فهما يدوران ليُظهران الوجه الخلفي. يُعتبر هذا التأثير نوعًا بسيطًا جدًا من تأثير "انقلاب البطاقة" card flip. لن نخوض في تفاصيل هذا التأثير.

لإنجاز هذا التأثير، سنطبّق الصنف rotate3d إلى القطعة التي نريد أن تنقلب. بالنسبة للقطعة التي نريد أن تنقلب عموديًّا، سنطبّق الصنف rotate3dy، أمّا بالنسبة للانقلاب الأفقي سنطبّق الصنف rotate3dx (مع الانتباه إلى وجوب وجود الصنف rotate3d في كلتا الحالتين). انظر إلى تنسيقات CSS التالية:

.rotate3d{ 
  perspective: 800px; 
  overflow: visible; 
}             

.faces{ 
  transform-style: preserve-3d; 
  transition: transform 1s; 
} 

.faces div { 
  display: block; 
  position: absolute; 
  top:0; left:0; right:0; bottom:0; 
  width: 100%; height: 100%; 
  backface-visibility: hidden; 
}

لاحظ بأنّه عند تحريك مؤشّر الفأرة فوقهما بالتتالي، فسيظهر وجه أحدهما في حين ستظهر خلفية الآخر بنفس الوقت.

.rotate3dY .back{ 
  transform: rotateY( 180deg ); 
} 

.rotate3dX .back{ 
  transform: rotateX( 180deg ); 
}

وعندما يحوم مؤشّر الفأرة فوق القطعة فإنّ أي عنصر div يخضع للصنف faces. سيُدوّر ليُظهر وجهه الخلفي:

.rotate3dY:hover .faces:hover{ 
  transform: rotateY( 180deg ); 
} 

.rotate3dX:hover .faces:hover{ 
  transform: rotateX( 180deg ); 
}

لتنسيق القطع في الدوران ثلاثي البعد، ينبغي الانتباه إلى ضبط لوني الخلفية والنص للوجه الأمامي (ذو الصنف front.) بحيث يمكن الحصول على هذين اللونين واستخدمهما لاحقًا في الصفحة المتربطة بالقطعة عند فتحها.

لنعرّف الآن تنسيقات تفاعلية للوح Dashboard. ستكون أعمدة اللوح ذات عرض كامل على الشاشات الصغيرة في البداية (تذكّر أنّنا نراعي متطلبات تصميم الأجهزة المحمولة أولًا mobile-first)، في حين أنّ هذه الأعمدة ستكون بجانب بعضها البعض في الشاشات الكبيرة.

.col1, .col2, .col3 { 
  width: 99%; 
  margin: 1em auto 
} 

@media screen and (min-width: 43.75em) {  
  .col1, .col2, .col3 { 
    float: left; 
    margin-right: 1%; 
    width: 49% 
  } 

  .page-title{ 
    font-size:2.5em; 
  } 

  .page-content{ 
    font-size:1em; 
  } 

  .close-button{ 
    font-size:2em; 
  } 
} 

@media screen and (min-width: 64em) {   
  .col1, .col2, .col3 { 
    float: left; 
    margin-right: .5%; 
    width: 31% 
  } 

  .col3 { 
    margin-right: 0 
  } 

  .col1 { 
    margin-left: 2em 
  } 

  .page-title{ 
    font-size:3.5em; 
  } 
}

جافا سكريبت JavaScript

ستُعالَج جميع أحداث النقر عن طريق JavaScript. سنستخدم مكتبة jQuery لهذه الغاية، وسنضبط معالج حدث event handler النقر لكل قطعة على لوح البداية، فعندما ينقر المستخدم على قطعة ما، يعمل معالج حدث النقر الموافق على الحصول على اسم ونوع الصفحة المرتبطة بهذه القطعة وذلك من السمتين data-page-name و data-page-type على الترتيب، حيث سنستخدم هذه المعلومات لفتح الصفحة المطلوبة.

أمّا عند إغلاق الصفحة عند النقر زر الإغلاق لها، فسيعمل معالج حدث النقر لهذا الزر على تطبيق أسماء الأصناف المناسبة لإغلاق الصفحة.

بالإضافة لذلك، ولكي نُكسِب كل صفحة لون خلفية ولون نص مماثل لتلك التي للقطعة المرتبطة بها، فإنّنا سنطوف بدايةً على جميع القطع، ونحصل على ألوانها، ثمّ نُطبّق هذه الألوان على الصفحات المرتبطة معها. في حال كان لقطعة ما الصنف rotate3d، فإنّنا سنبحث عن لون الخلفية لوجه face القطعة، ومن ثمّ نطبّق هذا اللون على الصفحة المرتبطة معها.

function(){ 
  $('.tile').each(function(){ 
    var $this= $(this), 
        page = $this.data('page-name'), 
        bgcolor = $this.css('background-color'), 
        textColor = $this.css('color');
 
    if($this.hasClass('rotate3d')) { 
      frontface = $this.find('.front'); 
      bgcolor = frontface.css('background-color'); 
      textColor = frontface.css('color'); 
    } 

    if($this.hasClass('fig-tile')) { 
      caption = $this.find('figcaption'); 
      bgcolor = caption.css('background-color'); 
      textColor = caption.css('color'); 
    } 

    $this.on('click',function(){ 
      $('.'+page).css({'background-color': bgcolor, 'color': textColor}) .find('.close-button').css({'background-color': textColor, 'color': bgcolor}); 
    }); 
  }); 

  function showDashBoard(){ 
    for(var i = 1; i <= 3; i++) { 
      $('.col'+i).each(function(){ 
        $(this).addClass('fadeInForward-'+i).removeClass('fadeOutback'); 
      }); 
    } 
  } 

  function fadeDashBoard(){ 
    for(var i = 1; i <= 3; i++) { 
      $('.col'+i).addClass('fadeOutback').removeClass('fadeInForward-'+i); 
    } 
  } 

  $('.tile').each(function(){ 
    var $this= $(this), 
        pageType = $this.data('page-type'), 
        page = $this.data('page-name'); 

    $this.on('click',function(){ 
      if(pageType === "s-page"){ 
        fadeDashBoard(); 
        $('.'+page).addClass('slidePageInFromLeft').removeClass('slidePageBackLeft'); 
      } 
      else{ 
        $('.'+page).addClass('openpage'); 
        fadeDashBoard(); 
      } 
    }); 
  }); 

  $('.r-close-button').click(function(){ 
    $(this).parent().addClass('slidePageLeft') .one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function(e) { 
      $(this).removeClass('slidePageLeft').removeClass('openpage'); 
    }); 
    showDashBoard(); 
  }); 

  $('.s-close-button').click(function(){ 
    $(this).parent().removeClass('slidePageInFromLeft').addClass('slidePageBackLeft'); 
    showDashBoard(); 
  }); 
})();

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

بإمكانك استعراض مثال حيّ لهذا الدّرس من هنا

أما الشيفرة المصدرية فهي مُتوفّرة في هذا المُستودع.

ترجمة -وبتصرّف- للمقال How to Create Windows-8-like animations with CSS3 and JQuery لصاحبته Sara Soueidan.



1 شخص أعجب بهذا


تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن