Lekcia 10
Milan Herda, 05/2020
Video k tejto lekcii:
Skúste Karla rozpohybovať pomocou klávesnice.
Zistite si (google, MDN), ako reagovať na stlačenie klávesy a zabezpečte, aby fungovali štyri klávesy (obdobne, ako naše štyri tlačidlá):
Vygooglime ako v JS reagovať na klávesnicu:
Dostaneme sa tak napríklad sem:
https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
alebo sem:
Vygooglime ako v JS reagovať na klávesnicu:
Zistíme, že stlačenie klávesy (podobne ako kliknutie na myši) v prehliadači vyvolá event.
A rovnako, ako vieme v JS reagovať na eventy spôsobené myšou
element.addEventListener('click', function (event) {
tak vieme reagovať aj na eventy vyvolané klávesnicou
Informácie o stlačenej klávese nájdeme v objekte event
Objekt event, ktorý prehliadač odovzdá do vášho listenera obsahuje property
Tu zistíte, ako tieto kódy vyzerajú pre klávesy, ktorými chcete ovládať Karla: https://developer.mozilla.org/en-US/docs/Web/API/Document/keydown_event
Musíme sa rozhodnúť, na aký element zavesiť náš listener.
Najlepšie bude priamo na document. Používateľovi tak bude hneď všetko fungovať a nemusí predtým nikam klikať, ani sa presúvať myšou.
document.addEventListener('keydown', function (event) {
switch (event.keyCode) {
case 38:
karel.krok();
break;
case 39:
karel.vpravoVbok();
break;
case 37:
karel.vlavoVbok();
break;
case 32:
karel.zmenZnacku();
break;
}
});
Pracujeme v súbore index.html
document.addEventListener('keydown', function (event) {
switch (event.code) {
case 'ArrowUp':
karel.krok();
break;
case 'ArrowRight':
karel.vpravoVbok();
break;
case 'ArrowLeft':
karel.vlavoVbok();
break;
case 'Space':
karel.zmenZnacku();
break;
}
});
Pracujeme v súbore index.html
Pri programovaní objektov karel a mapa sme sa snažili mať od seba oddelené dáta (premenné a metódy v js/karel.js) od ich vizuálnej reprezentácie (mapa a info o karlovi na index.html)
Toto oddelenie nie je 100%, lebo používame funkcie krokDopredu, otocDolava a pod., ktoré priamo a bez našej kontroly kreslia do mapy.
Urobíme si vlastné kreslenie do mapy, prestaneme používať funkcie krokDopredu, otocDolava a vytvoríme si tak nášho úplne vlastného Karla.
Pôvodného Karla a mapu kreslím ako SVG obrázok
SVG sme nepreberali, takže musíme použiť prostriedky HTML
A na toto sa nám hodí tabuľka.
<table>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
Toto je tabuľka o dvoch riadkoch a dvoch stĺpcoch.
Karlova mapa bude tabuľka o 20 riadkoch a 20 stĺpcoch
Vyskreslime ju nad tlačidlá.
<div class="column">
<div id="root"></div>
</div>
<div class="column">
<table>
<tr>
<td></td>
<!-- dalších 19 rovnakých riadkov -->
</tr>
<!-- ďalších 19 opakovaní tr a v nich 19-krát td -->
<!-- jop, to je niečo cez 400 riadkov -->
</table>
<section>
<div class="buttons has-addons">
Takto by sme sa upísali k smrti.
Ako správni programátori si tabuľku vygenerujeme v kóde pomocou cyklu.
Pracujeme v súbore index.html
<div class="column">
<div id="root"></div>
</div>
<div class="column">
<div id="map-table"></div>
<section>
<div class="buttons has-addons">
<!-- atď -->
<script>
function vygenerujMapu () {
let tabulka = '<table>';
for (let cisloRiadku = 0; cisloRiadku < mapa.vyska; cisloRiadku = cisloRiadku + 1) {
tabulka = tabulka + vygenerujRiadok(cisloRiadku);
}
tabulka = '</table>';
return tabulka;
};
function nakresliMapu() {
const tabulka = vygenerujMapu();
document.querySelector('#map-table').innerHTML = tabulka;
};
nakresliMapu();
</script>
Pracujeme v súbore index.html
<script>
function vygenerujRiadok(cisloRiadku) {
let riadokTabulky = '<tr>';
for (let cisloStlpca = 0; cisloStlpca < mapa.sirka; cisloStlpca = cisloStlpca + 1) {
riadokTabulky = riadokTabulky + vygenerujBunku(cisloRiadku, cisloStlpca);
}
riadokTabulky = riadokTabulky + '</tr>';
return riadokTabulky;
};
function vygenerujMapu() {
// ...
};
function nakresliMapu() {
// ...
};
nakresliMapu();
</script>
Pracujeme v súbore index.html
<script>
function vygenerujBunku(cisloRiadku, cisloStlpca) {
return '<td></td>';
};
function vygenerujRiadok(cisloRiadku) {
// ...
};
function vygenerujMapu() {
// ...
};
function nakresliMapu() {
// ...
};
nakresliMapu();
</script>
Pracujeme v súbore index.html
Tabuľku nevidíme, lebo jej bunky nemajú žiaden obsah.
Problém obídeme nastavením šírky a výšky každej bunky cez CSS
<style>
td {
border: solid 1px black;
width: 20px;
height: 20px;
font-size: 12px;
}
</style>
Pracujeme v súbore index.html
Do bunky, kde sa Karel nachádza môžeme vložiť obrázok (pomocou tagu <img>)
Budeme potrebovať štyri obrázky (pre každý smer) alebo jeden, ktorý budeme otáčať cez CSS.
Ja to urobím jednoduchšie a Karla nebudem kresliť, ale písať :)
Tieto znaky budú reprezentovať Karla a jeho natočenie:
Kľudne použite emoji
<script>
function vygenerujKarla() {
let znak = '^';
if (karel.smer === VYCHOD) {
znak = '>';
} else if (karel.smer === ZAPAD) {
znak = '<';
} else if (karel.smer === JUH) {
znak = 'V';
}
return '<span class="karel">' + znak + '</span>';
};
function vygenerujBunku(cisloRiadku, cisloStlpca) {
// ...
};
function vygenerujRiadok(cisloRiadku) {
// ...
};
function vygenerujMapu() {
// ...
};
function nakresliMapu() {
// ...
};
nakresliMapu();
</script>
Pracujeme v súbore index.html
<style>
td {
border: solid 1px black;
width: 20px;
height: 20px;
font-size: 12px;
}
span.karel {
color: red;
font-weight: bold;
}
</style>
Dáme Karlovi štýl
Pracujeme v súbore index.html
<script>
function vygenerujKarla() {
// ...
};
function vygenerujBunku(cisloRiadku, cisloStlpca) {
let bunka = '<td>';
if ((karel.riadok === cisloRiadku) && (karel.stlpec === cisloStlpca)) {
bunka = bunka + vygenerujKarla();
}
bunka = bunka + '</td>';
return bunka;
};
function vygenerujRiadok(cisloRiadku) {
// ...
};
function vygenerujMapu() {
// ...
};
function nakresliMapu() {
// ...
};
nakresliMapu();
</script>
Pracujeme v súbore index.html
Hmm, prečo sa nehýbe?
Lebo sme mapu dali vykresliť iba raz.
Po každej zmene ju musíme prekresliť.
karel.poZmeneSmeru = function (aktualnySmer) {
document.querySelector('#direction').innerHTML = aktualnySmer;
nakresliMapu();
};
karel.poZmenePozicie = function (novyRiadok, novyStlpec) {
document.querySelector('#row').innerHTML = novyRiadok;
document.querySelector('#col').innerHTML = novyStlpec;
nakresliMapu();
};
Pracujeme v súbore index.html
Opäť môžeme buď do bunky vykreslovať obrázok...
Alebo jej jednoducho nastavíme inú farbu pozadia
Pracujeme v súbore index.html
<style>
td {
border: solid 1px black;
width: 20px;
height: 20px;
font-size: 12px;
}
td.znacka {
background-color: black;
}
span.karel {
color: red;
font-weight: bold;
}
</style>
<script>
function vygenerujKarla() {
// ...
};
function vygenerujBunku(cisloRiadku, cisloStlpca) {
let bunka = '<td>';
if (mapa.bunky[cisloStlpca][cisloRiadku].jeOznacene) {
bunka = '<td class="znacka">';
}
if ((karel.riadok === cisloRiadku) && (karel.stlpec === cisloStlpca)) {
bunka = bunka + vygenerujKarla();
}
bunka = bunka + '</td>';
return bunka;
};
function vygenerujRiadok(cisloRiadku) {
// ...
};
function vygenerujMapu() {
// ...
};
function nakresliMapu() {
// ...
};
nakresliMapu();
</script>
Pracujeme v súbore index.html
Prečo sa uloženie značky neprejaví okamžite, ale až po pohybe?
Lebo objekt karel nám nedal možnosť reagovať na uloženie/zdvihnutie značky.
Pracujeme v súbore js/karel.js
const karel = {
smer: SEVER,
riadok: 19,
stlpec: 0,
poZmeneSmeru: null,
poZmenePozicie: null,
poZmeneZnacky: null,
// ...
zmenZnacku: function () {
if (stojisNaZnacke()) {
zdvihniZnacku();
mapa.zrusOznacenie(this.stlpec, this.riadok);
} else {
polozZnacku();
mapa.oznacBunku(this.stlpec, this.riadok);
}
this.upravInformaciuOZnacke();
},
upravInformaciuOZnacke: function () {
if (this.poZmeneZnacky) {
this.poZmeneZnacky();
}
}
};
Pracujeme v súbore index.html
karel.poZmenePozicie = function (novyRiadok, novyStlpec) {
document.querySelector('#row').innerHTML = novyRiadok;
document.querySelector('#col').innerHTML = novyStlpec;
nakresliMapu();
};
karel.poZmeneZnacky = function () {
nakresliMapu();
};
V tejto chvíli už máme funkčného Karla s kreslením do našej vlastnej mapy.
Môžeme pristúpiť k tomu, aby sme odobrali pôvodne používané funkcie (krokDopredu, otocDolava, stojisNaZnacke...) a spoliehali sa už iba na naše.
...potrebujeme napísať náhradu za tie, ktoré sme ešte nenapísali:
Ale najskôr...
Urobte vašu vlastnú implementáciu týchto funkcií. Nech sú to metódy objektu karel.
Pracujeme v súbore js/karel.js
const karel = {
// ...
stojisNaZnacke: function () {
if (mapa.bunky[this.stlpec][this.riadok].jeOznacene) {
return true;
}
return false;
},
jePredTebouStena: function () {
if ((this.smer === SEVER) && (this.riadok === 0)) {
return true;
}
if ((this.smer === VYCHOD) && (this.stlpec === 19)) {
return true;
}
if ((this.smer === JUH) && (this.riadok === 19)) {
return true;
}
if ((this.smer === ZAPAD) && (this.stlpec === 0)) {
return true;
}
return false;
}
};
Pracujeme v súbore js/karel.js
const karel = {
// ...
stojisNaZnacke: function () {
if (mapa.bunky[this.stlpec][this.riadok].jeOznacene) {
return true;
}
return false;
},
jePredTebouStena: function () {
if ((this.smer === SEVER) && (this.riadok === 0)) {
return true;
}
if ((this.smer === VYCHOD) && (this.stlpec === 19)) {
return true;
}
if ((this.smer === JUH) && (this.riadok === 19)) {
return true;
}
if ((this.smer === ZAPAD) && (this.stlpec === 0)) {
return true;
}
return false;
}
};
Toto tu (riadok 5) je hrozne škaredé a odhaľuje to internú štruktúru objektu mapa.
Pracujeme v súbore js/karel.js
const mapa = {
// ...
jeZnackaNaPozicii: function (cisloRiadku, cisloStlpca) {
return this.bunky[cisloStlpca][cisloRiadku].jeOznacene;
}
};
const karel = {
// ...
stojisNaZnacke: function () {
if (mapa.jeZnackaNaPozicii(this.riadok, this.stlpec)) {
return true;
}
return false;
},
jePredTebouStena: function () {
// ...
}
};
Pracujeme v súbore index.html
const vygenerujBunku = function (cisloRiadku, cisloStlpca) {
let bunka = '<td>';
if (mapa.jeZnackaNaPozicii(cisloRiadku, cisloStlpca)) {
bunka = '<td class="znacka">';
}
if (karel.riadok === cisloRiadku && karel.stlpec === cisloStlpca) {
bunka = bunka + vygenerujKarla();
}
bunka = bunka + '</td>';
return bunka;
};
Teraz už môžeme z objektu karel vyhodiť pôvodne používané funkcie na manipuláciu s Karlom
const karel = {
// ...
vpravoVbok: function () {
//otocDoprava(); <-- môžeme zmazať
this.smer = akyJeDalsiSmerVpravo(this.smer);
this.upravInformaciuOSmere();
},
vlavoVbok: function () {
//otocDolava(); <-- môžeme zmazať
this.smer = akyJeDalsiSmerVlavo(this.smer);
this.upravInformaciuOSmere();
},
krok: function () {
if (!this.jePredTebouStena()) { // <-- nahradili sme
//krokDopredu(); <-- môžeme zmazať
this.zmenPoziciu();
this.upravInformaciuOPozicii();
}
},
zmenZnacku: function () {
if (this.stojisNaZnacke()) { // <-- nahradili sme
//zdvihniZnacku(); <-- môžeme zmazať
mapa.zrusOznacenie(this.stlpec, this.riadok);
} else {
//polozZnacku(); <-- môžeme zmazať
mapa.oznacBunku(this.stlpec, this.riadok);
}
this.upravInformaciuOZnacke();
},
};
Pracujeme v súbore js/karel.js
Teraz sa nám zmeny prejavujú už iba na našej mape.
Z index.html môžeme odstrániť pôvodnú mapu a ponecháme si iba našu vlastnú.
Odstránime aj pôvodné JS knižnice (tri script tagy na konci súboru) a ponecháme si iba svoje.
<div class="container" id="main-container">
<div class="columns">
<div class="column">
<section id="table"></section>
<!-- nahradili sme pôvodnú mapu tou našou -->
</div>
<div class="column">
<section>
<div class="buttons has-addons">
<!-- ... -->
<!-- tieto tri script tagy na konci súboru zmažte -->
<script>
! function(l) {
// ...
}([])
</script>
<script src="./static/js/1.b368cc4a.chunk.js"></script>
<script src="./static/js/main.18ec5a2d.chunk.js"></script>
</body>
</html>
Pracujeme v súbore index.html
Tento Karel je už iba váš.