Meledakkan balon

Saya selalu terpesona dengan banyaknya pencapaian luar biasa yang dapat kami capai hanya dengan menggunakan HTML dan CSS. Fitur interaktif baru di Popover API adalah contoh lain seberapa banyak yang dapat kami capai hanya dengan menggunakan dua bahasa ini.

Anda mungkin pernah melihat tutorial lain yang menunjukkan apa yang dapat dilakukan Popover API, tetapi artikel ini membahas tentang cara menundukkannya tanpa ampun. Kami akan menambahkan lebih banyak <>Pop musik Untuk campurannya, seperti halnya balon… beberapa “letupan” literal jika Anda mau.

-Advertisement-.


Saya telah membuat game – tentu saja hanya menggunakan HTML dan CSS – berdasarkan API Popover. Misi Anda adalah meletuskan balon sebanyak mungkin dalam waktu kurang dari satu menit. Tapi hati-hati! Beberapa balon (seperti yang dikatakan Gollum) “rumit” dan menyebabkan lebih banyak balon meledak.

Saya menyebutnya pintar Meledakkan balon Kami akan melakukannya bersama-sama, langkah demi langkah. Dan ketika kita selesai melakukannya, akan terlihat seperti ini (yah, <>tepat Seperti dia:

Opsi penyertaan CodePen alternatif

menghadapi popover menjelaskan

Elemen apa pun bisa menjadi popup selama kita mendesainnya popover Menjelaskan:

<div popover>...</div>

Kami bahkan tidak perlu menyediakannya popover Dengan nilai. Secara default, popoverNilai awalnya adalah auto Mereka menggunakan apa yang disebut dalam spesifikasi “penutupan cepat”. Ini berarti jendela pop-up dapat ditutup dengan mengklik di mana saja di luarnya. Saat jendela pop-up terbuka, kecuali jika disarangkan, jendela pop-up lain di halaman tersebut akan ditutup. Popup otomatis saling berhubungan seperti ini.

Pilihan lainnya adalah penyetelan popover ke manual nilai:

<div popover=“manual”>...</div>

…yang berarti item tersebut dibuka dan ditutup secara manual — kita sebenarnya harus mengklik tombol tertentu untuk membuka dan menutupnya. Dengan kata lain, manual Ini menciptakan popup yang mengganggu yang hanya menutup ketika tombol yang benar ditekan dan benar-benar independen dari popup lain di halaman.

Opsi penyertaan CodePen alternatif

Menggunakan <details> Item sebagai permulaan

Salah satu tantangan dalam membuat game menggunakan Popover API adalah Anda tidak dapat memuat halaman dengan popover yang sudah terbuka… dan tidak ada solusi lain dengan JavaScript jika tujuan kami adalah membuat game hanya menggunakan HTML dan CSS .

Memasuki <details> Barang. Berbeda dengan jendela pop-up, <details> Item dapat dibuka secara default:

<details open>
  <!-- rest of the game -->
</details>

Jika kita mengikuti jalur ini, kita akan dapat memunculkan sekumpulan tombol (balon) dan “membukanya” hingga balon terakhir dengan menutupnya. <details>Dengan kata lain, kita bisa menempatkan balon awal di ruang terbuka. <details> Item ditampilkan di halaman saat dimuat.

Ini adalah struktur dasar yang saya bicarakan:

<details open>
  <summary>🎈</summary>
  <button>🎈</button>
  <button>🎈</button>
  <button>🎈</button>
</details>

Dengan cara ini, kita bisa mengklik balon yang terletak di dalamnya <summary> Untuk menutup <details> dan “meletuskan” semua balon kancing, meninggalkan kita dengan satu balon ( <summary> Terakhir (kami akan menyelesaikan cara menghapusnya nanti).

Mungkin Anda berpikir begitu <dialog> Ini akan menjadi arah yang lebih indikatif untuk permainan kami, dan Anda benar. Tapi ada dua kelemahannya <dialog> Ini tidak memungkinkan kami menggunakannya di sini:

  1. Satu-satunya cara untuk menutup <dialog> Yang terbuka saat halaman dimuat adalah JavaScript. Sejauh yang saya tahu, tidak ada penutupan <button> Kita bisa terjerumus ke dalam permainan yang akan ditutup <dialog> Ini tidak terkunci setelah diunduh.
  2. <dialog>Ini bersifat modular dan mencegah mengklik hal lain saat terbuka. Kita harus membiarkan pemain meniup balonnya <dialog> Untuk mengatasi sementara.

Oleh karena itu kami akan menggunakan <details open> Item sebagai wadah tingkat atas game dan gunakan bentuk normal <div> Adapun popup itu sendiri <div popover>.

Yang perlu kita lakukan sekarang adalah memastikan semua popup dan tombol ini terhubung bersama sehingga mengklik tombol tersebut akan membuka popup. Anda mungkin telah mempelajari hal ini dari tutorial lain, namun kita perlu memberi tahu elemen popup bahwa ada tombol yang perlu ditanggapi, lalu memberi tahu tombol tersebut bahwa ada popup yang perlu dibuka. Untuk melakukan ini, kami memberikan elemen popup sebuah pengidentifikasi unik (sebagaimana seharusnya semua pengidentifikasi) dan kemudian mengarahkannya ke <button> dengan popovertarget Menjelaskan:

<!-- Level 0 is open by default -->
<details open>
  <summary>🎈</summary>
  <button popovertarget="lvl1">🎈</button>
</details>

<!-- Level 1 -->
<div id="lvl1" popover="manual">
  <h2>Level 1 Popup</h2>
</div>

Inilah gagasan ketika semuanya terikat menjadi satu:

Opsi penyertaan CodePen alternatif

Buka dan tutup jendela pop-up

Ada sedikit pekerjaan yang harus dilakukan pada versi beta terbaru. Salah satu kelemahan dari permainan ini sejauh ini adalah kliknya <button> Dari a popup Membuka lebih banyak pop-up; Klik pada jendela yang sama <button> lagi dan mereka menghilang. Ini membuat permainan menjadi sangat mudah.

Kita dapat memisahkan perilaku membuka dan menutup dengan melakukan penyesuaian popovertargetaction atribut (tidak, penulis spesifikasi HTML tidak peduli dengan singkatnya) aktif <button>Jika kita menetapkan nilai atribut ke salah satu dari show atau hideitu <button> Tindakan ini hanya akan dilakukan untuk popup tertentu.

<!-- Level 0 is open by default -->
<details open>
  <summary>🎈</summary>
  <!-- Show Level 1 Popup -->
  <button popovertarget="lvl1" popovertargetaction="show">🎈</button>
  <!-- Hide Level 1 Popup -->
  <button popovertarget="lvl1" popovertargetaction="hide">🎈</button>
</details>

<!-- Level 1 -->
<div id="lvl1" popover="manual">
  <h2>Level 1 Popup</h2>
  <!-- Open/Close Level 2 Poppup -->
  <button popovertarget="lvl2">🎈</button>
</div>

<!-- etc. -->

Perhatikan saya menambahkan yang baru <button> di dalam <div> Yang diatur untuk menargetkan yang lain <div> Membuka atau menutup dengan sengaja <>TIDAK menyesuaikan popovertargetaction Lihatlah betapa sulitnya “mengeluarkan” barang (dengan cara yang baik):

Opsi penyertaan CodePen alternatif

Penataan balon

Sekarang kita perlu mendesain <summary> Dan <button> Item-itemnya sangat mirip sehingga pemain tidak dapat membedakannya. Perhatikan kataku <summary> Dan <>TIDAK <details>hal ini dikarenakan <summary> Ini adalah elemen sebenarnya yang kita klik untuk membuka dan menutup <details> wadah.

Sebagian besar pekerjaan ini merupakan pekerjaan CSS standar: menyesuaikan latar belakang, padding, margin, ukuran, batas, dll. Namun ada beberapa hal penting, dan belum tentu jelas, yang perlu disertakan.

  • Pertama, ada kontrol list-style-type properti untuk none pada <summary> Elemen untuk menghilangkan tanda segitiga yang menunjukkan apakah <details> Terbuka atau tertutup. Bendera ini sangat berguna dan bagus untuk memilikinya di sana secara default, tetapi untuk permainan seperti ini, akan lebih baik untuk menghapus petunjuk ini untuk tantangan yang lebih baik.
  • Safari tidak menyukai pendekatan yang sama. untuk menghapus <details> Tag di sini, kita perlu menyetel elemen dummy khusus dengan awalan vendor, summary::-webkit-details-marker ke display: none.
  • Alangkah baiknya jika penunjuk mouse menunjukkan bahwa balon tersebut dapat diklik, sehingga kita dapat menyesuaikannya cursor: pointer pada <summary> Barang juga.
  • Detail terakhir adalah penyetelan user-select properti untuk none pada <summary>Untuk mencegah pemilihan balon — yang hanya berupa teks ekspresif — menjadikannya lebih seperti objek di halaman.
  • Dan ya, ini tahun 2024 dan kami masih membutuhkan awalan itu -webkit-user-select Fitur untuk mendukung Safari. Terima kasih apel.

Masukkan semuanya ke dalam kode .balloon Baris yang akan kita gunakan <button> Dan <summary> Elemen:

.balloon {
  background-color: transparent;
  border: none;
  cursor: pointer;
  display: block;
  font-size: 4em;
  height: 1em;
  list-style-type: none;
  margin: 0;
  padding: 0;
  text-align: center;
  -webkit-user-select: none; /* Safari fallback */
  user-select: none;
  width: 1em;
}
Opsi penyertaan CodePen alternatif

Satu masalah dengan balon adalah beberapa di antaranya… <>dengan sengaja Jangan melakukan apa pun. Ini karena pop-up yang Anda tutup tidak terbuka. Pemain mungkin berpikir mereka tidak mengklik/mengetuk balon tertentu atau permainannya rusak, jadi mari tambahkan sedikit gradien saat balon berada di tempatnya. :active Status klik:

.balloon:active {
  scale: 0.7;
  transition: 0.5s;
}

Bonusnya: Karena cursor Itu adalah tangan yang menunjuk dengan jari telunjuknya, mengetuk balon, dan seolah-olah tangan tersebut mendorong balon dengan jarinya. 👉🎈💥

Cara kami mendistribusikan balon di sekitar layar adalah hal penting lainnya yang perlu dipertimbangkan. Kami tidak dapat mengacaknya tanpa JavaScript, jadi itu tidak mungkin. Saya telah mencoba banyak hal, seperti membuat angka “acak” sendiri yang didefinisikan sebagai properti khusus yang dapat digunakan sebagai pengganda, tetapi saya belum bisa mendapatkan hasil keseluruhan agar terlihat sepenuhnya “acak” tanpa tumpang tindih balon atau membuat semacam pola visual.

Saya akhirnya menemukan cara yang menggunakan kelas untuk menempatkan balon ke dalam baris dan kolom yang berbeda — tidak seperti Grid CSS atau Multikolom, tetapi baris dan kolom tiruan berdasarkan masukan fisik. Ini akan terlihat lebih mirip Grid dan menjadi kurang “acak” daripada yang saya inginkan, tapi selama tidak ada balon yang memiliki dua kategori yang sama, mereka tidak akan saling mengganggu.

Saya memutuskan untuk menggunakan kisi 8 x 8 tetapi membiarkan “baris” dan “kolom” pertama kosong sehingga balon terlihat jelas di tepi kiri dan atas browser.

/* Rows */
.r1 { --row: 1; }
.r2 { --row: 2; }
/* all the way up to .r7 */

/* Columns */
.c1 { --col: 1; }
.c2 { --col: 2; }
/* all the way up to .c7 */

.balloon {
  /* This is how they're placed using the rows and columns */
  top: calc(12.5vh * (var(--row) + 1) - 12.5vh);
  left: calc(12.5vw * (var(--col) + 1) - 12.5vw);
}
Opsi penyertaan CodePen alternatif

Memberi selamat kepada pemain (atau tidak memberi selamat kepada pemain)

Kami sudah memiliki sebagian besar potongan permainan, tapi akan menyenangkan jika bisa meraih kemenangan menari Muncul pesan pop-up untuk memberi selamat kepada pemain ketika mereka berhasil meletuskan semua balon dalam waktu yang ditentukan.

Semuanya kembali ke <details open> Barang. Setelah item ini menjadi… <>TIDAK openPermainan harus diakhiri dengan langkah terakhir yaitu meledakkan balon terakhir. Jadi, jika kita memberi elemen ini sebuah pengenal, misalnya, #rootKita dapat membuat kondisi untuk menyembunyikannya menggunakan display: none Kapan itu terjadi :not() di dalam open negara:

#root:not([open]) {
  display: none;
}

Itu adalah tempat terbaik yang kami miliki :has() Pembatas semu karena kita dapat menggunakannya untuk menentukan #root Elemen utama dari elemen tersebut sehingga kapan #root Jika elemennya tertutup, kita dapat mendefinisikan elemen anak dari elemen induknya — elemen baru dengan ID #congrats – Untuk menampilkan jendela pop-up palsu yang menampilkan pesan ucapan selamat kepada pemain. (Ya, saya menyadari ironi ini.)

#game:has(#root:not([open])) #congrats {
  display: flex;
}

Jika kita memainkan permainan ini pada titik ini, kita dapat menerima pesan kemenangan tanpa harus meledakkan semua balonnya. Sekali lagi, popup tidak akan ditutup secara manual kecuali tombol yang benar diklik — meskipun kita menutup tombol aslinya <details> komponen.

Apakah ada cara dalam CSS untuk mengetahui apakah popup masih terbuka? Ya, masuk :popover-open Kelas semu.

itu :popover-open Kelas semu memilih jendela popup yang terbuka. Kita bisa menggunakannya dengan :has() Sebelumnya untuk mencegah pesan muncul jika jendela pop-up masih terbuka di halaman. Beginilah cara menghubungkan hal-hal ini bersama-sama and klausa bersyarat.

/* If #game does *not* have an open #root 
 * but has an element with an open popover 
 * (i.e. the game isn't over),
 * then select the #congrats element...
 */
#game:has(#root:not([open])):has(:popover-open) #congrats {
  /* ...and hide it */
  display: none;
}

Sekarang, seorang pemain hanya diberi ucapan selamat ketika dia, lho, melakukan sesuatu. <>menang.

Sebaliknya, jika pemain tidak dapat meletuskan semua balon sebelum batas waktu habis, maka kita harus memberitahukan pemain tersebut bahwa permainan telah selesai. Karena tidak ada waktu tertentu, if() Jika tidak ada pernyataan kondisional di CSS (setidaknya belum) kami akan memutar animasi 1 menit hingga pesan ini menghilang untuk mengakhiri permainan.

#fail {
  animation: fadein 0.5s forwards 60s;
  display: flex;
  opacity: 0;
  z-index: -1;
}

@keyframes fadein {
  0% {
    opacity: 0;
    z-index: -1;
  }
  100% {
    opacity: 1;
    z-index: 10;
  }
}

Namun kita tidak ingin pesan kegagalan muncul jika layar kemenangan ditampilkan, sehingga kita dapat menulis pemilih blok #fail Pesan dari tampilan pada saat yang sama #congrats pesan.

#game:has(#root:not([open])) #fail {
  display: none;
}

Kami membutuhkan pengatur waktu permainan

Pemain harus mengetahui berapa lama waktu yang dimilikinya untuk meletuskan semua balon. Kita dapat membuat pengatur waktu yang cukup “sederhana” menggunakan elemen yang memenuhi seluruh lebar layar (100vw), skalakan ke arah horizontal, lalu cocokkan dengan animasi di atas yang memungkinkan hal ini #fail Pesan memudar.

#timer {
  width: 100vw;
  height: 1em;
}

#bar {
  animation: 60s timebar forwards;
  background-color: #e60b0b;
  width: 100vw;
  height: 1em;
  transform-origin: right;
}

@keyframes timebar {
  0% {
    scale: 1 1;
  }
  100% {
    scale: 0 1;
  }
}

Memiliki satu titik kegagalan saja mungkin membuat permainan sedikit lebih mudah, jadi mari kita coba menambahkan titik kegagalan kedua <details> Elemen yang berisi pengidentifikasi “root” kedua, #root2Sekali lagi, kita bisa menggunakannya :has Untuk memastikan tidak ada keduanya #root TIDAK #root2 Unsur-unsurnya adalah open Sebelum tawaran #congrats pesan.

#game:has(#root:not([open])):has(#root2:not([open])) #congrats {
  display: flex;
}

kesimpulan

Satu-satunya hal yang perlu dilakukan adalah memainkan permainannya!

Opsi penyertaan CodePen alternatif

Menyenangkan, bukan? Saya yakin kami dapat membangun sesuatu yang lebih kuat tanpa batasan yang dibuat sendiri dengan pendekatan bebas JavaScript, dan bukan berarti kami memberikan izin aksesibilitas dengan itikad baik, namun mendorong API hingga batasnya adalah hal yang menyenangkan. <>Dan Mendidik, bukan?


Saya tertarik: Ide aneh apa lagi yang dapat Anda pikirkan untuk menggunakan popup? Mungkin Anda memikirkan permainan lain, beberapa efek UI yang bagus, atau cara cerdas untuk menggabungkan popup dengan fitur CSS baru lainnya, seperti penempatan jangkar. Apa pun pendapat Anda, silakan bagikan!


Pop(over) the Balloons awalnya diterbitkan di CSS-Tricks, bagian dari keluarga DigitalOcean. Anda harus mendapatkan buletin.

Sumber

-Advertisement-.

IDJ