Kuidas ma tegin puhta CSS-i puslemängu PlatoBlockchain Data Intelligence. Vertikaalne otsing. Ai.

Kuidas ma tegin puhta CSS-i puslemängu

Avastasin hiljuti ainult CSS-i mängude loomise rõõmu. See on alati põnev, kuidas HTML ja CSS on võimelised toime tulema kogu võrgumängu loogikaga, nii et ma pidin seda proovima! Sellised mängud tuginevad tavaliselt vanale märkekasti häkkimisele, kus kombineerime HTML-sisendi märgistatud/märkimata oleku ja :checked pseudoklass CSS-is. Selle ühe kombinatsiooniga saame teha palju maagiat!

Tegelikult esitasin endale väljakutse ehitada terve mäng ilma Checkboxita. Ma ei olnud kindel, kas see on võimalik, aga kindlasti on, ja ma näitan teile, kuidas.

Lisaks mõistatusmängule, mida selles artiklis uurime, olen teinud puhaste CSS-mängude kogu, enamik neist ilma märkeruudu häkkimiseta. (Need on ka saadaval CodePenis.)

Kas soovite enne alustamist mängida?

Mina isiklikult eelistan mängu mängida täisekraanirežiimis, kuid saate seda mängida allpool või ava see siin.

Lahe eks? Ma tean, et see pole parim puslemäng, mida olete kunagi näinud™, kuid see pole ka üldse halb millegi jaoks, mis kasutab ainult CSS-i ja mõnda rida HTML-i. Saate hõlpsalt reguleerida ruudustiku suurust, muuta raskusastme kontrollimiseks lahtrite arvu ja kasutada mis tahes pilti, mida soovite!

Teeme selle demo koos uuesti, seejärel lisame sellesse lõpus veidi lisasära.

Pukseerimise funktsioon

Kuigi pusle ülesehitus on CSS-ruudustikuga üsna arusaadav, on pusletükkide pukseerimine veidi keerulisem. Selle tegemiseks pidin toetuma üleminekute, hõljutusefektide ja vendade valijate kombinatsioonile.

Kui hõljutate kursorit demos tühja kasti kohal, liigub pilt selle sisse ja jääb sinna isegi siis, kui liigutate kursori kastist välja. Trikk on lisada suur ülemineku kestus ja viivitus – nii suur, et pildil kulub algsesse asendisse naasmiseks palju aega.

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äärates ainult transition-delay on piisav, kuid suurte väärtuste kasutamine nii viivituse kui ka kestuse osas vähendab võimalust, et mängija näeb pilti kunagi tagasi liikumas. Kui ootad 999s + 999s — mis on umbes 30 minutit — siis näete kujutist liikumas. Aga sa ei tee, eks? Ma mõtlen, et kellelgi ei lähe pöörete vahel nii kaua aega, kui ta just mängust eemale ei kõnni. Seega pean seda heaks nipiks kahe oleku vahel vahetamiseks.

Kas märkasite, et muudatused käivitab ka pildi hõljutamine? Seda seetõttu, et pilt on osa kastielemendist, mis pole meile kasulik. Saame selle parandada lisades pointer-events: none pildile, kuid me ei saa seda hiljem lohistada.

See tähendab, et peame sisse viima veel ühe elemendi .box:

See lisa div (kasutame klassi .a) võtab pildiga sama ala (tänu CSS Gridile ja grid-area: 1 / 1) ja see on element, mis käivitab hõljumise efekti. Ja siin tulebki mängu õdede-vendade valija:

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

Hõljub peal .a element liigutab pilti ja kuna see võtab kogu kasti sees ruumi, siis tundub, nagu hõljuksime selle asemel kasti kohal! Pildi hõljutamine pole enam probleem!

Pukseerime oma pildi kasti sisse ja vaatame tulemust:

Kas sa nägid seda? Kõigepealt haarate pildist ja liigutate selle kasti, ei midagi uhket. Kuid kui pildi vabastate, käivitate hõljumise efekti, mis pilti liigutab, ja seejärel simuleerime pukseerimisfunktsiooni. Kui vabastate hiire väljaspool kasti, ei juhtu midagi.

Hmm, teie simulatsioon ei ole täiuslik, sest me võime ka kasti hõljutada ja saada sama efekti.

See on tõsi ja me parandame selle. Peame hõljumise efekti keelama ja lubama seda ainult siis, kui vabastame pildi kastis. Mängime oma mõõtmetega .a element, et see juhtuks.

Nüüd ei tee kasti hõljutamine midagi. Aga kui hakkate pilti lohistama, .a ilmub element ja kui see kasti sees vabastatakse, saame käivitada hõljumise efekti ja pilti liigutada.

Lahkame koodi:

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

Pildil klõpsates käivitub :active pseudoklass, mis muudab .a elemendi täislaius (see on algselt võrdne 0). Aktiivne olek jääb alles aktiivne kuni pildi vabastame. Kui vabastame pildi kasti sees, siis .a element läheb tagasi width: 0, kuid käivitame hõljumise efekti enne, kui see juhtub ja pilt kukub kasti sisse! Kui vabastate selle väljaspool kasti, ei juhtu midagi.

Siin on väike veidrus: tühja kasti klõpsamine liigutab ka pilti ja rikub meie funktsiooni. Praegu :active on seotud .box element, nii et sellel või mõnel selle alagrupil klõpsamine aktiveerib selle; ja seda tehes näitame lõpuks .a element ja hõljumise efekti käivitamine.

Me saame selle parandada mängides pointer-events. See võimaldab meil keelata igasuguse suhtluse rakendusega .box säilitades samal ajal suhtluse lapse elementidega.

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

Nüüd meie pukseerimisfunktsioon on täiuslik. Kui te ei leia, kuidas seda häkkida, on ainus viis pildi liigutamiseks lohistada see kasti.

Pusleruudustiku ehitamine

Pusle kokkupanemine on lihtne, võrreldes sellega, mida me just pukseerimisfunktsiooni jaoks tegime. Mõistatuse loomisel kasutame CSS-i ruudustikku ja taustanippe.

Siin on meie ruudustik, mis on mugavuse huvides kirjutatud mopsis:

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

Kood võib tunduda kummaline, kuid see kompileeritakse lihtsaks HTML-iks:

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

Vean kihla, et mõtlete, mis nende siltidega lahti on. Ühelgi neist elementidest pole erilist tähendust – ma lihtsalt leian, et koodi on palju lihtsam kirjutada <z> kui hunnik <div class="z"> või mida iganes.

Ma olen need kaardistanud järgmiselt:

  • <g> on meie võrgukonteiner, mis sisaldab N*N <z> elemente.
  • <z> esindab meie ruudustiku elemente. See mängib rolli .box element, mida nägime eelmises jaotises.
  • <a> käivitab hõljumise efekti.
  • <b> esindab osa meie pildist. Me rakendame draggable atribuut sellel, kuna seda ei saa vaikimisi lohistada.

Olgu, registreerime oma võrgukonteineri <g>. See on CSS-i asemel Sassis:

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

Me tegelikult teeme oma võrgust lapsed - <z> elemendid — samuti ruudud ja neil on mõlemad <a> ja <b> samas ruudustiku piirkonnas:

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

Nagu näete, pole midagi uhket – lõime kindla suurusega ruudustiku. Ülejäänud CSS-i, mida vajame, on pukseerimisfunktsiooni jaoks, mis eeldab, et me paneme nupud juhuslikult laua ümber. Pöördun selleks Sassi poole, et saaksin kõiki pusletükke funktsiooniga läbi vaadata ja stiilida:

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

Võib-olla olete märganud, et ma kasutan Sassi random() funktsiooni. Nii saame pusletükkide juhuslikud asukohad. Pidage meeles, et me teeme seda keelata selles asendis, kui hõljute kursorit <a> element pärast selle vastava pukseerimist <b> ruudustiku lahtri sees olev element.

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

Samas tsüklis määran ma ka iga pusletüki tausta konfiguratsiooni. Kõik need jagavad loogiliselt sama pilti kui taust ja selle suurus peaks olema võrdne kogu ruudustiku suurusega (määratletud --s muutuja). Kasutades sama background-image ja natuke matemaatikat, värskendame background-position et näidata ainult osa pildist.

See on kõik! Meie ainult CSS-iga puslemäng on tehniliselt valmis!

Aga me saame alati paremini teha, eks? Ma näitasin sulle kuidas teha pusletükkide kujunditest ruudustikku teises artiklis. Võtame sama idee ja rakendame seda siin, eks?

Pusletükkide kujundid

Siin on meie uus puzzle mäng. Sama funktsionaalsus, kuid realistlikumate kujunditega!

See on ruudustiku kujundite illustratsioon:

Kuidas ma tegin puhta CSS-i puslemängu

Kui vaatate tähelepanelikult, märkate, et meil on üheksa erinevat pusletüki kuju: neli nurka, neli servaja üks kõige muu jaoks.

Pusletükkide ruudustik, mille tegin teises artiklis, millele viitasin, on veidi sirgjoonelisem:

Erinevate kujundite loomiseks saame kasutada sama tehnikat, mis kombineerib CSS-i maske ja gradiente. Kui te pole tuttav mask ja kalded, soovitan tungivalt kontrollida see lihtsustatud juhtum et enne järgmise osa juurde liikumist tehnikast paremini aru saada.

Esiteks peame kasutama konkreetseid valijaid, et sihtida iga elementide rühma, millel on sama kuju. Meil on üheksa rühma, seega kasutame kaheksat valijat ja vaikevalijat, mis valib need kõik.

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

Siin on joonis, mis näitab, kuidas see meie ruudustikuga seostub:

Kuidas ma tegin puhta CSS-i puslemängu PlatoBlockchain Data Intelligence. Vertikaalne otsing. Ai.
Kuidas ma tegin puhta CSS-i puslemängu

Nüüd käsitleme kujundeid. Keskendume vaid ühe või kahe kujundi õppimisele, sest need kõik kasutavad sama tehnikat – ja nii on teil õppimise jätkamiseks kodutööd!

Võrestiku keskel olevate pusletükkide jaoks 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);

Kood võib tunduda keeruline, kuid keskendume ühele gradiendile korraga, et näha, mis toimub:

Kaks gradienti loovad kaks ringi (demol on märgitud roheliseks ja lillaks) ning kaks muud gradienti loovad pilud, millega teised tükid ühenduvad (sinine tähistab suurema osa kujundist, punasega tähistatud aga ülemise osa). CSS-i muutuja, --r, määrab ringikujuliste kujundite raadiuse.

Kuidas ma tegin puhta CSS-i puslemängu PlatoBlockchain Data Intelligence. Vertikaalne otsing. Ai.
Kuidas ma tegin puhta CSS-i puslemängu

Pusletükkide kuju keskel (märgitud 0 joonisel) on kõige raskem teha, kuna see kasutab nelja gradienti ja sellel on neli kumerust. Kõik ülejäänud tükid žongleerivad vähemate gradientidega.

Näiteks pusle tükid piki pusle ülemist serva (märgitud 2 joonisel) kasutab nelja gradiendi asemel kolme:

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

Eemaldasime esimese (ülemise) gradiendi ja kohandasime teise gradiendi väärtused nii, et see kataks maha jäänud ruumi. Kahe näite võrdlemisel ei märka te koodis suurt erinevust. Tuleb märkida, et sama kujundi loomiseks võime leida erinevaid taustakonfiguratsioone. Kui hakkate gradientidega mängima, leiate kindlasti midagi muud kui see, mida tegin. Võite isegi kirjutada midagi kokkuvõtlikumat - kui jah, siis jagage seda kommentaarides!

Lisaks kujundite loomisele leiate ka, et suurendan elementide laiust ja/või kõrgust nagu allpool:

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

Pusle tükid peavad ühendamiseks oma võrguelemendist üle ajama.

Kuidas ma tegin puhta CSS-i puslemängu PlatoBlockchain Data Intelligence. Vertikaalne otsing. Ai.
Kuidas ma tegin puhta CSS-i puslemängu

Lõplik demo

Siin on jälle täielik demo. Kui võrrelda seda esimese versiooniga, näete sama koodistruktuuri ruudustiku ja pukseerimisfunktsiooni loomiseks ning koodi kujundite loomiseks.

Võimalikud täiendused

Artikkel lõpeb siin, kuid me võiksime oma mõistatust veelgi täiustada veelgi rohkemate funktsioonidega! Kuidas oleks aa taimeriga? Või äkki õnnitlused, kui mängija pusle lõpetab?

Võib-olla kaalun kõiki neid funktsioone tulevases versioonis hoidke minu GitHubi repol silma peal.

Pakke kuni

And CSS ei ole programmeerimiskeel, nad ütlesid. ha!

Ma ei ürita sellega mingit #HotDramat tekitada. Ütlen seda sellepärast, et tegime väga keerulisi loogikaasju ja käsitlesime selle käigus palju CSS-i omadusi ja tehnikaid. Mängisime CSS-võrgu, üleminekute, maskeerimise, gradientide, selektorite ja taustaomadustega. Rääkimata vähestest Sassi nippidest, mida kasutasime koodi hõlpsaks kohandamiseks.

Eesmärk ei olnud mängu üles ehitada, vaid uurida CSS-i ning avastada uusi omadusi ja nippe, mida saad kasutada teistes projektides. CSS-is võrgumängu loomine on väljakutse, mis sunnib teid CSS-i funktsioone üksikasjalikult uurima ja neid kasutama õppima. Lisaks on see lihtsalt väga lõbus, et saame millegagi mängida, kui kõik on öeldud ja tehtud.

See, kas CSS on programmeerimiskeel või mitte, ei muuda tõsiasja, et me õpime alati uuenduslikke asju ehitades ja luues.

Ajatempel:

Veel alates CSSi trikid