Am iubit DragonBall Z în copilărie și încă o iubesc ca adult.

Printre numărul ridicol de transformări, originalul Super Saiyan rămâne preferatul meu.

0*qE2wxzg yiFOIN Q

Nimic asemănător originalului

Îmi place, de asemenea, RxJS cu cât mă ridic mai mult cu el, așa că de ce să nu le combin pe acestea două pentru confruntarea supremă?

Să mergem Super Saiyan

Cu patru foi sprite și un pic de HTML, CSS și RxJS, putem recrea această transformare legendară!

Mergeti SUPER SAIYAN cu RxJS Observables

Asta vom face. Emoționant, nu ?! ?

Înființat

Totul este pornit GitHub-ul meu.

cd ./wherever-you-want
git clone [https://github.com/yazeedb/dbz-rxjs](https://github.com/yazeedb/dbz-rxjs)
cd dbz-rxjs

Deschis index.html în browserul dvs. preferat și proiectul în editorul de text preferat, și sunteți gata de plecare!

Nu npm installe azi?

Și înainte, voi folosi acronimul „SSJ” în loc de „Super Saiyan” pentru scurtă durată.

Prima zi de antrenament

Mergeti SUPER SAIYAN cu RxJS Observables

Veți observa că Goku se mișcă deja. Întrucât ne concentrăm pe RxJS, vom defecta doar punctul de plecare al proiectului.

Iată HTML-ul principal:

<div id="root">
  <div id="meter-container">
    <span>Hold any key to POWER UP!</span>
    <div id="meter"></div>
  </div>

  <div id="sprite" class="base"></div>
</div>

Fundul div are class="base", care corespunde acestui CSS:

.base,
.ssj {
  width: 120px;
  height: 250px;
  animation: stand 0.8s steps(2) infinite;
}

.base {
  background-image: url('img/goku-standing-sheet.png');
}

Aceasta setează lățimea, înălțimea și animația în picioare a lui Goku.

Dacă te uiți la foile sale sprite de bază / ssj, sunt două poziții diferite și trecem între ele la fiecare 0,8 secunde.

1611427327 680 Mergeti SUPER SAIYAN cu RxJS Observables1611427327 80 Mergeti SUPER SAIYAN cu RxJS Observables

Comutarea este gestionată spre partea de jos a style.css:

@keyframes stand {
  from {
    background-position: 0px;
  }
  to {
    background-position: -255px;
  }
}

Același lucru pentru pornire:

1611427328 715 Mergeti SUPER SAIYAN cu RxJS Observables1611427328 546 Mergeti SUPER SAIYAN cu RxJS Observables

@keyframes powerup {
  from {
    background-position: 0px;
  }
  to {
    background-position: -513px;
  }
}

Vom acoperi contorul de alimentare când îl vom manipula.

Stăpânirea elementelor DOM

index.html include deja RxJS@6.2.1 prin CDN, deci ești acoperit.

În app.js, să captăm elementele DOM care ne interesează:

const sprite = document.querySelector('#sprite');
const meterContainer = document.querySelector('#meter-container');
const meter = document.querySelector('#meter');

Prefer să fiu alias document.querySelector deci folosirea lui nu-mi provoacă dureri la încheietura mâinii.

const $ = document.querySelector.bind(document);**
const sprite = $('#sprite');
const meterContainer = $('#meter-container');
const meter = $('#meter');

Apoi, vom crea un main funcția și numiți-o imediat.

// ...

const main = () => {
  // do something
};
main();

Pornirea

Aici este mainprimul fragment de cod:

const main = () => {
  const { fromEvent } = rxjs;

  const begin = fromEvent(document, 'keydown');
  const end = fromEvent(document, 'keyup');
};

Goku ar trebui să se aprindă atunci când o tastă este ținută apăsată și să se oprească atunci când cheia este eliberată. Putem folosi fromEvent operator pentru a crea două observabile:

  • begin: Notifică când utilizatorul apasă o tastă jos.
  • end: Notifică ori de câte ori utilizatorul sa mergem a unei chei.

Atunci putem Abonati-va la aceste emisii și acționează asupra lor. Pentru a obține animația de putere, dați sprite powerup numele clasei.

begin.subscribe(() => {
  sprite.classList.add('powerup');
});

1611427329 964 Mergeti SUPER SAIYAN cu RxJS Observables

Funcționează, dar apăsând o tastă îl determină să pornească pentru totdeauna …

De asemenea, trebuie să ne abonăm la end observabil, deci știm când a fost eliberată cheia.

end.subscribe(() => {
  sprite.classList.remove('powerup');
});

Acum, el pornește în sus și în jos la porunca ta.

Construirea unui Scouter

Orice fan DBZ a văzut un scouter, micii ochelari folosiți pentru a urmări nivelurile de putere (până la episodul 20 …).

1611427329 266 Mergeti SUPER SAIYAN cu RxJS Observables

Obligatoriu> 9000 glumă

Pe măsură ce Saiyanii se alimentează, nivelul lor de putere crește. De neconceput, nu?

Avem nevoie de o modalitate de a urmări nivelul de putere al lui Goku pe măsură ce urcă și de a declanșa transformarea SSJ după, de exemplu, 100 de puncte.

Putem începe oprirea la 1 și o putem crește în timp ce utilizatorul ține apăsată o cheie.

Operatori RxJS

Operatorii sunt locul în care RxJS strălucește cu adevărat. Putem folosi funcții pure pentru a descrie modul în care datele ar trebui să se transforme prin flux.

Când utilizatorul ține apăsată o cheie, să transformăm aceste emisii într-un număr care crește în timp.

Scanează

operator de scanare este perfect pentru asta. E ca și cum Array.reduce, dar emite pe măsură ce se reduce.

De exemplu, dacă aveți o serie de numere:

nums = [1, 2, 3, 4, 5];

Și doresc să le adun, reduce este o alegere excelentă.

nums.reduce((a, b) => a + b, 0);
// 15

Ce se întâmplă dacă doriți să vedeți fiecare adăugare așa cum se întâmplă?

introduce scan. Puteți rula acest lucru în consola aplicației noastre.

const { from } = rxjs;
const { scan } = rxjs.operators;

from([1, 2, 3, 4, 5])
  .pipe(scan((a, b) => a + b, 0))
  .subscribe(console.log);

// 1 (0 + 1)
// 3 (1 + 2)
// 6 (3 + 3)
// 10 (6 + 4)
// 15 (10 + 5)

Vedeți cum cresc emisiile în timp? Putem face asta cu Goku pe măsură ce el se alimentează!

const { fromEvent } = rxjs;
const { scan, tap } = rxjs.operators;

const begin = fromEvent(document, 'keydown');
const end = fromEvent(document, 'keyup');

begin
  .pipe(
    scan((level) => level + 1, 1),
    tap((level) => {
      console.log({ level });
    })
  )
  .subscribe(() => {
    sprite.classList.add('powerup');
  });

Începem nivelul lui la 1 și creșteți-l cu 1 de fiecare dată keydown incendii de eveniment.

Si operator robinet operatorul ne permite să înregistrăm rapid valoarea fără a deranja conducta.

1611427330 297 Mergeti SUPER SAIYAN cu RxJS Observables

Puterea mea se apropie infinit de MAXIM!

Merge Super Saiyan

Ne-am antrenat din greu, este timpul să ne transformăm.

scan operatorul urmărește nivelul de putere al lui Goku. Acum trebuie să mergem SSJ când emite 100.

Am construit o hartă a levels: transformations. O puteți pune chiar deasupra main.

const powerLevels = {
  100: {
    current: 'base',
    next: 'ssj'
  }
};

const main = () => {
  // ...
};

Este exagerat, dar ar trebui să simplifice adăugarea de viitoare transformări.

Când nivelul de putere atinge un număr în care powerLevels hartă, o vom elimina current clasa din sprite și adăugați next clasă.

Acest lucru ne permite să trecem ușor de la o transformare la alta.

Iată codul.

const { fromEvent } = rxjs;
const { filter, map, scan, tap } = rxjs.operators;

const begin = fromEvent(document, 'keydown');
const end = fromEvent(document, 'keyup');

begin
  .pipe(
    scan((level) => level + 1, 1),
    tap((level) => {
      console.log({ level });
      sprite.classList.add('powerup');
    }),
    map((level) => powerLevels[level]),
    filter((level) => level && level.next)
  )
  .subscribe(({ current, next }) => {
    sprite.classList.remove(current);
    sprite.classList.add(next);
  });

Hartă și filtrare

Adăugarea powerup ora se întâmplă acum în interiorul tap, pentru că ar trebui să se întâmple întotdeauna. Cu toate acestea, transformarea SSJ, nu ar trebui întotdeauna se întâmplă.

Folosind map, cel mai recent nivel de putere devine o intrare în powerLevels Hartă. Folosim filter pentru a verifica dacă există intrarea și are o .next proprietate.

Dacă da, înseamnă că Goku poate merge și mai departe! Al nostru .subscribe va schimba current și next ca nume de clasă pe sprite.

Rezultatul final?

1611427331 919 Mergeti SUPER SAIYAN cu RxJS Observables

Masurator de putere

Te distrezi la fel de mult ca mine, nu? Din păcate, utilizatorul nostru nu o va face.

Nu pot vedea cât de mare este nivelul de putere al lui Goku! Nu vor ști cum să deschidă consola DevTools. Trebuie să remediem acest lucru!

Să ne îmbunătățim UX-ul umplând contorul de putere. Puteți pune acest lucru mai sus main.

const fillMeter = (level) => {
  const limit = 100;

  if (level >= limit) {
    return;
  }

  const containerWidth = meterContainer.offsetWidth;
  const newWidth = (level / limit) * containerWidth;

  meter.style.width = `${newWidth}px`;
};

Și spune-i înăuntru tap.

tap((level) => {
  console.log({ level });
  sprite.classList.add('powerup');
  fillMeter(level);
});

Și iată-ne:

1611427332 146 Mergeti SUPER SAIYAN cu RxJS Observables

Mergând și mai departe dincolo

Deblocarea mai multor transformări este doar o chestiune de a adăuga sprite și de a le actualiza powerLevels Hartă. Dacă sunteți interesat, trimiteți un PR pe repo și cu siguranță vom vorbi.

Iată foaie sprite originală. Bucurați-vă!