Pavol Hejný
pavolhejny.com
Webové aplikace v
JavaScriptu
27.5.2016 | Praha | ITnetwork.cz
Stránka vs. Aplikace
WWW App
API
server DBDB
View
Controller
WWW
server
Model
HTML
page
Frontend
v JS
Proč?
Rychlost
využití 2D / 3D grafiky
Využití web audio
Využití files API
http://birds.cz/avif/atlas_nest_map.php?rok=2016&druh=Phasianus_colchicus
http://zastavky.birdlife.cz/
Towns.cz
Towns.cz
develop.towns.cz
develop.towns.cz
develop.towns.cz
develop.towns.cz
develop.towns.cz
JS
a
HTML
<body>
<p id="itnetwork">ITnetwork</p>
</body>
<script>
var element = window.document.getElementById
("itnetwork");
element.onclick = function(){
element.style.color = '#3B94E0';
}
</script>
DOM
ITnetwork
DOM vs. jQuery
$('#itnetwork').click(function(){
$(this).css('color','#00ff00');
});
var element = window.document.getElementById
("itnetwork");
element.onclick = function(){
element.style.color = '#00ff00';
}
7
var second=11;
var element =window.document.getElementById
("counter");
var interval = setInterval(function(){
second--;
element.innerHTML = second;
}, 1000);
setTimeout(function(){
alert('Ahoj!');
clearInterval(interval);
}
, 11000);
interval, timeout
https://jsfiddle.net/phejny/xjyerkq2/
var start = null;
var element = document.getElementById("ball");
element.style.position = 'absolute';
function step(timestamp) {
if (!start) start = timestamp;
var progress = timestamp - start;
element.style.top = Math.sin(progress/1000*Math.PI)
*100+100 + "px";
element.style.left = Math.cos(progress/1000*Math.PI)
*100+100 + "px";
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
Animation frame
https://jsfiddle.net/phejny/8nLcpffb/1/
5€ je 135,- kč.
var request = new XMLHttpRequest();
var url = "https://api.fixer.io/latest?symbols=CZK";
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200)
{
var response = JSON.parse(request.responseText);
var rate = response.rates.CZK;
document.getElementById('CZK').innerText = Math.
round(rate*5);
}
};
request.open("GET", url, true);
request.send();
XMLHttpRequest
https://jsfiddle.net/phejny/0y6dbyug/1
XMLHttpRequest vs. jQuery
var url = "https://api.fixer.io/latest?symbols=CZK";
$.get(url)
.done(function(response){
var rate = response.rates.CZK;
$('#CZK').text(Math.round(rate*5));
});
var request = new XMLHttpRequest();
var url = "https://api.fixer.io/latest?symbols=CZK";
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
var response = JSON.parse(request.responseText);
var rate = response.rates.CZK;
document.getElementById('CZK').innerText = Math.round
(rate*5);
}
};
request.open("GET", url, true);
request.send();
https://jsfiddle.net/phejny/0y6dbyug/3
Sync vs. Async
$.get("https://api.fixer.io/latest?symbols=CZK")
.done(function(response){
var rate = response.rates.CZK;
$('#CZK').text(Math.round(rate*5));
});
$.get("https://api.fixer.io/latest?symbols=USD")
.done(function(response){
var rate = response.rates.USD;
$('#USD').text(Math.round(rate*5*100)/100);
});
<?php
$json = file_get_contents("https://api.fixer.io/latest?
symbols=CZK,EUR");
//...
echo('5€ je '.$CZK.',- kč.');
$json = file_get_contents("https://api.fixer.io/latest?
symbols=USD,EUR");
//...
echo('5€ je '.$USD.'$.');
?>
Sync vs. Async
Event
Event
Výsledek
Výsledek
Angular
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5
/angular.min.js"></script>
</head>
<body>
<div>
<input type="text" ng-model="jmeno_cajovny">
<hr>
<h1>Jsme v čajovně {{jmeno_cajovny}}!</h1>
</div>
</body>
</html>
ECMAScript 5
var Position = function(x,y){
this.x= x || 0;
this.y= y || 0;
};
Position.prototype.getDistance = function(position){
return Math.sqrt(Math.pow(position.x-this.x,2)+Math.pow(position.y-
this.y,2));
};
Position.prototype.toString = function(){
return ''+this.x+','+this.y+'';
};
var multiline_string = (function () {/*
Podle
mě
ten nejhorší
způsob :(
*/}).toString().match(/[^]*/*([^]*)*/}$/)[1];
var multiline_string = `V ES6
to jde
bez problémů!`;
var multiline_string = 'Takhle
d*****e
se musí psát
string na více
řádků.';
var multiline_string = 'Nebo' +
'takhle - s tímhle zápisem alespoň umí' +
'pracovat PHPStorm';
var multiline_string =
['Nebo',
'takhle',
].join('n')
String
ECMAScript 6
var Position = class{
constructor(x=0,y=0){
this.x= x;
this.y= y;
}
getDistance(position){
return Math.sqrt(Math.pow(position.x-this.x,2)+Math.pow(position.
y-this.y,2));
}
toString(){
return ''+this.x+','+this.y+'';
}
};
CoffeeScript
transpiler
Position = (x, y) ->
@x = x or 0
@y = y or 0
return
Position::getDistance = (position) ->
Math.sqrt (position.x - (@x)) ** 2+ (position.y - (@y)) ** 2
Position::toString = ->
'' + @x + ',' + @y + ''
Backend
Proč?
Node JS
Přepínání
Rychlost
Sdílení kódu
REST API
používání WS
PHP vs. Node
● Apache
● Jednotlivé stránky
● node, pm2
● Celý server
Response
● http://www.itnetwork.
cz/software/sublime-text?
nastaveni=123#goto173043
● GET, POST, DELETE, HEAD,...
Body
● status code - 200, 404, 403,...
● mime type - text/html
● cache control
● form data, upload,... ● html, js, css, image, pdf,
video,...
Request
Header
Response Headers
alt-svc:quic=":443"; ma=2592000; v="
34,33,32,31,30,29,28,27,26,25"
alternate-protocol:443:quic
cache-control:private, max-age=0
content-encoding:gzip
content-type:text/html; charset=UTF-8
date:Fri, 27 May 2016 11:46:06 GMT
expires:-1
server:gws
status:200
x-frame-options:SAMEORIGIN
x-xss-protection:1; mode=block
Request Headers
URL:https://www.google.cz/search?
q=itnetwork+je+nej&oq=status+code&aqs=ch
rome..69i57.
395j0j7&sourceid=chrome&es_sm=93&ie=UT
F-8
Request Method:GET
Remote Address:216.58.212.67:443
RAW
Browser vs. Node
● window
● <script src="..."></script>
● HTTP klient
● JQuery, Angular
● global
● var module = require("...");
● HTTP server, klient
● Express
Express
Routing
var http = require("http");
http.createServer(function(request, response){
if(request.url=='/') {
response.writeHead(200);
response.end('GET request to the home');
}else
if(request.url=='/objects') {
response.writeHead(200);
response.end('GET request to the objects');
}else{
response.writeHead(404);
response.end('Not fount :( ');
}
}).listen(80);
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('GET request to the home');
});
app.get('/objects', function (req, res) {
res.send('GET request to the objects');
});
Jasmine
testování
describe('lineCollision', function() {
it('//', function () {
expect(T.Math.lineCollision(0,0,10,10,0,2,10,12)).toBe(false);
});
it('/|', function () {
expect(T.Math.lineCollision(0,0,10,10,100,0,100,10)).toBe(false);
});
it('X', function () {
expect(T.Math.lineCollision(0,0,10,10,0,2,2,0)).toBe(true);
});
it('L', function () {
expect(T.Math.lineCollision(0,0,10,10,10,10,10,0)).toBe(true);
});
it('T', function () {
expect(T.Math.lineCollision(0,0,10,0,10,10,10,-10)).toBe(true);
});
it('/', function () {
expect(T.Math.lineCollision(0,0,10,10,1,1,9,9)).toBe(true);
});
it('!', function () {
expect(T.Math.lineCollision(0,0,0,2,0,4,0,10)).toBe(false);
});
});
Middleware
načítání js souborů
HTML
browser includes
<script src="/app/js/00-other/cookie.static.js"></script>
<script src="/app/js/00-other/copy.static.js"></script>
<script src="/app/js/00-other/date.functions.js"></script>
<script src="/app/js/00-other/escape.functions.js"></script>
<script src="/app/js/00-other/generate-file.jquery.js"></script>
<script src="/app/js/00-other/interval.static.js"></script>
<script src="/app/js/00-other/is-image-ok.function.js"></script>
<script src="/app/js/00-other/is.functions.js"></script>
<script src="/app/js/00-other/log.functions.js"></script>
<script src="/app/js/00-other/outer-html.jquery.js"></script>
<script src="/app/js/00-other/select-text.function.js"></script>
<script src="/app/js/00-other/set-input-error.function.js"></script>
<script src="/app/js/00-other/string.class.js"></script>
<script src="/app/js/00-other/track-event.function.js"></script>
<script src="/app/js/00-other/uri-plugin.js"></script>
<script src="/app/js/00-other/validate.functions.js"></script>
<script src="/app/js/10-model/10-model-draw-3d.prototype.js"></script>
<script src="/app/js/10-model/10-model-draw-cache.prototype.js"></script>
<script src="/app/js/20-ui/10-browser-compatibility.static.js"></script>
<script src="/app/js/20-ui/10-messages.static.js"></script>
<script src="/app/js/20-ui/10-popup-window.static.js"></script>
<script src="/app/js/20-ui/10-status.static.js"></script>
<script src="/app/js/20-ui/10-templates.static.js"></script>
<script src="/app/js/20-uri/10-uri.static.js"></script>
<script src="/app/js/20-uri/20-uri.onload.js"></script>
<script src="/app/js/30-api/10-townsapi.class.js"></script>
GULP
build
<script src="/app-build/js/towns.min.js"></script>
//--------------------------------------------------------------------------------------------------
gulp.task('build', function () {
gulp.src(includes)
.pipe(sort())
.pipe(concat('towns-shared.js'))
.pipe(es6transpiler({
"disallowUnknownReferences": false,
"disallowDuplicated": false
}))
.pipe(gulp.dest('./build'))
.pipe(uglify())
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('./build'));
});
PHP
browser includes
<?php
$scripts = glob('./js/*.js');
foreach($scripts as $script){
echo('<script src="'.htmlentities($script).'"></script>'."n");
}
?>
Middleware
komunikace WWW a API serveru
http://www.itnetwork.cz/software/sublime-
text
Sublime Text 3 napsal Jon Skinner. Jde o komerční editor napsaný v C++ a jehož
licence stojí kolem 1400 Kč. Editor si ale můžete vyzkoušet a to na neomezenou
dobu. Bez licence se však čas od času při ukládání ukáže dialogové okno, kde je
doporučena koupě licence….
Honza Bittner | ITNetwork.cz
WWW App
API
server DB
WWW
server
WWW App
API
server DB
WWW
server
http://www.towns.
cz/story/572cbec9b8
3bf0b7710c6b58#23,
56
<title>Lesní mýtina | Towns</title>
<meta name="description" content="">
<link rel="icon" href="/favicon.ico">
<meta property="fb:app_id" content="408791555870621" >
<meta property="og:site_name" content="Lesní mýtina | Towns" >
<meta property="og:title" content="Lesní mýtina | Towns" >
<meta property="og:description" content="Na lesní mýtině…." >
<meta property="og:type" content="article" >
<meta property="og:url" content="http://towns.
cz/story/572cbec9b83bf0b7710c6b58" >
<meta property="og:image" content="http://cdn.towns.cz/towns-cdn/?
file=572cbec84cc89-bGVzMi5qcGc=&&width=1200" >
………………………………………………………………………………...
<main class="popup-window">
<div class="header">Lesní mýtina</div>
<article class="content">
<p><img src="http://cdn.towns.cz/towns-cdn/?file=572cbec84cc89-
bGVzMi5qcGc=&amp;&amp;width=800" alt="les2.jpg"></p>
</article>
</main>
Další témata
● TypeScript
● Angular
● Google Maps vs. Seznam Mapy
● WS
● REST API
● Mongo DB
● JS Files API - drag & drop
● 2D grafika - canvas / svg
● 3D grafika webgl
● Procedurální mapy
● Gulp
● Sass

Webové aplikace v JavaScriptu

  • 1.
    Pavol Hejný pavolhejny.com Webové aplikacev JavaScriptu 27.5.2016 | Praha | ITnetwork.cz
  • 2.
    Stránka vs. Aplikace WWWApp API server DBDB View Controller WWW server Model HTML page
  • 3.
  • 4.
    Proč? Rychlost využití 2D /3D grafiky Využití web audio Využití files API
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 16.
  • 17.
    <body> <p id="itnetwork">ITnetwork</p> </body> <script> var element= window.document.getElementById ("itnetwork"); element.onclick = function(){ element.style.color = '#3B94E0'; } </script> DOM ITnetwork
  • 18.
    DOM vs. jQuery $('#itnetwork').click(function(){ $(this).css('color','#00ff00'); }); varelement = window.document.getElementById ("itnetwork"); element.onclick = function(){ element.style.color = '#00ff00'; }
  • 19.
    7 var second=11; var element=window.document.getElementById ("counter"); var interval = setInterval(function(){ second--; element.innerHTML = second; }, 1000); setTimeout(function(){ alert('Ahoj!'); clearInterval(interval); } , 11000); interval, timeout https://jsfiddle.net/phejny/xjyerkq2/
  • 20.
    var start =null; var element = document.getElementById("ball"); element.style.position = 'absolute'; function step(timestamp) { if (!start) start = timestamp; var progress = timestamp - start; element.style.top = Math.sin(progress/1000*Math.PI) *100+100 + "px"; element.style.left = Math.cos(progress/1000*Math.PI) *100+100 + "px"; window.requestAnimationFrame(step); } window.requestAnimationFrame(step); Animation frame https://jsfiddle.net/phejny/8nLcpffb/1/
  • 21.
    5€ je 135,-kč. var request = new XMLHttpRequest(); var url = "https://api.fixer.io/latest?symbols=CZK"; request.onreadystatechange = function() { if (request.readyState == 4 && request.status == 200) { var response = JSON.parse(request.responseText); var rate = response.rates.CZK; document.getElementById('CZK').innerText = Math. round(rate*5); } }; request.open("GET", url, true); request.send(); XMLHttpRequest https://jsfiddle.net/phejny/0y6dbyug/1
  • 22.
    XMLHttpRequest vs. jQuery varurl = "https://api.fixer.io/latest?symbols=CZK"; $.get(url) .done(function(response){ var rate = response.rates.CZK; $('#CZK').text(Math.round(rate*5)); }); var request = new XMLHttpRequest(); var url = "https://api.fixer.io/latest?symbols=CZK"; request.onreadystatechange = function() { if (request.readyState == 4 && request.status == 200) { var response = JSON.parse(request.responseText); var rate = response.rates.CZK; document.getElementById('CZK').innerText = Math.round (rate*5); } }; request.open("GET", url, true); request.send(); https://jsfiddle.net/phejny/0y6dbyug/3
  • 23.
    Sync vs. Async $.get("https://api.fixer.io/latest?symbols=CZK") .done(function(response){ varrate = response.rates.CZK; $('#CZK').text(Math.round(rate*5)); }); $.get("https://api.fixer.io/latest?symbols=USD") .done(function(response){ var rate = response.rates.USD; $('#USD').text(Math.round(rate*5*100)/100); }); <?php $json = file_get_contents("https://api.fixer.io/latest? symbols=CZK,EUR"); //... echo('5€ je '.$CZK.',- kč.'); $json = file_get_contents("https://api.fixer.io/latest? symbols=USD,EUR"); //... echo('5€ je '.$USD.'$.'); ?>
  • 24.
  • 25.
    Angular <!doctype html> <html ng-app> <head> <scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5 /angular.min.js"></script> </head> <body> <div> <input type="text" ng-model="jmeno_cajovny"> <hr> <h1>Jsme v čajovně {{jmeno_cajovny}}!</h1> </div> </body> </html>
  • 26.
    ECMAScript 5 var Position= function(x,y){ this.x= x || 0; this.y= y || 0; }; Position.prototype.getDistance = function(position){ return Math.sqrt(Math.pow(position.x-this.x,2)+Math.pow(position.y- this.y,2)); }; Position.prototype.toString = function(){ return ''+this.x+','+this.y+''; };
  • 27.
    var multiline_string =(function () {/* Podle mě ten nejhorší způsob :( */}).toString().match(/[^]*/*([^]*)*/}$/)[1]; var multiline_string = `V ES6 to jde bez problémů!`; var multiline_string = 'Takhle d*****e se musí psát string na více řádků.'; var multiline_string = 'Nebo' + 'takhle - s tímhle zápisem alespoň umí' + 'pracovat PHPStorm'; var multiline_string = ['Nebo', 'takhle', ].join('n') String
  • 28.
    ECMAScript 6 var Position= class{ constructor(x=0,y=0){ this.x= x; this.y= y; } getDistance(position){ return Math.sqrt(Math.pow(position.x-this.x,2)+Math.pow(position. y-this.y,2)); } toString(){ return ''+this.x+','+this.y+''; } };
  • 29.
    CoffeeScript transpiler Position = (x,y) -> @x = x or 0 @y = y or 0 return Position::getDistance = (position) -> Math.sqrt (position.x - (@x)) ** 2+ (position.y - (@y)) ** 2 Position::toString = -> '' + @x + ',' + @y + ''
  • 30.
  • 31.
  • 32.
    PHP vs. Node ●Apache ● Jednotlivé stránky ● node, pm2 ● Celý server
  • 33.
    Response ● http://www.itnetwork. cz/software/sublime-text? nastaveni=123#goto173043 ● GET,POST, DELETE, HEAD,... Body ● status code - 200, 404, 403,... ● mime type - text/html ● cache control ● form data, upload,... ● html, js, css, image, pdf, video,... Request Header
  • 34.
    Response Headers alt-svc:quic=":443"; ma=2592000;v=" 34,33,32,31,30,29,28,27,26,25" alternate-protocol:443:quic cache-control:private, max-age=0 content-encoding:gzip content-type:text/html; charset=UTF-8 date:Fri, 27 May 2016 11:46:06 GMT expires:-1 server:gws status:200 x-frame-options:SAMEORIGIN x-xss-protection:1; mode=block Request Headers URL:https://www.google.cz/search? q=itnetwork+je+nej&oq=status+code&aqs=ch rome..69i57. 395j0j7&sourceid=chrome&es_sm=93&ie=UT F-8 Request Method:GET Remote Address:216.58.212.67:443 RAW
  • 35.
    Browser vs. Node ●window ● <script src="..."></script> ● HTTP klient ● JQuery, Angular ● global ● var module = require("..."); ● HTTP server, klient ● Express
  • 36.
    Express Routing var http =require("http"); http.createServer(function(request, response){ if(request.url=='/') { response.writeHead(200); response.end('GET request to the home'); }else if(request.url=='/objects') { response.writeHead(200); response.end('GET request to the objects'); }else{ response.writeHead(404); response.end('Not fount :( '); } }).listen(80); var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('GET request to the home'); }); app.get('/objects', function (req, res) { res.send('GET request to the objects'); });
  • 37.
    Jasmine testování describe('lineCollision', function() { it('//',function () { expect(T.Math.lineCollision(0,0,10,10,0,2,10,12)).toBe(false); }); it('/|', function () { expect(T.Math.lineCollision(0,0,10,10,100,0,100,10)).toBe(false); }); it('X', function () { expect(T.Math.lineCollision(0,0,10,10,0,2,2,0)).toBe(true); }); it('L', function () { expect(T.Math.lineCollision(0,0,10,10,10,10,10,0)).toBe(true); }); it('T', function () { expect(T.Math.lineCollision(0,0,10,0,10,10,10,-10)).toBe(true); }); it('/', function () { expect(T.Math.lineCollision(0,0,10,10,1,1,9,9)).toBe(true); }); it('!', function () { expect(T.Math.lineCollision(0,0,0,2,0,4,0,10)).toBe(false); }); });
  • 38.
  • 39.
    HTML browser includes <script src="/app/js/00-other/cookie.static.js"></script> <scriptsrc="/app/js/00-other/copy.static.js"></script> <script src="/app/js/00-other/date.functions.js"></script> <script src="/app/js/00-other/escape.functions.js"></script> <script src="/app/js/00-other/generate-file.jquery.js"></script> <script src="/app/js/00-other/interval.static.js"></script> <script src="/app/js/00-other/is-image-ok.function.js"></script> <script src="/app/js/00-other/is.functions.js"></script> <script src="/app/js/00-other/log.functions.js"></script> <script src="/app/js/00-other/outer-html.jquery.js"></script> <script src="/app/js/00-other/select-text.function.js"></script> <script src="/app/js/00-other/set-input-error.function.js"></script> <script src="/app/js/00-other/string.class.js"></script> <script src="/app/js/00-other/track-event.function.js"></script> <script src="/app/js/00-other/uri-plugin.js"></script> <script src="/app/js/00-other/validate.functions.js"></script> <script src="/app/js/10-model/10-model-draw-3d.prototype.js"></script> <script src="/app/js/10-model/10-model-draw-cache.prototype.js"></script> <script src="/app/js/20-ui/10-browser-compatibility.static.js"></script> <script src="/app/js/20-ui/10-messages.static.js"></script> <script src="/app/js/20-ui/10-popup-window.static.js"></script> <script src="/app/js/20-ui/10-status.static.js"></script> <script src="/app/js/20-ui/10-templates.static.js"></script> <script src="/app/js/20-uri/10-uri.static.js"></script> <script src="/app/js/20-uri/20-uri.onload.js"></script> <script src="/app/js/30-api/10-townsapi.class.js"></script>
  • 40.
    GULP build <script src="/app-build/js/towns.min.js"></script> //-------------------------------------------------------------------------------------------------- gulp.task('build', function() { gulp.src(includes) .pipe(sort()) .pipe(concat('towns-shared.js')) .pipe(es6transpiler({ "disallowUnknownReferences": false, "disallowDuplicated": false })) .pipe(gulp.dest('./build')) .pipe(uglify()) .pipe(rename({suffix: '.min'})) .pipe(gulp.dest('./build')); });
  • 41.
    PHP browser includes <?php $scripts =glob('./js/*.js'); foreach($scripts as $script){ echo('<script src="'.htmlentities($script).'"></script>'."n"); } ?>
  • 42.
  • 43.
    http://www.itnetwork.cz/software/sublime- text Sublime Text 3napsal Jon Skinner. Jde o komerční editor napsaný v C++ a jehož licence stojí kolem 1400 Kč. Editor si ale můžete vyzkoušet a to na neomezenou dobu. Bez licence se však čas od času při ukládání ukáže dialogové okno, kde je doporučena koupě licence…. Honza Bittner | ITNetwork.cz
  • 44.
    WWW App API server DB WWW server WWWApp API server DB WWW server
  • 45.
    http://www.towns. cz/story/572cbec9b8 3bf0b7710c6b58#23, 56 <title>Lesní mýtina |Towns</title> <meta name="description" content=""> <link rel="icon" href="/favicon.ico"> <meta property="fb:app_id" content="408791555870621" > <meta property="og:site_name" content="Lesní mýtina | Towns" > <meta property="og:title" content="Lesní mýtina | Towns" > <meta property="og:description" content="Na lesní mýtině…." > <meta property="og:type" content="article" > <meta property="og:url" content="http://towns. cz/story/572cbec9b83bf0b7710c6b58" > <meta property="og:image" content="http://cdn.towns.cz/towns-cdn/? file=572cbec84cc89-bGVzMi5qcGc=&&width=1200" > ………………………………………………………………………………... <main class="popup-window"> <div class="header">Lesní mýtina</div> <article class="content"> <p><img src="http://cdn.towns.cz/towns-cdn/?file=572cbec84cc89- bGVzMi5qcGc=&amp;&amp;width=800" alt="les2.jpg"></p> </article> </main>
  • 46.
    Další témata ● TypeScript ●Angular ● Google Maps vs. Seznam Mapy ● WS ● REST API ● Mongo DB ● JS Files API - drag & drop ● 2D grafika - canvas / svg ● 3D grafika webgl ● Procedurální mapy ● Gulp ● Sass