Hoe het Romeinse Rijk Maakte Pure CSS Connect 4 Mogelijk

0
20

Experimenten zijn een leuk excuus om te leren van de nieuwste trucs bedenken van nieuwe ideeën, en duw je grenzen. “Pure CSS” demo ‘ s zijn een zaak voor een tijdje, maar nieuwe mogelijkheden open, zoals browsers en CSS zelf evolueert. CSS en HTML preprocessors hielp ook de scène vooruit. Soms preprocessors worden gebruikt voor hardcoding elk mogelijk scenario, bijvoorbeeld, lange reeksen :gecontroleerd en aangrenzende broer of zus kiezers.

In dit artikel zal ik een wandeling door de belangrijkste ideeën van een Pure CSS Connect 4 spel ik gebouwd heb. Ik heb geprobeerd om te voorkomen dat hardcoding zo veel als ik kon in mijn experiment en werkte zonder preprocessors om zich te concentreren op het houden van de resulterende code te kort. U kunt zien dat alle code en het spel hier:

Zie de Pen Pure CSS Connect 4 door Michal Szabó (@finnhvman) op CodePen.

Essentiële begrippen

Ik denk dat er een aantal concepten die essentieel worden geacht in de “pure CSS” genre. Meestal vorm elementen zijn gebruikt voor het beheren van state en het vastleggen van acties van de gebruiker. Ik was opgewonden toen ik mensen gebruiken <button type=”reset”> reset of start een nieuw spel. Alles wat je hoeft te doen is pak je de elementen in een <form> tag en de knop toevoegen. Dit is naar mijn mening een veel betere oplossing dan de pagina te vernieuwen.

Mijn eerste stap was het maken van een formulier element dan gooit een heleboel input voor de slots en voeg de reset-knop. Hier is een eenvoudige demonstratie van de <button type=”reset”> in actie:

Zie de Pen Pure HTML-Formulier opnieuw instellen door Michal Szabó (@finnhvman) op CodePen.

Ik wilde een mooi visueel voor deze demo om te zorgen voor een volledige ervaring. In plaats van te trekken in een externe afbeelding voor de raad van bestuur of de schijven, gebruikte ik een radiaal verloop(). Een mooie bron die ik vaak gebruik is Lea Verou de CSS3 Patronen Gallery. Het is een verzameling van patronen gemaakt door gradiënten, en ze bewerkbaar! Ik gebruikte currentcolor, die kwam erg handig voor de schijf patroon. Ik voegde een header en hergebruikt mijn Pure CSS Rimpeling Knop.

Op dit moment is de lay-out en de disc ontwerp was al definitief is, alleen dat het spel niet werkt op alle

Vallen schijven op het bord

Vervolgens heb ik ervoor gezorgd dat gebruikers hun beurt vallen schijven op de Connect 4 raad van bestuur. In Connect 4 spelers (een rode en een gele) drop schijven in de kolommen in beurten. Er zijn 7 kolommen en 6 rijen (42 slots). Elk slot kan worden leeg of bezet door een rode of gele schijf. Dus, een slot kunnen drie statussen hebben (leeg, rood of geel). Discs daalde in dezelfde kolom zijn op elkaar gestapeld.

Ik ben begonnen met het plaatsen van twee selectievakjes voor elk slot. Als ze het beide niet aangevinkt, is de sleuf wordt beschouwd als leeg, en wanneer één van hen is ingeschakeld, wordt de desbetreffende speler heeft de schijf in.

De mogelijke toestand van het hebben van hen beiden gecontroleerd moet worden vermeden door ze te verbergen zodra een van hen wordt gecontroleerd. Deze selectievakjes zijn directe broers en zussen, dus als de eerste van een paar is aangevinkt kunt u verbergen zowel door het gebruik van :gecontroleerd pseudo-class en het naast verwant combinator (+). Wat als de tweede wordt gecontroleerd? U kunt verbergen de tweede, maar hoe beïnvloedt de eerste? Nou, er is geen vorige sibling selector, dat is gewoon niet hoe CSS selectors werk. Ik moest verwerpen dit idee.

Eigenlijk een aankruisvakje kunnen drie statussen hebben door zelf, het kan in de onbepaald. Het probleem is dat je niet kunt zetten in onbepaalde staat met alleen HTML. Zelfs als je zou kunnen, de volgende klik op de checkbox zou altijd het transformeren naar aangevinkt staat. Het forceren van de tweede speler dubbel-klik bij het verplaatsen is onbetrouwbaar en onacceptabel.

Ik zat vast op de MDN doc van :onbepaalde en merkte dat de radio-ingangen hebben ook onbepaald. Keuzerondjes met dezelfde naam zijn in deze staat als ze allemaal uitgeschakeld zijn. Wow, dat is een feitelijke eerste staat! Wat echt nuttig is dat het controleren van de laatste broer of zus heeft ook een effect op de vorige! Dus vulde ik in de raad van bestuur met 42 paren van radio-ingangen.

In retrospect, slim bestellen en gebruik van labels met selectievakjes of keuzerondjes zou hebben gemaakt van de truc, maar ik heb geen rekening met labels om een optie om de code eenvoudiger en korter.

Ik wilde hebben grote gebieden voor interactie te hebben mooie UX, dus ik dacht dat het redelijk is om spelers te laten maken met een verhuizing door te klikken op een kolom. Ik gestapeld de controles van dezelfde kolom op elke andere door het toevoegen van absolute en relatieve positionering van de juiste elementen. Op deze manier alleen de laagste lege sleuf kan worden geselecteerd in een kolom. Ik nauwgezet de tijd van de overgang van de schijf valt per rij en hun timing functie is een aanpassing van een kwadratische curve te lijken op een realistische vrije val. Zo ver de stukjes van de puzzel kwam goed samen, al is de animatie hieronder laat duidelijk zien dat alleen de rode speler hun bewegingen te maken.

Zelfs als alle controles zijn er, alleen rode schijven kunnen worden geplaatst op het bord

De klikbare gebieden van de radio-ingangen worden gevisualiseerd met gekleurde semi-transparante rechthoeken. De gele en rode ingangen worden gestapeld boven elkaar zes keer(=zes rijen per kolom, waardoor de rode ingang van de onderste rij op de top van de stack. Het mengsel van rood en geel maakt de oranjeachtige kleur die kan worden gezien op het bord bij de start. De minder lege slots zijn beschikbaar in een kolom, de minder intense dit oranjeachtige kleur krijgt sinds de radio-ingangen worden niet weergegeven wanneer ze niet :nader te bepalen. Door de rode ingang altijd precies op de gele ingang in elke sleuf, alleen de rode speler is in staat om bewegingen te maken.

Het bijhouden van bochten

Ik had slechts een vaag idee en veel hoop dat ik op één of andere manier op te lossen schakelen schakelt tussen de twee spelers met de algemene sibling selector. Het concept was te laat de rode speler neemt u als het aantal gecontroleerde ingangen werd zelfs (0, 2, 4, enz.) en laat de gele speler neemt u wanneer dat aantal is oneven. Al snel realiseerde ik me dat de algemene sibling selector niet (en mag niet!) werk op de manier die ik wilde.

Vervolgens een zeer voor de hand liggende keuze was om te experimenteren met de n-selectors. Echter het aantrekken was het gebruik van de even en oneven trefwoorden, ik liep in een dead-end. De :nth-child selector “telt” de kinderen binnen een ouder, ongeacht het type, klasse, pseudo-class, wat dan ook. De :nth-of-type selector “telt” kinderen van een soort binnen een ouder, ongeacht de klasse of pseudo-class. Het probleem is dus dat ze niet kunnen rekenen op basis van de :aangevinkt staat.

CSS tellers tellen ook mee, dus waarom niet geef ze een keer te proberen? Een gemeenschappelijk gebruik van de tellers is om een aantal rubrieken (ook in meerdere niveaus) in een document. Ze worden aangestuurd door CSS-regels, willekeurig reset op elk gewenst moment en hun verhogen (of te verlagen!) waarden kunnen worden een geheel getal. De tellers worden weergegeven door de teller() functie in de inhoud eigendom.

De makkelijkste stap was het opzetten van een teller en te tellen :gecontroleerd ingangen in de Connect 4 raster. Er zijn slechts twee problemen met deze aanpak. De eerste is dat je niet meer kan uitvoeren aanstelling op een counter om te detecteren of het even of oneven is. De tweede is dat je kan toepassen CSS-regels om elementen op basis van de waarde van de teller.

Ik slaagde erin om het overwinnen van de eerste uitgave door het maken van de binaire teller. De waarde van de teller is in eerste instantie nul. Als de rode speler de controle van hun radio-knop de teller wordt met één verhoogd. Wanneer de gele speler controleert hun radio-knop de teller wordt verlaagd met één, enzovoort. Daarom is de waarde van de teller wordt op nul of één, even of oneven.

Het oplossen van het tweede probleem vereist veel meer creativiteit (lees: hack). Zoals vermeld tellers kunnen worden weergegeven, maar alleen in de ::before en ::after pseudo-elementen. Dat is een no-brainer, maar hoe kunnen zij van invloed op de andere elementen? Op zijn minst de waarde van de teller, kunt u de breedte van de pseudo-element. Verschillende nummers hebben verschillende breedtes. Figuur 1 is doorgaans dunner dan 0, maar dat is iets wat erg moeilijk te controleren. Als het aantal tekens wijzigen in plaats van het teken zelf de resulterende breedte verandering is beter controleerbaar. Het is niet ongewoon om het gebruik van Romeinse cijfers met CSS tellers. Één en twee vertegenwoordigd in Romeinse cijfers zijn hetzelfde karakter eenmaal en tweemaal en dus ook de breedte in pixels.

Mijn idee was om te verbinden met de knoppen van een speler (geel) naar links, en bevestig de knoppen van de andere speler (rood) naar het recht van hun gemeenschappelijke bovenliggende container. In eerste instantie, de rode knoppen zijn bedekt op de gele toets, dan wordt de breedte wijzigen van de container zou leiden tot de rode knoppen te “ga weg” en onthullen de gele knoppen. Een soortgelijke real-world concept is het schuifvenster met twee vensters, en een paneel is opgelost (gele toets), de andere is te verschuiven (rode knoppen) meer dan de andere. Het verschil is dat in het spel slechts de helft van het venster zichtbaar is.

So far, So good, maar ik was nog niet tevreden met de font-grootte (en de andere eigenschappen van het lettertype) indirect het beheersen van de breedte. Ik dacht letter-spacing zou mooi passen hier, omdat het verhoogt alleen de grootte in één dimensie. Onverwacht, zelfs een brief van de tekenafstand (die wordt weergegeven na de brief), en twee brieven maken van de letter-afstand twee keer. Voorspelbaar breedtes zijn van cruciaal belang om dit betrouwbaar is. Nul-byte tekens, samen met enkele en dubbele letter spatiering zou werken, maar het is gevaarlijk om de lettertype-grootte in op nul. Het definiëren van groot letter-spacing (in pixels) en een kleine (1) font-size maakte het bijna overal in alle browsers, ja ik heb het over sub-pixels.

Ik nodig de breedte van de houder om te wisselen tussen de initiële grootte (=w) en ten minste het dubbele van de oorspronkelijke grootte (>=2w) volledig te kunnen verbergen en tonen van de gele knoppen. Laten we zeggen dat v de weergegeven breedte van de ‘ik’ – teken (lagere romeinse vertegenwoordiging, varieert in verschillende browsers), en c is de weergegeven breedte (constant) van de letter-afstand. Ik moest v + c = w om waar te zijn, maar het kon niet, omdat c en b gehele getallen zijn, maar v is een niet-geheel getal. Uiteindelijk heb ik met behulp min-width en max-width eigenschappen beperken de mogelijke breedte waarden, dus ik veranderde ook de mogelijke waarden van teller van ‘ik’ en ‘iii’ en zorg ervoor dat de tekst breedtes underflow en het overlopen van de beperkingen. In vergelijkingen dit leek v + c < w, 3v + 3c > 2w, en v << c, die geeft 2/3w < c < w. De conclusie is dat de letter-spacing is iets kleiner dan de oorspronkelijke breedte.

Ik heb redenering zover als de pseudo-element weergeven van de waarde van de teller is de moedermaatschappij van de radio knoppen, het is het niet. Echter, ik heb gemerkt dat de breedte van de pseudo-element wijzigt de breedte van het parent element, en in dit geval de ouder is de container van de keuzerondjes.

Als u denkt kon dit niet worden opgelost met arabische cijfers? Je hebt gelijk, afwisselend de waarde van de teller tussen iets als ‘1’ en ‘111’ ook zou werken. Niettemin, Romeinse cijfers gaf mij het idee in de eerste plaats, en ze waren ook een goed excuus voor de clickbaity titel, dus ik bleef.

De spelers om de beurten met de rode speler

Het toepassen van de techniek besproken, maakt de bovenliggende container van de radio-ingangen dubbel in de breedte als een rode ingang is ingeschakeld en maakt het oorspronkelijke breedte als een geel-ingang is ingeschakeld. In de oorspronkelijke breedte van de container de rode ingangen zijn over de gele, maar in de dubbele breedte van de container, de rode ingangen zijn verhuisd.

Het herkennen van patronen

In het echte leven, de Verbinding 4 van commissarissen is niet te zien of je hebt gewonnen of verloren, maar het verstrekken van juiste feedback is een onderdeel van een goede gebruikerservaring op de software. Het volgende doel is om te detecteren of een speler heeft het spel gewonnen. Om te winnen het spel een speler heeft als eerste vier schijven in een kolom, rij of diagonaal. Dit is een zeer eenvoudige taak te lossen in vele programmeer talen, maar in pure CSS wereld, dit is een enorme uitdaging. Het af te breken om de deeltaken tot stand is de manier om dit te benaderen systematisch.

Ik heb een flex container als de ouder van de radio knoppen en schijven. Een gele radio knop, een rode knop radio en een div voor de schijf die behoren tot een slot. Een dergelijk slot wordt herhaald 42 keer en gerangschikt in kolommen die wrap. Bijgevolg, de sleuven in een kolom naast elkaar liggen, dat maakt het herkennen van vier in een kolom de gemakkelijkste deel met de aangrenzende kiezen:

<div class=”grid”>
<input type=”radio” name=”slot11″>
<input type=”radio” name=”slot11″>
<div class=”disc”></div>
<input type=”radio” name=”slot12″>
<input type=”radio” name=”slot12″>
<div class=”disc”></div>

<input type=”radio” name=”slot16″>
<input type=”radio” name=”slot16″>
<div class=”disc”></div>

<input type=”radio” name=”slot21″>
<input type=”radio” name=”slot21″>
<div class=”disc”></div>

</div>
/* Rood vier in een kolom kiezen */
input:gecontroleerd + .disc + input + ingang:gecontroleerd + .disc + input + ingang:gecontroleerd + .disc + input + ingang:gecontroleerd ~ .uitkomst

/* Gele vier in een kolom kiezen */
input:gecontroleerd + input + .disc + ingang:gecontroleerd + input + .disc + ingang:gecontroleerd + input + .disc + ingang:gecontroleerd ~ .uitkomst

Dit is een eenvoudige maar lelijke oplossing. Er zijn 11 van het type en de klasse-elementen samengevoegd per speler ter dekking van het geval van de vier in een kolom. Het toevoegen van een div met de class van .resultaat na de elementen van de gokkasten maakt het mogelijk om voorwaardelijk weer te geven van de uitkomst bericht. Er is ook een probleem met het valselijk en opsporen van vier in een kolom waar de kolom wordt verpakt, maar laten we gewoon deze kwestie.

Een soortgelijke aanpak voor het opsporen van vier op een rij, zou echt een verschrikkelijk idee. Er zou een 56-elementen samengevoegd per speler (als ik deed de wiskunde rechts), en niet te vergeten dat ze een soortgelijke fout van valse detectie. Dit is een situatie waar de :nth-child(An+B [S]) of de kolom combinators zal zeker van pas komen in de toekomst.

Voor een betere semantiek kunnen toevoegen een nieuwe div voor elke kolom en het regelen van de sleuf elementen in hen. Deze wijziging zou ook het elimineren van de mogelijkheid van valse detectie hierboven vermeld. Dan is het opsporen van vier op een rij kon gaan zoals: selecteer een kolom waar de eerste rode radio-ingang is ingeschakeld en selecteer het naast verwant kolom waar de eerste rode radio-ingang is ingeschakeld, en dus op twee keer. Dit klinkt erg omslachtig en zou vereisen dat de “ouder” – selector toewijzen.

Het selecteren van de ouder is niet haalbaar, maar het selecteren van het kind is. Hoe zou het opsporen van vier in een rij gaan met de beschikbare combinators en keuzemogelijkheden? Selecteer een kolom en kies vervolgens de eerste rood-radio-invoer als deze optie is aangevinkt en selecteer de aangrenzende kolom, dan selecteert u de eerste rood-radio-invoer als deze optie is aangevinkt, en dus op twee keer. Het klinkt nog steeds omslachtig, nog niet mogelijk. De truc is niet alleen in de CSS, maar ook in de HTML-code, de volgende kolom worden de zus van de keuzerondjes in de vorige kolom het maken van een geneste structuur.

<div class=”grid-column”>
<input type=”radio” name=”slot11″>
<input type=”radio” name=”slot11″>
<div class=”disc”></div>

<input type=”radio” name=”slot16″>
<input type=”radio” name=”slot16″>
<div class=”disc”></div>

<div class=”kolom”>
<input type=”radio” name=”slot21″>
<input type=”radio” name=”slot21″>
<div class=”disc”></div>

<input type=”radio” name=”slot26″>
<input type=”radio” name=”slot26″>
<div class=”disc”></div>

<div class=”kolom”>

</div>
</div>
</div>
/* Rood vier op een rij voor selectors */
input:nth-of-type(2):gecontroleerd ~ .kolom > invoer:nth-of-type(2):gecontroleerd ~ .kolom > invoer:nth-of-type(2):gecontroleerd ~ .kolom > invoer:nth-of-type(2):gecontroleerd ~ .column::na,
input:nth-of-type(4):gecontroleerd ~ .kolom > invoer:nth-of-type(4):gecontroleerd ~ .kolom > invoer:nth-of-type(4):gecontroleerd ~ .kolom > invoer:nth-of-type(4):gecontroleerd ~ .column::na,

input:nth-of-type(12):gecontroleerd ~ .kolom > invoer:nth-of-type(12):gecontroleerd ~ .kolom > invoer:nth-of-type(12):gecontroleerd ~ .kolom > invoer:nth-of-type(12):gecontroleerd ~ .column::na

Ook de semantiek in de war zijn en deze kiezers zijn alleen voor de rode speler (een andere ronde gaat voor de geel-speler), aan de andere kant het werkt. Een klein voordeel is dat er geen ten onrechte gedetecteerd kolommen of rijen. De weergegeven mechanisme van de uitkomst moest ook worden gewijzigd met behulp van de ::after pseudo-element van een overeenkomende kolom is een consistente oplossing bij de juiste styling is toegepast. Als gevolg van deze, een nep achtste kolom dient te worden toegevoegd na het laatste slot.

Zoals te zien is in de code hierboven, specifieke posities binnen een kolom zijn afgestemd op het detecteren van vier op een rij. De zeer dezelfde techniek kan worden gebruikt voor het opsporen van vier in een diagonaal door het aanpassen van deze posities. Merk op dat de diagonalen kunnen worden in twee richtingen.

input:nth-of-type(2):gecontroleerd ~ .kolom > invoer:nth-of-type(4):gecontroleerd ~ .kolom > invoer:nth-of-type(6):gecontroleerd ~ .kolom > invoer:nth-of-type(8):gecontroleerd ~ .column::na,
input:nth-of-type(4):gecontroleerd ~ .kolom > invoer:nth-of-type(6):gecontroleerd ~ .kolom > invoer:nth-of-type(8):gecontroleerd ~ .kolom > invoer:nth-of-type(10):gecontroleerd ~ .column::na,

input:nth-of-type(12):gecontroleerd ~ .kolom > invoer:nth-of-type(10):gecontroleerd ~ .kolom > invoer:nth-of-type(8):gecontroleerd ~ .kolom > invoer:nth-of-type(6):gecontroleerd ~ .column::na

Het aantal keuzemogelijkheden sterk toegenomen in de laatste run, en dit is zeker een plek waar CSS preprocessors, kan het verminderen van de lengte van de verklaring. Toch, ik denk dat de demo is redelijk kort. Het moet ergens rond het midden op de schaal van hardcoding een keuzemogelijkheid voor elke mogelijke winnende patroon te gebruik 4 magische schakelaars (kolom, rij, twee diagonalen).

Een bericht wordt weergegeven wanneer een speler wint

Het sluiten van achterpoortjes

Alle software is van de rand van de gevallen en ze moeten worden behandeld. De mogelijke uitkomsten van een Connect 4 spel zijn niet alleen de rode of gele speler om te winnen, maar geen van beide spelers winnen het vullen van de raad van bestuur bekend als tekenen. Technisch in dit geval niet het breken van het spel of het produceren van eventuele fouten, wat ontbreekt is de feedback aan de spelers.

Het doel is om te detecteren wanneer er zijn 42 :gecontroleerd radio knoppen op het bord. Dit betekent ook dat geen van hen zijn in de :onbepaald. Dat is een eigen keuze te worden gemaakt voor elke radio group. Radio knoppen ongeldig zijn, wanneer ze zijn :nader te bepalen, anders zijn ze geldig. Dus ik toegevoegd de benodigde attribuut voor elke ingang, dan gebruikt het :pseudo-klasse op het formulier op te sporen trekken.

De loting uitkomst bericht wordt weergegeven wanneer de raad van bestuur is gevuld

Het bedekken van de loting uitkomst introduceerde een bug. In het zeer zeldzame geval van de gele speler om te winnen op de laatste beurt, zowel de win en tekenen berichten worden weergegeven. Dit is omdat de detectie en weergave methode van deze uitkomsten zijn orthogonaal. Ik werkte rond de uitgifte door ervoor te zorgen dat de win-bericht heeft een witte achtergrond en is over de loting bericht. Ik had ook een vertraging van de fade-in de overgang van de trekking bericht, dus het zou niet gemengd met het winnen bericht overgang.

De geel wint bericht over de loting uitkomst het voorkomen dat het weergegeven

Terwijl veel van radio knoppen verborgen zijn achter elkaar door absolute positionering, alle mensen in een onbepaalde toestand kan nog steeds geraadpleegd worden door de tab-toets door de controles. Dit geeft spelers de mogelijkheid om drop hen discs in willekeurige slots. Een manier om dit te behandelen is om gewoon te verbieden toetsenbord interacties door het tabindex attribuut: een instelling van -1 betekent dat het niet bereikbaar zijn via opeenvolgende navigatie via het toetsenbord. Ik had in aanvulling op elke radio-ingang met deze eigenschap om te elimineren deze maas in de wet.

<input type=”radio” name=”slot11″ tabindex=”-1″ required>
<input type=”radio” name=”slot11″ tabindex=”-1″ required>
<div class=”disc”></div>

Beperkingen

Het meest belangrijke nadeel is dat het bestuur niet reageren en het kan storing op kleine viewports vanwege de onbetrouwbare oplossing van het bijhouden van de bochten. Ik durfde niet om het risico te nemen van refactoren naar een snelle oplossing, vanwege de aard van de uitvoering, het voelt veel veiliger met vaste afmetingen.

Een ander probleem is de sticky met de muis op touch-apparaten. Het toevoegen van enkele interactie media query ‘ s op de juiste plaatsen is de makkelijkste manier om te genezen van deze, al zou het elimineren van de vrije val animatie.

Men zou kunnen denken dat het :onbepaalde pseudo-class is al breed wordt ondersteund, en het is. Het probleem is dat het slechts gedeeltelijk ondersteund in sommige browsers. Observeren Opmerking 1 in de compatibiliteit tabel: MS IE en de Rand van het niet ondersteunen van keuzerondjes. Als u de demo in die browsers uw cursor zal veranderen in de niet-toegestaan cursor op het bord, dit is een onbedoeld maar enigszins graceful degradation.

Niet alle browsers ondersteunen :nader te bepalen op radio knoppen

Conclusie

Bedankt voor het maken van het laatste deel! Laten we een paar getallen:

  • 140 HTML-elementen
  • 350 (redelijke) regels van CSS
  • 0 JavaScript
  • 0 van externe bronnen

Over het algemeen ben ik tevreden met het resultaat en de reacties waren geweldig. Ik heb zeker veel geleerd van het maken van deze demo en ik hoop dat ik kan delen in een stuk van het schrijven van dit artikel!