Přejít k navigační liště

Zdroják » JavaScript » Dart StageXL – Jak napsat flashovku bez Flashe (a námahy)

Dart StageXL – Jak napsat flashovku bez Flashe (a námahy)

Články ‐ ‐ JavaScript ‐

Dart je vynikající jazyk a StageXL je vynikající knihovna. Umožní vám nad HTML canvas naprogramovat hru, kterou si pustíte i na Androidu nebo iOS. Pochopitelně v prohlížeči, bez rozšíření a pluginů.

Nálepky:

Výstupem článku je jednoduchá interaktivní blbůstka á la Rorschachův test, kterou si můžete online prohlédnout na we.are.hiring.cz (Chrome prosím, ale ne kvůli StageXL, ale kvůli Polymeru, o tom ovšem jindy).

Co budete potřebovat

Postačí vám DartEditor a základní znalost Dart-u, žádné pokročilé techniky nám nehrozí.
Zdrojáky tutoriálu jsou na GitHub, takže si je stáhněte, otevřete v DartEditoru a jdeme na to.

pubspec.yaml

V závislostech vidíte, že toho moc nevidíte:

name: devfest_2014_stageXL
author: +TomasZverina
description: Workshop o StageXL
dependencies:
  browser: any
  stagexl: any

StageXL je poměrně stabilní, takže “any” nám tu nijak zvlášť nevadí, ale pokud se bojíte, specifikujte si verzi přesně.

Krok 0: Inicizalizace StageXL

StageXL je technologie pro web. Obvykle svůj projekt pomocí “pub” kompilujete do JavaScriptu a nasazujete na web server. Celkem logicky je tedy základem html dokument.
Kromě běžné inicializace Dart-u pak potřebujete jen:

<canvas id="stage" width="500" height="400" style="border: 1px solid black;"></canvas>

Nad tímhle canvasem bude StageXL operovat. Takhle ho nastartujeme …

var canvas = html.querySelector('#stage');
_stage = new Stage(canvas);
var renderLoop = new RenderLoop();
renderLoop.addStage(_stage);

… a takhle do něj přidáme první objekt:

var shape = new Shape();
shape.graphics.rect(0, 0, 50, 50);
shape.graphics.fillColor(Color.Red);
shape.x = 100;
shape.y = 100;
_stage.addChild(shape);

Asi je jasné co to dělá – na souřadnice 100×100 dej červený čtverec 50×50.

Shape je objekt z knihovny StageXL, který se dědí z DisplayObject. DisplayObject je bázová třída pro všechno, co se ve StageXL renderuje a obsahuje všechno, co tak můžete potřebovat pro 2D grafiku:

num x = 0.0; // souřadnice objektu v rodiči
num y = 0.0; // souřadnice objektu v rodiči
num pivotX = 0.0; // referenční bod [0,0], ke kterému vztahuji svoje ...
num pivotY = 0.0; //      souřadnice. Moje "těžiště", střed rotace atd.
num scaleX = 1.0; // natažení v ose X
num scaleY = 1.0; // natažení v ose Y
num skewX = 0.0; // zkosení
num skewY = 0.0;
num rotation = 0.0; // rotace

num alpha = 1.0; // průhlednost (1=plný, 0=zcela průhledný)
bool visible = true; // je viditelný?

Jestli je můj objekt Shape, Bitmap nebo skupina více objektů nastrkaná do jednoho Sprite – to už je jedno.

A rovnou si ukážeme, jak objekty animovat. Náš Shape necháme, aby se během 100 vteřin (konstanta REPEAT) 100x otočil o 380°.

Tween t = new Tween(shape, REPEAT * 1, TransitionFunction.linear);
t.animate.rotation.to(REPEAT * math.PI);
_stage.renderLoop.juggler.add(t);

Animace se dělají tak, že vytvoříte Tween, řeknete co a jak dlouho se má animovat a přidáte Tween do “juggleru” – správce animací.

Když se teď ohlédnete na to málo, co jsme zatím vytvořili, zjistíte, že už nám vlastně skoro nic nechybí. Teď už můžeme napsat v podstatě cokoliv:

  • umíme si připravit HTML a zinicializovat prostředí
  • umíme přidávat a pozicovat objekty
  • umíme je animovat

Zatím neumíme pracovat s obrázky (bitmapami) a neumíme přijímat události (myš, touch).

Krok 1: ResourceManager

Stáhnout si obrázky, zvuky apod. zdroje nějakou dobu trvá. Bylo by užitečné mít možnost si vypsat seznam věcí, které budeme potřebovat, zobrazit “loading” screen a až se zdroje stáhnou, tak abychom mohli náš “stage” spustit. K tomu slouží ResourceManager.

void main() {
  _Rorschach r = new _Rorschach();
  r.initGame();
}

class _Rorschach {

  ResourceManager _resourceManager = new ResourceManager();

  initGame() {
    _resourceManager
      ..addBitmapData("bg", "bg.jpg")
      ..addBitmapData("stain", "stain.png");
    _resourceManager.load().then(_runGame);
  }

  _runGame(_) {
    // muzeme to spustit
  }
}

Vytvoříme instanci ResourceManager, zaregistrujeme zdroje, které budeme potřebovat, a spustíme load(). Load() vrací Future<ResourceManager>, tzn. až bude staženo (then), zavolá se naše metoda _runGame, která ResourceManager bere jako argument. V tomhle případě ale argument ignoruji, ResourceManager mám uložený jako vlastnost mojí třídy _Rorschach.

Stažené bitmapy pak můžeme použít ve stage:

var bgBitmap = new Bitmap(_resourceManager.getBitmapData("bg"));
_stage.addChild(bgBitmap);

Bitmap je opět DisplayObject, takže ji můžeme pozicovat, otáčet, animovat, …

Krok 2: Responzivita

V tomto kroce si upravíme HTML do finální podoby a ukážeme si, jak StageXL nastavit, aby dobře fungovala bez ohledu na velikost obrazovky.

<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
    <script async type="application/dart" src="step01.dart"></script>
    <script async src="packages/browser/dart.js"></script>
    <style type="text/css">
      body { margin: 0; padding: 0; overflow: hidden; }
      #stage { position: absolute; width: 100%; height: 100%; }
    </style>
  </head>
  <body>
    <canvas id="stage"></canvas>
  </body>
</html>

Canvas si nastylujeme na celou obrazovku a meta tagem viewport vysvětlíme prohlížeči, jak má s naším dokumentem zacházet. V tomto případě vytváříme spíš aplikaci než WWW stránku, takže si můžeme dovolit takové neslušnosti jako user-scalable=no.

Teď je potřeba StageXL naučit, jak s dostupným prostorem pracovat.

var bgBitmap = new Bitmap(_resourceManager.getBitmapData("bg"));
var canvas = html.querySelector('#stage');
_stage = new Stage(canvas, width: bgBitmap.bitmapData.width, height: bgBitmap.bitmapData.height);
_stage.scaleMode = StageScaleMode.NO_BORDER;
_stage.align = StageAlign.NONE;

V konstruktoru Stage předáme rozměry, na které ji optimalizujeme – v našem případě šířku a výšku Bitmapy, kterou máme na pozadí.

Režimem StageScaleMode.NO_BORDER jí říkáme: “Vyplň dostupné místo tak, aby kolem tebe nebyl žádný prázdný prostor, ale nedeformuj se”.

Režim StageAlign.NONE zařídí, aby se Stage centrovala uprostřed dostupného viewportu. Pusťte si hotový krok 5 a zahýbejte velikostí okna, myslím, že to bude jasné.

Kromě toho si v kroce připravíme pole skvrn, které budeme později zobrazovat.

for (int a = 0; a < 2 * STAINS_COUNT; a++) {
  Bitmap stain = new Bitmap(stainData);
  stain.visible = false;
  stain.pivotX = stainPx;
  stain.pivotY = stainPy;

  if (a % 2 == 1) {
    // zrcadlove prevracene skrvrny na druhe strane události myši
    stain.scaleX = -1;
  }

  STAINS.add(stain);
  _stage.addChild(stain);
}

Krok 3: Umísťování skvrn na stage

  • html – se nezměnilo
  • dart

Teď už to začne být zajímavější. Napojíme se na stream událostí myši nad stage a budeme kreslit.

int _stainPointer = 0;

...

_stage.onMouseMove.listen(_placeStain);

...

void _placeStain(MouseEvent event) {

  double rotation = _randomGenerator.nextDouble() * PI;
  double x = event.stageX;
  double y = event.stageY;

  STAINS[_stainPointer].x = x;
  STAINS[_stainPointer].y = y;
  STAINS[_stainPointer].rotation = rotation;
  STAINS[_stainPointer].visible = true;

  // a presunem ukazatel na dalsi
  _stainPointer = (_stainPointer + 1) % STAINS_COUNT;

}

Mimochodem – proč vůbec mám nějaké pole těch skvrn a řeším tu složitosti se _stainPointer? Inu tak – rád recykluju. Mohl bych pokaždé vytvořit novou Bitmap a staré Bitmapy zahazovat, ale garbage collector je taky jenom člověk, tak proč ho trápit.

Krok 4: Zrcadlení skvrn

  • html – se nezměnilo
  • dart

V dalším kroce zařídím, aby se skvrny zobrazovaly nejen pod myší, ale i zrcadlově na druhé straně “papíru”. To už je jen nudná 2D geometrie, tak se jen lehce mrkněte do zdrojáků, co se
změnilo.

Krok 5: Filtr událostí

No a jsme na konci! Teď už nám to dělá co chceme, snad jen že se skvrny objevují zbytečně často. K tomu, abych jejich četnost omezil, použiju API Dart streamů, konkrétně metodu “where”:

_stage.onMouseMove.where(_timeToPlaceFilter).listen(_placeStain);

...

bool _timeToPlaceFilter(Event event) {
  _nowTime = new DateTime.now().millisecondsSinceEpoch;
  return (_nowTime - _lastEventTime > MINIMUM_DELAY);
}

void _placeStain(Event event) {
  ...
  _lastEventTime = _nowTime;
}

Kudy dál

StageXL je velmi silný nástroj. Pokud se vám líbí Dart, není v podstatě o čem přemýšlet. Kdekoliv, kde funguje Dart (resp.
Dartem vygenerovaný JavaScript), bude fungovat i StageXL.

Pokračujte pochopitelně na stránkách StageXL. Pravdou je, že dokumentace knihovny není úplně košatá, ale je tam alespoň spousta komentovaných a názorných příkladů, které si můžete nastudovat.

Kromě toho co jsem vám tu ukázal, umí StageXL masky, filtry, WebGL akceleraci, přehrávání zvuků a spoustu dalších libůstek.

P.S.: RIP, Flash. Chybět nám nebudeš, muahahaha!

Komentáře

Odebírat
Upozornit na
guest
0 Komentářů
Nejstarší
Nejnovější Nejvíce hlasů

Začal jako projekt studentů Matfyzu. Český BIRD dnes pomáhá řídit provoz na největších internetových uzlech

Internet drží pohromadě i díky místům, kde se propojují sítě poskytovatelů připojení, cloudů nebo videoslužeb. S předáváním informací o tom, kudy jsou jednotlivé sítě dostupné, jim ve Frankfurtu, Londýně i Amsterdamu pomáhá český BIRD. Začal jako studentský projekt, po první verzi z roku 2000 léta spíš dřímal a soustavný vývoj obnovil až CZ.NIC. Ve své úloze uspěl tak výrazně, že největší uzly nakonec začaly platit vývoj konkurenčního softwaru, aby nebyly závislé jen na něm.

WebSocket: Jak dostat webovou aplikaci do reálného času

Chat, živé notifikace, společná úprava dokumentu nebo průběžně aktualizované kurzy. Uživatelé dnes čekají, že se webová aplikace změní ve chvíli, kdy se změní data, a ne až po stisknutí klávesy F5. Protokol WebSocket je nejrozšířenější způsob, jak mezi prohlížečem a serverem vytvořit trvalý obousměrný kanál. V článku se podíváme, jak funguje pod kapotou, jak ho použít v prohlížeči i na serveru a na co si dát pozor při nasazení do produkce.

Zahoďte knihovny: dropdowny, tooltipy a přechody stránek jen s HTML a CSS

Rozbalovací menu, tooltip, modální okno a plynulý přechod mezi stránkami. Ještě nedávno čtyři důvody, proč do projektu přidat několik knihoven a desítky kilobajtů JavaScriptu. V roce 2026 už to všechno zvládne prohlížeč sám. Ukážeme si, jak na to s Popover API, CSS anchor positioningem a View Transitions, a také kde mají své limity.