چگونه یک بازی پازل CSS خالص ساختم هوش داده پلاتو بلاک چین. جستجوی عمودی Ai.

چگونه یک بازی پازل CSS خالص ساختم

من اخیراً لذت ایجاد بازی های فقط CSS را کشف کردم. همیشه جالب است که چگونه HTML و CSS قادر به مدیریت منطق کل یک بازی آنلاین هستند، بنابراین من مجبور شدم آن را امتحان کنم! چنین بازی‌هایی معمولاً بر روی ol'Checkbox Hack تکیه می‌کنند که در آن ما وضعیت علامت‌گذاری شده/تیک‌نخورده یک ورودی HTML را با :checked شبه کلاس در CSS. ما می توانیم با این یک ترکیب جادوهای زیادی انجام دهیم!

در واقع، من خودم را به چالش کشیدم تا یک بازی کامل را بدون چک باکس بسازم. مطمئن نبودم که ممکن است یا نه، اما قطعا اینطور است، و من به شما نشان خواهم داد که چگونه.

علاوه بر بازی پازلی که در این مقاله به بررسی آن خواهیم پرداخت، من ساخته ام مجموعه ای از بازی های CSS خالص، اکثر آنها بدون چک باکس هک هستند. (آنها نیز موجود هستند در 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 خالص ساختم

اگر دقت کنید متوجه خواهید شد که ما XNUMX شکل مختلف پازل داریم: چهار گوشهاز چهار لبهو یکی برای هر چیز دیگری.

شبکه قطعات پازلی که در مقاله دیگری که به آن اشاره کردم درست کردم کمی ساده تر است:

ما می‌توانیم از تکنیک مشابهی استفاده کنیم که ماسک‌های 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 خالص ساختم هوش داده پلاتو بلاک چین. جستجوی عمودی Ai.
چگونه یک بازی پازل 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 خالص ساختم هوش داده پلاتو بلاک چین. جستجوی عمودی Ai.
چگونه یک بازی پازل 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 خالص ساختم هوش داده پلاتو بلاک چین. جستجوی عمودی Ai.
چگونه یک بازی پازل CSS خالص ساختم

نسخه ی نمایشی نهایی

در اینجا نسخه ی نمایشی کامل دوباره است. اگر آن را با نسخه اول مقایسه کنید، همان ساختار کد برای ایجاد شبکه و ویژگی کشیدن و رها کردن، به علاوه کد ایجاد اشکال را خواهید دید.

پیشرفت های احتمالی

مقاله در اینجا به پایان می رسد، اما ما می توانیم پازل خود را با ویژگی های بیشتر تقویت کنیم! یک تایمر چطور؟ یا شاید زمانی که بازیکن پازل را تمام می کند به نوعی تبریک می گویم؟

من ممکن است تمام این ویژگی ها را در نسخه بعدی در نظر بگیرم، بنابراین مراقب مخزن GitHub من باشید.

پسگفتار

و CSS یک زبان برنامه نویسی نیست، میگویند. ها!

من سعی نمی کنم با آن جرقه یک #درام داغ بزنم. من این را می گویم زیرا ما چیزهای منطقی واقعاً پیچیده ای انجام دادیم و بسیاری از ویژگی ها و تکنیک های CSS را در طول مسیر پوشش دادیم. ما با CSS Grid، transitions، masking، gradients، selector ها و ویژگی های پس زمینه بازی کردیم. ناگفته نماند چند ترفند Sass که برای تنظیم آسان کدمان استفاده کردیم.

هدف ساخت بازی نبود، بلکه کاوش در CSS و کشف خواص و ترفندهای جدید بود که می توانید در پروژه های دیگر از آنها استفاده کنید. ایجاد یک بازی آنلاین در CSS چالشی است که شما را وادار می کند تا ویژگی های CSS را با جزئیات زیاد بررسی کنید و نحوه استفاده از آنها را بیاموزید. بعلاوه، زمانی که همه چیز گفته شده و انجام می شود، بسیار سرگرم کننده است که ما چیزی برای بازی با آن پیدا می کنیم.

اینکه CSS یک زبان برنامه نویسی است یا نه، این واقعیت را تغییر نمی دهد که ما همیشه با ساختن و ایجاد چیزهای خلاقانه یاد می گیریم.

تمبر زمان:

بیشتر از ترفندهای CSS