كيف صنعت لعبة ألغاز CSS نقية ذكاء بيانات PlatoBlockchain. البحث العمودي. عاي.

كيف صنعت لعبة ألغاز CSS نقية

اكتشفت مؤخرًا متعة إنشاء ألعاب CSS فقط. إنه أمر رائع دائمًا كيف أن HTML و CSS قادران على التعامل مع منطق لعبة كاملة عبر الإنترنت ، لذلك كان علي أن أجربها! تعتمد هذه الألعاب عادةً على "Checkbox Hack" حيث نقوم بدمج الحالة المحددة / غير المحددة لإدخال HTML مع :checked فئة زائفة في CSS. يمكننا القيام بالكثير من السحر مع تلك المجموعة الواحدة!

في الواقع ، لقد تحدت نفسي لبناء لعبة كاملة بدون Checkbox. لم أكن متأكدًا مما إذا كان ذلك ممكنًا ، لكنه بالتأكيد كذلك ، وسأوضح لك كيف.

بالإضافة إلى لعبة الألغاز التي سندرسها في هذا المقال ، لقد صنعت مجموعة من ألعاب CSS الخالصة، معظمهم بدون Checkbox Hack. (وهي متوفرة أيضًا على CodePen.)

تريد أن تلعب قبل أن نبدأ؟

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

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

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

وظيفة السحب والإفلات

في حين أن بنية اللغز واضحة إلى حد ما مع CSS Grid ، فإن القدرة على سحب وإسقاط قطع اللغز أصعب قليلاً. اضطررت إلى الاعتماد على مجموعة من التحولات وتأثيرات التمرير ومحددات الأشقاء لإنجاز ذلك.

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

img {
  transform: translate(200%);
  transition: 999s 999s; /* very slow move on mouseout */
}
.box:hover img {
  transform: translate(0);
  transition: 0s; /* instant move on hover */
}

تحديد فقط ملف transition-delay يكفي ، ولكن استخدام قيم كبيرة في كل من التأخير والمدة يقلل من فرصة رؤية اللاعب للصورة تتراجع. إذا انتظرت 999s + 999s - وهي ما يقرب من 30 دقيقة - ثم سترى الصورة تتحرك. لكنك لن تفعل ، أليس كذلك؟ أعني ، لن يستغرق أي شخص هذا الوقت الطويل بين الأدوار ما لم يبتعدوا عن اللعبة. لذا ، أنا أعتبر هذه حيلة جيدة للتبديل بين حالتين.

هل لاحظت أن تحريك الصورة يؤدي أيضًا إلى حدوث التغييرات؟ ذلك لأن الصورة جزء من عنصر الصندوق ، وهذا ليس جيدًا بالنسبة لنا. يمكننا إصلاح هذا عن طريق إضافة pointer-events: none إلى الصورة ولكننا لن نتمكن من سحبها لاحقًا.

هذا يعني أنه يتعين علينا إدخال عنصر آخر داخل .box:

هذا إضافي div (نحن نستخدم فئة من .a) سيأخذ نفس مساحة الصورة (بفضل CSS Grid و grid-area: 1 / 1) وسيكون العنصر الذي يقوم بتشغيل تأثير التمرير. وهنا يأتي دور محدد الأشقاء:

.a {
  grid-area: 1 / 1;
}
img {
  grid-area: 1 / 1;
  transform: translate(200%);
  transition: 999s 999s;
}
.a:hover + img {
  transform: translate(0);
  transition: 0s;
}

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

لنقم بسحب وإسقاط صورتنا داخل المربع ونرى النتيجة:

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

حسنًا ، المحاكاة ليست مثالية لأنه يمكننا أيضًا تحريك المربع والحصول على نفس التأثير.

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

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

لنفصل الكود:

.a {
  width: 0%;
  transition: 0s .2s; /* add a small delay to make sure we catch the hover effect */
}
.box:active .a { /* on :active increase the width */
  width: 100%;
  transition: 0s; /* instant change */
}
img {
  transform: translate(200%);
  transition: 999s 999s;
}
.a:hover + img {
  transform: translate(0);
  transition: 0s;
}

يؤدي النقر فوق الصورة إلى تشغيل ملف :active الفئة الزائفة التي تجعل .a العرض الكامل للعنصر (يساوي في البداية 0). ستبقى الحالة النشطة نشط حتى نصدر الصورة. إذا أطلقنا الصورة داخل المربع ، فسيتم إنشاء ملف .a يعود العنصر إلى width: 0، لكننا سنقوم بتشغيل تأثير التمرير قبل حدوثه وستسقط الصورة داخل المربع! إذا تركته خارج الصندوق ، فلن يحدث شيء.

هناك القليل من المراوغة: النقر فوق المربع الفارغ يؤدي أيضًا إلى تحريك الصورة وكسر ميزتنا. حالياً، :active مرتبط ب .box عنصر ، وبالتالي فإن النقر فوقه أو أي من أبنائه سيتم تنشيطه ؛ ومن خلال القيام بذلك ، ينتهي بنا الأمر إلى عرض .a عنصر وتحريك تأثير التمرير.

يمكننا إصلاح ذلك من خلال اللعب pointer-events. يسمح لنا بتعطيل أي تفاعل مع .box مع الحفاظ على التفاعلات مع العناصر التابعة.

.box {
  pointer-events: none;
}
.box * {
  pointer-events: initial;
}

الآن ميزة السحب والإفلات لدينا مثالية. ما لم تتمكن من العثور على كيفية اختراقها ، فإن الطريقة الوحيدة لتحريك الصورة هي سحبها وإفلاتها داخل المربع.

بناء شبكة اللغز

سيبدو تجميع الأحجية معًا أمرًا سهلاً مقارنةً بما فعلناه للتو لميزة السحب والإفلات. سوف نعتمد على شبكة CSS وحيل الخلفية لإنشاء اللغز.

ها هي شبكتنا ، مكتوبة بلغة Pug ​​للراحة:

- let n = 4; /* number of columns/rows */
- let image = "https://picsum.photos/id/1015/800/800";

g(style=`--i:url(${image})`)
  - for(let i = 0; i < n*n; i++)
    z
      a
      b(draggable="true") 

قد تبدو الشفرة غريبة ولكنها تترجم إلى HTML عادي:

<g style="--i: url(https://picsum.photos/id/1015/800/800)">
 <z>
   <a></a>
   <b draggable="true"></b>
 </z>
 <z>
   <a></a>
   <b draggable="true"></b>
 </z>
 <z>
   <a></a>
   <b draggable="true"></b>
 </z>
  <!-- etc. -->
</g>

أراهن أنك تتساءل ما الأمر بهذه العلامات. ليس لأي من هذه العناصر أي معنى خاص - أجد أن الكود أسهل بكثير في الكتابة باستخدامه <z> من حفنة من <div class="z"> أو أيا كان.

هذه هي الطريقة التي حددتها لهم:

  • <g> هي حاوية شبكتنا التي تحتوي على N*N <z> العناصر.
  • <z> تمثل عناصر شبكتنا. إنها تلعب دور .box العنصر الذي رأيناه في القسم السابق.
  • <a> لتشغيل تأثير التمرير.
  • <b> يمثل جزءًا من صورتنا. نحن نطبق draggable السمة عليه لأنه لا يمكن سحبه افتراضيًا.

حسنًا ، دعنا نسجل حاوية الشبكة الخاصة بنا على <g>. هذا في Sass بدلاً من CSS:

$n : 4; /* number of columns/rows */

g {
  --s: 300px; /* size of the puzzle */

  display: grid;
  max-width: var(--s);
  border: 1px solid;
  margin: auto;
  grid-template-columns: repeat($n, 1fr);
}

سنقوم في الواقع بجعل شبكتنا الأطفال - ال <z> العناصر - الشبكات أيضًا وتحتوي على كليهما <a> و <b> داخل نفس منطقة الشبكة:

z {
  aspect-ratio: 1;
  display: grid;
  outline: 1px dashed;
}
a {
  grid-area: 1/1;
}
b {
  grid-area: 1/1;
}

كما ترى ، لا شيء رائع - لقد أنشأنا شبكة بحجم معين. ما تبقى من CSS الذي نحتاجه هو لميزة السحب والإفلات ، والتي تتطلب منا وضع القطع بشكل عشوائي حول اللوحة. سأنتقل إلى Sass من أجل هذا ، مرة أخرى من أجل الراحة لكوني قادرًا على إجراء حلقة وتصميم جميع قطع الألغاز باستخدام وظيفة:

b {
  background: var(--i) 0/var(--s) var(--s);
}

@for $i from 1 to ($n * $n + 1) {
  $r: (random(180));
  $x: (($i - 1)%$n);
  $y: floor(($i - 0.001) / $n);
  z:nth-of-type(#{$i}) b{
    background-position: ($x / ($n - 1)) * 100% ($y / ($n - 1)) * 100%;
    transform: 
      translate((($n - 1) / 2 - $x) * 100%, (($n - 1)/2 - $y) * 100%) 
      rotate($r * 1deg) 
      translate((random(100)*1% + ($n - 1) * 100%)) 
      rotate((random(20) - 10 - $r) * 1deg)
   }
}

ربما لاحظت أنني أستخدم Sass random() وظيفة. هذه هي الطريقة التي نحصل بها على المواضع العشوائية لقطع اللغز. تذكر أننا سنفعل تعطيل هذا الموقف عند التمرير فوق <a> عنصر بعد سحب وإسقاط المقابل له <b> عنصر داخل خلية الشبكة.

z a:hover ~ b {
  transform: translate(0);
  transition: 0s;
}

في نفس الحلقة ، أقوم أيضًا بتحديد تكوين الخلفية لكل قطعة من اللغز. سيشارك كل منهم منطقيًا نفس الصورة كخلفية ، ويجب أن يكون حجمها مساويًا لحجم الشبكة بالكامل (معرّف بـ --s عامل). باستخدام نفس الشيء background-image وبعض الرياضيات ، نقوم بتحديث background-position لإظهار جزء فقط من الصورة.

هذا هو! لقد تم تقنيًا لعبة الألغاز المقتصرة على CSS فقط!

لكن يمكننا دائمًا القيام بعمل أفضل ، أليس كذلك؟ لقد عرضت لك كيف تصنع شبكة من أشكال قطع الألغاز في مقال آخر. لنأخذ نفس الفكرة ونطبقها هنا ، أليس كذلك؟

أشكال قطعة اللغز

ها هي لعبة اللغز الجديدة. نفس الوظيفة ولكن بأشكال أكثر واقعية!

هذا رسم توضيحي للأشكال الموجودة على الشبكة:

كيف صنعت لعبة ألغاز CSS نقية

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

شبكة قطع الألغاز التي صنعتها في المقالة الأخرى التي أشرت إليها هي أكثر وضوحًا:

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

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

z  /* 0 */

z:first-child  /* 1 */

z:nth-child(-n + 4):not(:first-child) /* 2 */

z:nth-child(5) /* 3 */

z:nth-child(5n + 1):not(:first-child):not(:nth-last-child(5)) /* 4 */

z:nth-last-child(5)  /* 5 */

z:nth-child(5n):not(:nth-child(5)):not(:last-child) /* 6 */

z:last-child /* 7 */

z:nth-last-child(-n + 4):not(:last-child) /* 8 */

فيما يلي شكل يوضح كيفية تعيين ذلك لشبكتنا:

كيف صنعت لعبة ألغاز CSS نقية ذكاء بيانات PlatoBlockchain. البحث العمودي. عاي.
كيف صنعت لعبة ألغاز CSS نقية

الآن دعونا نتعامل مع الأشكال. دعنا نركز على تعلم واحد أو اثنين فقط من الأشكال لأنهم جميعًا يستخدمون نفس الأسلوب - وبهذه الطريقة ، لديك بعض الواجبات المنزلية لمواصلة التعلم!

لقطع اللغز في وسط الشبكة ، 0:

mask: 
  radial-gradient(var(--r) at calc(50% - var(--r) / 2) 0, #0000 98%, #000) var(--r)  
    0 / 100% var(--r) no-repeat,
  radial-gradient(var(--r) at calc(100% - var(--r)) calc(50% - var(--r) / 2), #0000 98%, #000) 
    var(--r) 50% / 100% calc(100% - 2 * var(--r)) no-repeat,
  radial-gradient(var(--r) at var(--r) calc(50% - var(--r) / 2), #000 98%, #0000),
  radial-gradient(var(--r) at calc(50% + var(--r) / 2) calc(100% - var(--r)), #000 98%, #0000);

قد يبدو الرمز معقدًا ، لكن دعنا نركز على تدرج لوني واحد في كل مرة لمعرفة ما يحدث:

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

كيف صنعت لعبة ألغاز CSS نقية ذكاء بيانات PlatoBlockchain. البحث العمودي. عاي.
كيف صنعت لعبة ألغاز CSS نقية

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

على سبيل المثال ، قطع اللغز على طول الحافة العلوية للغز (ملحوظ 2 في الرسم التوضيحي) يستخدم ثلاثة تدرجات بدلاً من أربعة:

mask: 
  radial-gradient(var(--r) at calc(100% - var(--r)) calc(50% + var(--r) / 2), #0000 98%, #000) var(--r) calc(-1 * var(--r)) no-repeat,
  radial-gradient(var(--r) at var(--r) calc(50% - var(--r) / 2), #000 98%, #0000),
  radial-gradient(var(--r) at calc(50% + var(--r) / 2) calc(100% - var(--r)), #000 98%, #0000);

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

بالإضافة إلى إنشاء الأشكال ، ستجد أيضًا أنني أقوم بزيادة عرض و / أو ارتفاع العناصر كما هو موضح أدناه:

height: calc(100% + var(--r));
width: calc(100% + var(--r));

تحتاج قطع اللغز إلى تجاوز خلية الشبكة الخاصة بهم للاتصال.

كيف صنعت لعبة ألغاز CSS نقية ذكاء بيانات PlatoBlockchain. البحث العمودي. عاي.
كيف صنعت لعبة ألغاز CSS نقية

العرض النهائي

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

التحسينات الممكنة

تنتهي المقالة هنا ولكن يمكننا الاستمرار في تحسين اللغز الخاص بنا بمزيد من الميزات! ماذا عن الموقت؟ أو ربما نوع من التهاني عندما ينتهي اللاعب من اللغز؟

قد أعتبر كل هذه الميزات في إصدار مستقبلي ، لذلك راقب مستودع جيثب الخاص بي.

اختتام

و CSS ليست لغة برمجةيقولون. ها!

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

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

سواء كانت CSS لغة برمجة أم لا ، لا يغير حقيقة أننا نتعلم دائمًا من خلال بناء وإنشاء أشياء مبتكرة.

الطابع الزمني:

اكثر من الخدع المغلق