Kuinka tein puhtaan CSS-pulmapelin PlatoBlockchain Data Intelligence. Pystysuuntainen haku. Ai.

Kuinka tein puhtaan CSS-pulmapelin

Löysin äskettäin vain CSS-pelien luomisen ilon. On aina kiehtovaa, kuinka HTML ja CSS pystyvät käsittelemään koko verkkopelin logiikkaa, joten minun piti kokeilla sitä! Tällaiset pelit käyttävät yleensä vanhaa Checkbox Hackia, jossa yhdistämme HTML-syötteen valitun/valitsemattoman tilan :checked pseudoluokka CSS:ssä. Voimme tehdä paljon taikuutta tällä yhdellä yhdistelmällä!

Itse asiassa haastoin itseni rakentamaan koko pelin ilman valintaruutua. En ollut varma, olisiko se mahdollista, mutta varmasti on, ja aion näyttää sinulle kuinka.

Tässä artikkelissa tutkittavan pulmapelin lisäksi olen tehnyt kokoelma puhtaita CSS-pelejä, suurin osa niistä ilman valintaruutuhakkerointia. (Ne ovat myös saatavilla CodePenissä.)

Haluatko pelata ennen kuin aloitamme?

Itse pelaan mieluummin koko näytön tilassa, mutta voit pelata sen alla tai avaa se tästä.

Siistiä eikö? Tiedän, että se ei ole paras pulmapeli, jonka olet koskaan nähnyt™, mutta se ei myöskään ole ollenkaan huono asia, joka käyttää vain CSS:ää ja muutaman rivin HTML:ää. Voit helposti säätää ruudukon kokoa, muuttaa solujen määrää hallitaksesi vaikeustasoa ja käyttää mitä tahansa kuvaa!

Teemme demon uudelleen yhdessä ja laitamme siihen lopussa hieman ylimääräistä kimaltelua.

Vedä ja pudota -toiminto

Vaikka palapelin rakenne on melko suoraviivainen CSS Gridillä, kyky vetää ja pudottaa palapelin osia on hieman hankalampi. Minun täytyi luottaa siirtymien, hover-tehosteiden ja sisarusvalitsimien yhdistelmään, jotta se onnistuisi.

Jos siirrät hiiren demon tyhjän laatikon päälle, kuva liikkuu sen sisällä ja pysyy siellä, vaikka siirrät kohdistimen pois laatikosta. Temppu on lisätä suuri siirtymäkesto ja viive – niin suuri, että kuvan palaaminen alkuperäiseen asentoonsa vie paljon aikaa.

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

Määrittämällä vain transition-delay riittää, mutta suurten arvojen käyttäminen sekä viiveellä että kestolla vähentää todennäköisyyttä, että pelaaja näkee kuvan liikkuvan taaksepäin. Jos odotat 999s + 999s — joka on noin 30 minuuttia — silloin näet kuvan liikkuvan. Mutta et tee, eihän? Tarkoitan, kenelläkään ei kestä niin kauan vuorojen välillä, ellei he kävele pois pelistä. Joten mielestäni tämä on hyvä temppu vaihtaa kahden tilan välillä.

Huomasitko, että myös kuvan siirtäminen käynnistää muutokset? Tämä johtuu siitä, että kuva on osa laatikkoelementtiä, mikä ei ole hyvä meille. Voimme korjata tämän lisäämällä pointer-events: none kuvaan, mutta emme voi vetää sitä myöhemmin.

Tämä tarkoittaa, että meidän on otettava käyttöön toinen elementti .box:

Tuo ylimääräinen div (käytämme luokkaa .a) ottaa saman alueen kuin kuva (kiitos CSS Grid ja grid-area: 1 / 1) ja se on elementti, joka laukaisee hover-efektin. Ja tässä sisarusten valitsin tulee peliin:

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

Leijuu päällä .a elementti siirtää kuvaa, ja koska se vie kaiken tilan laatikon sisällä, on kuin olisimme sen sijaan laatikon päällä! Kuvan siirtäminen ei ole enää ongelma!

Vedetään ja pudotetaan kuvamme laatikon sisään ja katsotaan tulos:

Näitkö tuon? Ota ensin kuva ja siirrä se laatikkoon, ei mitään hienoa. Mutta kun vapautat kuvan, käynnistät hover-tehosteen, joka siirtää kuvaa, ja sitten simuloimme vedä ja pudota -ominaisuutta. Jos vapautat hiiren laatikon ulkopuolella, mitään ei tapahdu.

Hmm, simulaatiosi ei ole täydellinen, koska voimme myös siirtää laatikkoa ja saada saman tehosteen.

Totta ja korjaamme tämän. Hover-tehoste on poistettava käytöstä ja sallittava se vain, jos vapautamme kuvan laatikon sisällä. Leikimme ulottuvuutemme kanssa .a elementtiä, jotta se tapahtuu.

Nyt laatikon leijuminen ei tee mitään. Mutta jos alat vetää kuvaa, .a elementti tulee näkyviin, ja kun se vapautetaan laatikon sisällä, voimme laukaista hover-efektin ja siirtää kuvaa.

Tarkastellaanpa koodia:

.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;
}

Klikkaamalla kuvaa laukeaa :active pseudoluokka, joka tekee .a elementti täysleveä (se on aluksi yhtä suuri kuin 0). Aktiivinen tila säilyy aktiivinen kunnes vapautamme kuvan. Jos vapautamme kuvan laatikon sisällä, .a elementti palaa width: 0, mutta käynnistämme hover-efektin ennen kuin se tapahtuu ja kuva putoaa laatikon sisään! Jos vapautat sen laatikon ulkopuolella, mitään ei tapahdu.

Siinä on pieni omituisuus: tyhjän laatikon napsauttaminen myös siirtää kuvaa ja rikkoo ominaisuutemme. Tällä hetkellä, :active on linkitetty .box elementti, joten sen tai sen lapsien napsauttaminen aktivoi sen; ja tekemällä tämän, päädymme näyttämään .a elementti ja laukaisee hover-efektin.

Voimme korjata sen pelaamalla pointer-events. Sen avulla voimme poistaa käytöstä kaiken vuorovaikutuksen .box samalla kun säilytetään vuorovaikutus lapsielementtien kanssa.

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

Nyt vedä ja pudota -ominaisuus on täydellinen. Jos et löydä kuinka hakkeroida se, ainoa tapa siirtää kuvaa on vetää ja pudottaa se laatikon sisään.

Palapeliruudukon rakentaminen

Palapelin kokoaminen tuntuu helpolta, verrattuna siihen, mitä teimme juuri vedä ja pudota -ominaisuuden suhteen. Luotamme palapelin luomiseen CSS-ruudukon ja taustatemppujen avulla.

Tässä on ruudukkomme, joka on kirjoitettu mopsiksi mukavuuden vuoksi:

- 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") 

Koodi saattaa näyttää oudolta, mutta se käännetään tavalliseksi HTML:ksi:

<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>

Veikkaan, että ihmettelet, mitä noille tunnisteille kuuluu. Millään näistä elementeistä ei ole erityistä merkitystä - huomaan vain, että koodi on paljon helpompi kirjoittaa <z> kuin joukko <div class="z"> tai miten vaan.

Näin olen kartoittanut ne:

  • <g> on verkkosäiliömme, joka sisältää N*N <z> elementtejä.
  • <z> edustaa ruudukkokohteitamme. Se näyttelee roolia .box elementti, jonka näimme edellisessä osiossa.
  • <a> laukaisee hover-efektin.
  • <b> edustaa osaa kuvastamme. Sovellamme draggable attribuuttia, koska sitä ei voi oletusarvoisesti vetää.

Selvä, rekisteröidään verkkosäiliömme <g>. Tämä on Sassissa CSS:n sijaan:

$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);
}

Aiomme itse asiassa tehdä verkkomme lapsista - <z> elementit — myös ruudukot ja niissä on molemmat <a> ja <b> samalla ruudukkoalueella:

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

Kuten näet, ei mitään hienoa – loimme tietyn kokoisen ruudukon. Loput CSS:stä, jota tarvitsemme, on vedä ja pudota -ominaisuutta varten, joka vaatii meidän sijoittamaan palaset satunnaisesti laudan ympärille. Käännyn Sassin puoleen tämän asian vuoksi, jotta voisin käydä läpi ja muotoilla kaikki palapelin palat toiminnolla:

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)
   }
}

Olet ehkä huomannut, että käytän Sassia random() toiminto. Näin saamme satunnaistetut paikat palapelin palasille. Muista, että teemme poista tuossa asennossa, kun viet hiiren osoittimen päällä <a> elementtiä vastaavan vetämällä ja pudottamalla <b> elementti ruudukon solun sisällä.

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

Samassa silmukassa määritän myös taustakokoonpanon jokaiselle palapelin palalle. Ne kaikki jakavat loogisesti saman kuvan kuin tausta, ja sen koon tulee olla yhtä suuri kuin koko ruudukon koko (määritetty --s muuttuja). Käyttämällä samaa background-image ja vähän matematiikkaa, päivitämme background-position näyttää vain osan kuvasta.

Se siitä! Pelkästään CSS-pulmapelimme on teknisesti valmis!

Mutta aina voimme tehdä paremmin, eikö niin? Minä näytin sinulle kuinka tehdä ruudukko palapelin kappaleiden muodoista toisessa artikkelissa. Otetaanpa tämä sama ajatus käyttöön ja sovelletaan sitä tässä, eikö niin?

Palapelin muodot

Tässä on uusi pulmapelimme. Sama toiminnallisuus, mutta realistisemmat muodot!

Tämä on esimerkki ruudukon muodoista:

Kuinka tein puhtaan CSS-pulmapelin

Jos katsot tarkasti, huomaat, että meillä on yhdeksän erilaista palapelin muotoa: neljä kulmaa, The neljä reunaaja yksi kaikelle muulle.

Toisessa mainitsemassani artikkelissa tekemäni palapelin osien ruudukko on hieman yksinkertaisempi:

Voimme käyttää samaa tekniikkaa, joka yhdistää CSS-maskit ja liukuvärit eri muotojen luomiseen. Jos et ole perehtynyt mask ja kaltevuudet, suosittelen tarkistamaan tuo yksinkertaistettu tapaus ymmärtääksesi tekniikan paremmin ennen kuin siirryt seuraavaan osaan.

Ensinnäkin meidän on käytettävä erityisiä valitsimia kohdistaaksemme jokaiseen elementtiryhmään, jolla on sama muoto. Meillä on yhdeksän ryhmää, joten käytämme kahdeksaa valitsinta sekä oletusvalitsinta, joka valitsee ne kaikki.

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 */

Tässä on kuva, joka näyttää, kuinka se liittyy ruudukoomme:

Kuinka tein puhtaan CSS-pulmapelin PlatoBlockchain Data Intelligence. Pystysuuntainen haku. Ai.
Kuinka tein puhtaan CSS-pulmapelin

Nyt käsitellään muotoja. Keskitytään vain yhden tai kahden muodon oppimiseen, koska ne kaikki käyttävät samaa tekniikkaa – ja siten sinulla on läksyjä oppimista varten!

Kun kyseessä ovat ruudukon keskellä olevat palapelin palat, 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);

Koodi saattaa näyttää monimutkaiselta, mutta keskitytään yhteen gradienttiin kerrallaan nähdäksesi, mitä tapahtuu:

Kaksi liukuväriä luo kaksi ympyrää (merkitty esittelyssä vihreäksi ja violetiksi), ja kaksi muuta liukuväriä luo paikat, joihin muut palat liittyvät (sininen täyttää suurimman osan muodosta, kun taas punaiseksi merkitty yläosan). CSS-muuttuja, --r, määrittää pyöreän muotojen säteen.

Kuinka tein puhtaan CSS-pulmapelin PlatoBlockchain Data Intelligence. Pystysuuntainen haku. Ai.
Kuinka tein puhtaan CSS-pulmapelin

Palapelin palasten muoto keskellä (merkitty 0 kuvassa) on vaikein tehdä, koska se käyttää neljää gradienttia ja siinä on neljä kaarevuutta. Kaikki muut palat jongleeraavat vähemmän gradientteja.

Esimerkiksi palapelin palaset palapelin yläreunaa pitkin (merkitty 2 kuvassa) käyttää kolmea gradienttia neljän sijaan:

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);

Poistimme ensimmäisen (ylemmän) gradientin ja sääsimme toisen gradientin arvoja niin, että se peittää jäljelle jääneen tilan. Et huomaa suurta eroa koodissa, jos vertaat kahta esimerkkiä. On huomattava, että voimme löytää erilaisia ​​taustakonfiguraatioita saman muodon luomiseksi. Jos aloitat pelaamisen gradienteilla, tulet varmasti keksimään jotain erilaista kuin mitä tein. Voit jopa kirjoittaa jotain ytimekkäämpää - jos on, jaa se kommenteissa!

Muotojen luomisen lisäksi huomaat myös, että lisään elementtien leveyttä ja/tai korkeutta kuten alla:

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

Palapelin palasten on täytettävä ruudukkosolunsa muodostaakseen yhteyden.

Kuinka tein puhtaan CSS-pulmapelin PlatoBlockchain Data Intelligence. Pystysuuntainen haku. Ai.
Kuinka tein puhtaan CSS-pulmapelin

Lopullinen demo

Tässä taas koko demo. Jos vertaat sitä ensimmäiseen versioon, näet saman koodirakenteen ruudukon ja vedä ja pudota -ominaisuuden luomiseksi sekä koodin muotojen luomiseksi.

Mahdollisia parannuksia

Artikkeli päättyy tähän, mutta voisimme jatkaa palapelimme parantamista vieläkin enemmän! Entä aa ajastin? Tai ehkä jonkinlaiset onnittelut, kun pelaaja on saanut palapelin valmiiksi?

Voin harkita kaikkia näitä ominaisuuksia tulevassa versiossa, joten pidä silmällä GitHub-repostiani.

Käärimistä

Ja CSS ei ole ohjelmointikieli, he sanovat. Hah!

En yritä herättää sillä mitään #HotDramaa. Sanon sen, koska teimme todella hankalia logiikkaa ja käsittelimme paljon CSS-ominaisuuksia ja -tekniikoita matkan varrella. Pelasimme CSS Gridillä, siirtymillä, maskeilla, liukuväreillä, valitsimilla ja taustaominaisuuksilla. Puhumattakaan muutamista Sass-temppuista, joita käytimme tehdäksemme koodimme helpoksi säädettäväksi.

Tavoitteena ei ollut rakentaa peliä, vaan tutkia CSS:ää ja löytää uusia ominaisuuksia ja temppuja, joita voit käyttää muissa projekteissa. Verkkopelin luominen CSS:ssä on haaste, joka pakottaa sinut tutustumaan CSS:n ominaisuuksiin yksityiskohtaisesti ja oppimaan niiden käyttöä. Lisäksi on vain hauskaa, että saamme leikkiä, kun kaikki on sanottu ja tehty.

Se, onko CSS ohjelmointikieli vai ei, ei muuta sitä tosiasiaa, että opimme aina rakentamalla ja luomalla innovatiivisia juttuja.

Aikaleima:

Lisää aiheesta CSS-temppuja