Amikor csatlakoztatsz egy vezérlőt, összenyomod a gombokat, mozgatod a botokat, meghúzod a ravaszt… és fejlesztőként semmit sem látsz. A böngésző természetesen felveszi, de hacsak nem naplózza a számokat a konzolban, akkor láthatatlan. Ez a fejfájás a Gamepad API-val. Évek óta létezik, és valójában elég erős. Elolvashatod a gombokat, botokat, triggereket, a műveket. De a legtöbben nem nyúlnak hozzá. Miért? Mert nincs visszajelzés. Nincs panel a fejlesztői eszközökben. Nincs egyértelmű módja annak, hogy megtudja, hogy a vezérlő egyáltalán azt teszi-e, amit Ön gondol. Olyan érzés, mintha vakon repülnék. Ez annyira zavart, hogy megépítsek egy kis eszközt: Gamepad Cascade Debuggert. Ahelyett, hogy a konzol kimenetét bámulná, élő, interaktív nézetet kap a vezérlőről. Nyomja meg valamit, és az reagál a képernyőn. A CSS Cascade Layers segítségével pedig a stílusok rendezettek maradnak, így egyszerűbb a hibakeresés. Ebben a bejegyzésben megmutatom, miért olyan fájdalmas a vezérlők hibakeresése, hogyan segít a CSS megtisztításában, és hogyan építhetsz újrafelhasználható vizuális hibakeresőt saját projektjeidhez.

Még ha mindet naplózni is tudja, akkor is gyorsan olvashatatlan konzolspamhez jut. Például: [0,0,1,0,0,0,5,0,...] [0,0,0,0,1,0,0,...] [0,0,1,0,0,0,0,...]

Meg tudod mondani, hogy melyik gombot nyomták meg? Lehet, de csak miután megerőltette a szemét, és kihagyott néhány bevitelt. Tehát nem, a hibakeresés nem megy könnyen, ha a bemenetek olvasásáról van szó. 3. probléma: A szerkezet hiánya Még ha össze is dob egy gyors megjelenítőt, a stílusok gyorsan összezavarodhatnak. Az alapértelmezett, az aktív és a hibakeresési állapotok átfedhetik egymást, és egyértelmű szerkezet nélkül a CSS törékennyé válik, és nehezen bővíthető. A CSS Cascade Layers segíthet. A stílusokat „rétegekbe” csoportosítják, amelyek prioritás szerint vannak rendezve, így nem kell többé harcolnia a konkrétumokkal, és nem kell találgatnia: „Miért nem jelenik meg a hibakeresési stílusom?” Ehelyett külön aggályokat tart fenn:

Alap: A vezérlő szabványos, kezdeti megjelenése. Aktív: Kiemelések a megnyomott gombokhoz és az elmozdított botokhoz. Hibakeresés: fedvények fejlesztőknek (pl. numerikus kiolvasások, útmutatók stb.).

Ha ennek megfelelően határoznánk meg a rétegeket a CSS-ben, akkor a következőket kapnánk: /* a legalacsonyabbtól a legmagasabb prioritásig */ @layer base, aktív, hibakeresés;

@réteg alap { /* ... */ }

@réteg aktív { /* ... */ }

@layer debug { /* ... */ }

Mivel minden réteg kiszámíthatóan halmozódik fel, mindig tudja, melyik szabály nyer. Ez a kiszámíthatóság nemcsak könnyebbé teszi a hibakeresést, hanem valójában kezelhetővé is. Kitértünk a problémára (láthatatlan, rendetlen bemenet) és a megközelítésre (egy Cascade Layers segítségével épített vizuális hibakereső). Most lépésről lépésre végigjárjuk a hibakereső felépítésének folyamatát. A hibakereső koncepció A rejtett bemenet láthatóvá tételének legegyszerűbb módja, ha egyszerűen felrajzolja a képernyőre. Ez az, amit ez a hibakereső csinál. A gombok, triggerek és joystickok mind látványt kapnak.

Nyomja meg az A gombot: Egy kör világít. Bökd meg a botot: A kör körbecsúszik. Húzza meg a ravaszt félig: Egy rúd félig megtelik.

Most nem a 0-kat és az 1-eket bámulod, hanem a vezérlő élőben reagálását. Természetesen amint elkezdi halmozni az olyan állapotokat, mint az alapértelmezett, lenyomott, hibakeresési információk, esetleg még egy rögzítési mód is, a CSS egyre nagyobb és összetettebb lesz. Itt jönnek jól a kaszkád rétegek. Íme egy lecsupaszított példa: @réteg alap { .button { háttér: #222; határsugár: 50%; szélesség: 40 képpont; magasság: 40px; } }

@réteg aktív { .button.pressed { háttér: #0f0; /* élénkzöld */ } }

@layer debug { .button::after { tartalom: attr(adat-érték); betűméret: 12 képpont; szín: #fff; } }

A rétegsorrend számít: alap → aktív → hibakeresés.

alap felhívja a vezérlőt. aktív kezeli a lenyomott állapotokat. debug dobja a fedvényeket.

Az ilyen felosztás azt jelenti, hogy nem vívsz furcsa sajátosságháborúkat. Minden rétegnek megvan a maga helye, és mindig tudja, mi nyer. Kiépítés Először vigyünk fel valamit a képernyőre. Nem kell jól kinéznie – csak léteznie kell, hogy legyen mivel dolgozni.

Gamepad Cascade Debugger

A
B
X

A hibakereső inaktív

Ez szó szerint csak dobozok. Még nem izgalmas, de olyan fogantyúkat ad, amelyeket később megragadhatunk CSS-sel és JavaScripttel. Rendben, itt kaszkádrétegeket használok, mert ez rendszerezi a dolgokat, ha több állapotot ad hozzá. Íme egy durva átvétel:

/* =================================== CASCADE RÉTEGEK BEÁLLÍTÁSA A sorrend számít: alap → aktív → hibakeresés =================================== */

/* A rétegsorrend előre meghatározása */ @layer base, aktív, hibakeresés;

/* 1. réteg: Alapstílusok – alapértelmezett megjelenés */ @réteg alap { .button { háttér: #333; határsugár: 50%; szélesség: 70 képpont; magasság: 70 képpont; kijelző: flex; indokol-tartalom: center; align-ites: center; }

.pause { szélesség: 20 képpont; magasság: 70 képpont; háttér: #333; kijelző: inline-block; } }

/* 2. réteg: Aktív állapotok – kezeli a lenyomott gombokat */ @réteg aktív { .button.active { háttér: #0f0; /* Világos zöld lenyomva */ transzformáció: skála(1.1); /* Kissé nagyítja a gombot */ }

.pause.active { háttér: #0f0; transzformáció: scaleY(1.1); /* Lenyomva függőlegesen nyúlik */ } }

/* 3. réteg: Hibakeresési fedvények – fejlesztői információk */ @layer debug { .button::after { tartalom: attr(adat-érték); /* A számértéket mutatja */ betűméret: 12 képpont; szín: #fff; } }

Ennek a megközelítésnek az a szépsége, hogy minden rétegnek világos célja van. Az alapréteg soha nem bírálhatja felül az aktívat, és az aktív soha nem bírálhatja felül a hibakeresést, függetlenül a specifikusságtól. Ez kiküszöböli a CSS-specifikus háborúkat, amelyek általában sújtják a hibakereső eszközöket. Most úgy tűnik, hogy néhány klaszter sötét háttéren ül. Őszintén szólva nem is olyan rossz.

A JavaScript hozzáadása JavaScript idő. Itt a vezérlő valóban csinál valamit. Ezt lépésről lépésre megépítjük. 1. lépés: Állítsa be az államkezelést Először is változókra van szükségünk a hibakereső állapotának nyomon követéséhez: // =================================== // ÁLLAMIRÁNYÍTÁS // ===================================

legyen futás = false; // Nyomon követi, hogy a hibakereső aktív-e legyen rafId; // Tárolja a requestAnimationFrame azonosítót törléshez

Ezek a változók szabályozzák az animációs hurkot, amely folyamatosan olvassa a gamepad bemenetét. 2. lépés: Fogja meg a DOM-referenciákat Ezután hivatkozásokat kapunk az összes frissíteni kívánt HTML-elemre: // =================================== // DOM ELEMEK HIVATKOZÁSAI // ===================================

const btnA = document.getElementById("btn-a"); const btnB = document.getElementById("btn-b"); const btnX = document.getElementById("btn-x"); const pause1 = document.getElementById("pause1"); const pause2 = document.getElementById("pause2"); const status = document.getElementById("állapot");

Ezeket a hivatkozásokat előre tárolni hatékonyabb, mint a DOM ismételt lekérdezését. 3. lépés: Billentyűzet tartalék hozzáadása A fizikai vezérlő nélküli teszteléshez a billentyűzet billentyűit a gombokhoz rendeljük: // =================================== // BILLENTYŰZET VISSZATÉRÉS (vezérlő nélküli teszteléshez) // ===================================

const keyMap = { "a": btnA, "b": btnB, "x": btnX, "p": [pause1, pause2] // A 'p' billentyű mindkét szünetsávot vezérli };

Ez lehetővé teszi a felhasználói felület tesztelését a billentyűzet billentyűinek megnyomásával. 4. lépés: Hozza létre a fő frissítési hurkot Itt történik a varázslat. Ez a funkció folyamatosan fut, és kiolvassa a gamepad állapotát: // =================================== // A GAMEPAD FŐ FRISSÍTÉSI KÖR // ===================================

function updateGamepad() { // Szerezze be az összes csatlakoztatott játékvezérlőt const gamepads = navigator.getGamepads(); if (!gamepad) visszatér;

// Az első csatlakoztatott játékvezérlő használata const gp = játékvezérlők[0];

if (gp) { // Frissítse a gomb állapotait az "aktív" osztály váltásával btnA.classList.toggle("aktív", gp.buttons[0].pressed); btnB.classList.toggle("aktív", gp.buttons[1].pressed); btnX.classList.toggle("aktív", gp.buttons[2].pressed);

// Szünet gomb kezelése (a gombindex 9 a legtöbb vezérlőn) const pausePressed = gp.buttons[9].pressed; pause1.classList.toggle("aktív", pausePressed); pause2.classList.toggle("aktív", pausePressed);

// Listát készíthet az aktuálisan lenyomott gombokról az állapotkijelzéshez let pressed = []; gp.buttons.forEach((btn, i) => { ha (btn.pressed)pressed.push("Button " + i); });

// Frissítse az állapotszöveget, ha bármelyik gombot megnyomja if (pressed.length > 0) { status.textContent = "Lenyomva: " + pressed.join(", "); } }

// Folytassa a ciklust, ha a hibakereső fut if (futás) { rafId = requestAnimationFrame(updateGamepad); } }

A classList.toggle() metódus a gomb megnyomása alapján hozzáadja vagy eltávolítja az aktív osztályt, ami kiváltja a CSS rétegstílusainkat. 5. lépés: Kezelje a billentyűzetes eseményeket Ezek az eseményfigyelők biztosítják a billentyűzet tartalék működését: // =================================== // BILLENTYŰZET ESEMÉNYKEZELŐI // ===================================

document.addEventListener("keydown", (e) => { if (kulcstérkép[e.kulcs]) { // Egy vagy több elem kezelése if (Array.isArray(keyMap[e.key])) { keyMap[e.key].forEach(el => el.classList.add("aktív")); } másik { keyMap[e.key].classList.add("aktív"); } status.textContent = "Billentyű lenyomva: " + e.key.toUpperCase(); } });

document.addEventListener("kulcsup", (e) => { if (kulcstérkép[e.kulcs]) { // Aktív állapot eltávolítása a kulcs elengedésekor if (Array.isArray(keyMap[e.key])) { keyMap[e.key].forEach(el => el.classList.remove("aktív")); } másik { keyMap[e.key].classList.remove("aktív"); } status.textContent = "Kulcs felszabadítva: " + e.key.toUpperCase(); } });

6. lépés: Adja hozzá a Start/Stop vezérlőt Végül szükségünk van egy módra a hibakereső be- és kikapcsolására: // =================================== // HIBAKERESŐ BE/KI // ===================================

document.getElementById("toggle").addEventListener("click", () => { futás = !futás; // A futó állapot megfordítása

if (futás) { status.textContent = "A hibakereső fut..."; updateGamepad(); // Indítsa el a frissítési ciklust } másik { status.textContent = "A hibakereső inaktív"; cancelAnimationFrame(rafId); // Állítsa le a hurkot } });

Szóval igen, nyomj meg egy gombot és világít. Nyomja meg a botot, és mozog. Ennyi. Még valami: nyers értékek. Néha csak számokat szeretne látni, nem fényeket.

Ebben a szakaszban látnia kell:

Egy egyszerű képernyő-vezérlő, Gombok, amelyek reagálnak, amikor kapcsolatba lép velük, és Opcionális hibakeresési kijelzés, amely a megnyomott gombok indexeit mutatja.

Hogy ez kevésbé legyen elvont, íme egy gyors bemutató a képernyőn megjelenő vezérlőről, amely valós időben reagál:

Most a Felvétel indítása gomb megnyomása mindent naplóz, amíg meg nem nyomja a Felvétel leállítása gombot. 2. Adatok exportálása CSV/JSON formátumba Ha megvan a napló, el akarjuk menteni.

1. lépés: A letöltési segéd létrehozása Először is szükségünk van egy segédfunkcióra, amely kezeli a fájlletöltéseket a böngészőben: // =================================== // FÁJLLETÖLTÉS SEGÍTŐ // ===================================

function downloadFile(fájlnév, tartalom, típus = "text/plain") { // Blob létrehozása a tartalomból const blob = new Blob([tartalom], { típus }); const url = URL.createObjectURL(blob);

// Hozzon létre egy ideiglenes letöltési hivatkozást, és kattintson rá const a = document.createElement("a"); a.href = url; a.download = fájlnév; a.click();

// Tisztítsa meg az objektum URL-címét a letöltés után setTimeout(() => URL.revokeObjectURL(url), 100); }

Ez a funkció úgy működik, hogy létrehoz egy Blobot (bináris nagy objektumot) az adatokból, létrehoz egy ideiglenes URL-t, és programozottan rákattint egy letöltési hivatkozásra. A tisztítás biztosítja, hogy ne szivárogtassunk memóriát. 2. lépés: Kezelje a JSON-exportálást A JSON tökéletes a teljes adatstruktúra megőrzésére:

// =================================== // EXPORTÁLÁS JSON-KÉNT // ===================================

document.getElementById("export-json").addEventListener("click", () => { // Ellenőrizze, hogy van-e exportálnivaló if (!frames.length) { console.warn("Nincs exportálható felvétel."); visszatérés; }

// Hozzon létre egy hasznos adatot metaadatokkal és keretekkel const payload = { CreatedAt: new Date().toISOString(), keretek };

// Letöltés formázott JSON-ként downloadFile( "gamepad-log.json", JSON.stringify(payload, null, 2), "alkalmazás/json" ); });

A JSON formátum mindent strukturált és könnyen értelmezhetővé tesz, így ideális a fejlesztői eszközökbe való visszatöltéshez vagy a csapattársakkal való megosztáshoz. 3. lépés: Kezelje a CSV-exportálást A CSV-exportáláshoz a hierarchikus adatokat sorokra és oszlopokra kell simítanunk:

//=================================== // EXPORTÁLÁS CSV-KÉNT // ===================================

document.getElementById("export-csv").addEventListener("click", () => { // Ellenőrizze, hogy van-e exportálnivaló if (!frames.length) { console.warn("Nincs exportálható felvétel."); visszatérés; }

// CSV-fejlécesor összeállítása (időbélyegző oszlopok, minden gomb, minden tengely) const headerButtons = frames[0].buttons.map((_, i) => btn${i}); const headerAxes = frames[0].axes.map((_, i) => tengely${i}); const header = ["t", ...headerButtons, ...headerAxes].join(",") + "\n";

// CSV-adatsorok létrehozása const rows = frames.map(f => { const btnVals = f.buttons.map(b => b.value); return [f.t, ...btnVals, ...f.axes].join(","); }).join("\n");

// Letöltés CSV-ként downloadFile("gamepad-log.csv", fejléc + sorok, "text/csv"); });

A CSV kiválóan alkalmas adatelemzésre, mert közvetlenül az Excelben vagy a Google Táblázatokban nyílik meg, lehetővé téve diagramok létrehozását, adatok szűrését vagy minták vizuális kiszűrését. Most, hogy az export gombok be vannak kapcsolva, két új lehetőség jelenik meg a panelen: JSON exportálása és CSV exportálása. A JSON jó megoldás, ha vissza akarja dobni a nyers naplót a fejlesztői eszközökbe, vagy körbe akarja bökni a szerkezetet. A CSV viszont közvetlenül az Excelben vagy a Google Táblázatokban nyílik meg, így diagramokat készíthet, szűrhet vagy összehasonlíthat bemeneteket. A következő ábra azt mutatja, hogy hogyan néz ki a panel ezekkel az extra vezérlőkkel.

3. Pillanatfelvétel rendszer Néha nincs szükség teljes felvételre, csak egy gyors „képernyőképre” a bemeneti állapotokról. Ebben segít a Pillanatfelvétel készítése gomb.

És a JavaScript:

// =================================== // PILLANATFELVÉTEL KÉSZÍTÉSE // ===================================

document.getElementById("pillanatkép").addEventListener("click", () => { // Szerezze be az összes csatlakoztatott játékvezérlőt const pads = navigator.getGamepads(); const activePads = [];

// Lépjen végig és rögzítse az egyes csatlakoztatott játékvezérlők állapotát for (const gp of pads) { if (!gp) folytatódik; // Üres helyek kihagyása

activePads.push({ id: gp.id, // Vezérlő neve/modell időbélyeg: performance.now(), gombok: gp.buttons.map(b => ({ lenyomva: b.nyomva, érték: b.érték })) tengelyek: [...gp.axes] }); }

// Ellenőrizze, hogy találtak-e játékvezérlőket if (!activePads.length) { console.warn("Nincs játékvezérlő csatlakoztatva pillanatfelvételhez."); alert("Nem észlelhető vezérlő!"); visszatérés; }

// Jelentkezzen be és értesítse a felhasználót console.log("Pillanatkép:", activePads); alert(Pillanatfelvétel készült! Rögzített ${activePads.length} vezérlő(k).); });

A pillanatképek rögzítik a vezérlő pontos állapotát egy adott pillanatban. 4. Ghost Input Replay Most pedig jöjjön a móka: szellembemeneti visszajátszás. Ez vesz egy naplót, és vizuálisan játssza le, mintha egy fantomlejátszó használná a kontrollert.

JavaScript újrajátszáshoz: // =================================== // SZELLEM REPLAY // ===================================

document.getElementById("replay").addEventListener("click", () => { // Győződjön meg arról, hogy van egy újrajátszható felvételünk if (!frames.length) { alert("Nincs felvétel az újrajátszáshoz!"); visszatérés; }

console.log("Ghost replay indítása...");

// Szám időzítése szinkronizált lejátszáshoz legyen startTime = performance.now(); legyen frameIndex = 0;

// Animációs ciklus újrajátszása function step() { const most = teljesítmény.most(); const eltelt = most - startTime;

// Feldolgozza az összes olyan keretet, amelynek már meg kellett volna történnie while (frameIndex < frames.length && frames[frameIndex].t <= eltelt) { const frame = keretek[frameIndex];

// Frissítse a felhasználói felületet a rögzített gombállapotokkal btnA.classList.toggle("aktív", frame.buttons[0].pressed); btnB.classList.toggle("aktív", frame.buttons[1].pressed); btnX.classList.toggle("aktív", frame.buttons[2].pressed);

// Állapotkijelző frissítése let pressed = []; frame.buttons.forEach((btn, i) => { if (btn.pressed) pressed.push("Button " + i); }); if (pressed.length > 0) { status.textContent = "Ghost: " + pressed.join(", "); }

frameIndex++; }

// Folytassa a ciklust, ha több keret van if (frameIndex < frames.length) { requestAnimationFrame(lépés); } másik { console.log("Replaykész."); status.textContent = "Az újrajátszás kész"; } }

// Indítsa el az újrajátszást step(); });

A hibakeresés gyakorlatiasabbá tétele érdekében hozzáadtam egy szellemvisszajátszást. Miután felvett egy munkamenetet, megnyomhatja a visszajátszást, és megnézheti, ahogy a felhasználói felület eljátssza, mintha egy fantomlejátszó futná a padot. Ehhez egy új Replay Ghost gomb jelenik meg a panelen.

Nyomja meg a Felvétel gombot, szórakozzon egy kicsit a kontrollerrel, álljon meg, majd játssza le újra. A felhasználói felület csak visszhangzik mindent, amit tett, mint egy szellem, amely követi a bemeneteit. Minek foglalkozni ezekkel az extrákkal?

A rögzítés/exportálás megkönnyíti a tesztelők számára, hogy pontosan megmutassák, mi történt. A pillanatképek egy pillanatra lefagynak, ami rendkívül hasznos, ha furcsa hibákat üldöz. A szellemvisszajátszás nagyszerűen használható oktatóanyagokhoz, kisegítő lehetőségek ellenőrzéséhez, vagy csak a vezérlőbeállítások egymás melletti összehasonlításához.

Ezen a ponton már nem csak egy ügyes demóról van szó, hanem valamiről, amit ténylegesen is be lehet állítani. Valós használati esetek Most megvan ez a hibakereső, amely sok mindenre képes. Megmutatja az élő bevitelt, rögzíti a naplókat, exportálja azokat, és még visszajátsza is a dolgokat. De az igazi kérdés az: valójában kit érdekel? Kinek hasznos ez? Játékfejlesztők A vezérlők a munka részét képezik, de hibakeresésük? Általában fájdalom. Képzeld el, hogy egy verekedős játék kombót tesztelsz, például a ↓ → + ütést. Ahelyett, hogy imádkoztál volna, kétszer ugyanúgy megnyomtad, egyszer felveszed, és visszajátszod. Kész. Vagy cserélje ki a JSON-naplókat egy csapattársával, hogy ellenőrizze, hogy a többjátékos kódja ugyanúgy reagál-e az ő gépükön. Ez óriási. Kisegítő lehetőségeket gyakorló szakemberek Ez közel áll a szívemhez. Nem mindenki játszik „standard” kontrollerrel. Az adaptív vezérlők néha furcsa jeleket adnak ki. Ezzel az eszközzel pontosan láthatja, mi történik. Tanárok, kutatók, bárki. Megragadhatják a naplókat, összehasonlíthatják őket, vagy egymás mellett lejátszhatják a bemeneteket. Hirtelen a láthatatlan dolgok nyilvánvalóvá válnak. Minőségbiztosítási tesztelés A tesztelők általában olyan megjegyzéseket írnak, mint: „Ide nyomtam a gombokat, és elromlott”. Nem túl hasznos. Most? Pontosan rögzíthetik a préseket, exportálhatják a naplót és elküldhetik. Nincs találgatás. Pedagógusok Ha oktatóanyagokat vagy YouTube-videókat készítesz, a szellemvisszajátszás arany. Szó szerint azt mondhatja: „Íme, mit csináltam a vezérlővel”, miközben a felhasználói felület ezt mutatja. Világosabbá teszi a magyarázatokat. A játékokon túl És igen, ez nem csak a játékokról szól. Az emberek vezérlőket használtak robotokhoz, művészeti projektekhez és kisegítő felületekhez. Minden alkalommal ugyanaz a probléma: valójában mit lát a böngésző? Ezzel nem kell találgatni. Következtetés A vezérlő bemenetének hibakeresése mindig is vakon repülésnek tűnt. A DOM-mal vagy a CSS-sel ellentétben nincs beépített ellenőr a játékvezérlőkhöz; csak nyers számok vannak a konzolban, könnyen elvesznek a zajban. Néhány száz soros HTML, CSS és JavaScript használatával valami mást építettünk fel:

Vizuális hibakereső, amely láthatóvá teszi a láthatatlan bemeneteket. Réteges CSS-rendszer, amely tisztán és hibakereshetően tartja a felhasználói felületet. Fejlesztések készlete (rögzítés, exportálás, pillanatképek, szellemvisszajátszás), amelyek demóból fejlesztői eszközzé emelik.

Ez a projekt megmutatja, milyen messzire juthat el, ha a Web Platform erejét egy kis kreativitással keveri a CSS Cascade Layersben. Az imént ismertetett eszköz teljes egészében nyílt forráskódú. Klónozhatja a GitHub repót, és kipróbálhatja saját maga. De ami még fontosabb, saját kezűvé teheted. Adja hozzá saját rétegeit. Építse fel saját visszajátszási logikáját. Integrálja a játék prototípusával. Vagy akár olyan módon is használhatom, amit el sem képzeltem. Tanításhoz, akadálymentesítéshez vagy adatelemzéshez. A nap végén ez nem csak a játékvezérlők hibakereséséről szól. Arról van szó, hogy fényt derítsünk a rejtett bemenetekre, és önbizalmat adjunk a fejlesztőknek, hogy olyan hardverekkel dolgozzanak, amelyeket a web még mindig nem ölel fel teljesen. Tehát csatlakoztassa a vezérlőt, nyissa meg a szerkesztőt, és kezdje el a kísérletezést. Meglepődhet azon, hogy böngészője és CSS-je valójában mire képes.

You May Also Like

Enjoyed This Article?

Get weekly tips on growing your audience and monetizing your content — straight to your inbox.

No spam. Join 138,000+ creators. Unsubscribe anytime.

Create Your Free Bio Page

Join 138,000+ creators on Seemless.

Get Started Free