Перейдите на SUPER SAIYAN из RxJS Observables

perejdite na super saiyan iz rxjs observables?v=1656578650

Я любил DragonBall Z в младенчестве и все еще люблю его как взрослый.

Среди смешного количества трансформаций оригинальный Super Saiyan остается моим любимым.

0*qE2wxzg_yiFOIN-Q

Ничего похожего на оригинал

Мне тоже нравится RxJS, чем больше я повышаю его уровень, то почему бы не соединить эти два для окончательного раскрытия?

Let’s Go Super Saiyan

Имея четыре листа спрайтов и немного HTML, CSS и RxJS, мы можем воспроизвести это легендарное преобразование!

1*qgex4ns9jPE_tpCy_OTfWA

Это то, что мы будем делать. Трогательно, правда?! ?

Настройка

Все на моем GitHub.

cd ./куда-нибудь-вы-желаете git clone [
cd dbz-rxjs

Open index.html in your favorite browser, and the project in your favorite text editor, and you’re ready to go!

No npm installs today ?

And going forward, I’ll use the acronym “SSJ” instead of “Super Saiyan” for brevity.

First Day of Training

1*FpxB4WdbNMmldqZDnpVp1g

You’ll notice that Goku’s already moving. Since we’re focusing on RxJS, we’ll just skim the project’s starting point.

Here’s the main HTML:

<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>

The bottom div has class="base", which corresponds to this CSS:

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

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

This sets Goku’s width, height, and standing animation.

If you look at his base/ssj sprite sheets, it’s two different positions and we’re switching between them every 0.8 seconds.

1*Cy1fArcSxNJwGDc98YMHEA1*mNVDs7NKfcfkA8l86IxOTA

The switching’s handled towards the bottom of style.css:

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

Same thing for power up:

1*sSTHMTvkazP0BaZPubo4kg1*xkI3tsGLGRpVoH2RjaFK9w

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

We’ll cover the power up meter when we manipulate it.

Mastering the DOM Elements

index.html already includes RxJS@6.2.1 via CDN, so you’re covered.

In app.js, let’s capture the DOM elements we’re interested in:

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

I prefer to alias document.querySelector so using it doesn’t cause me wrist pain.

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

Next, we’ll create a main function and immediately call it.

// ...

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

Powering Up

Here is main’s first code snippet:

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

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

Goku should power up when a key is held down, and stop when that key is let go. We can use the fromEvent operator to create two observables:

  • begin: Notifies when the user presses a key down.
  • end: Notifies whenever the user lets go of a key.

Then we can subscribe to these emissions and act upon them. To get the power up animation, give sprite the powerup class name.

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

1*6-GLDooG9dTyGqNQ2XP0ww

It works, but pressing a key causes him to power up forever…

We must also subscribe to the end observable, so we know when the key has been let go.

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

Now he powers up and down at your command.

Building a Scouter

Any DBZ fan has seen a scouter, the little eyewear used to track power levels (until like episode 20…).

0*8H9CSUZbfDPxmdgR

Obligatory > 9000 joke

As Saiyans power up, their power level grows. Inconceivable, right?

We need a way to track Goku’s power level as he ascends, and trigger the SSJ transformation after say, 100 points.

We can start his power off at 1, and increase it while the user holds a key down.

RxJS Operators

Operators are where RxJS really shines. We can use pure functions to describe how data should transform through the stream.

When the user holds a key down, let’s transform those emissions into a number that increases over time.

Scan

The scan operator is perfect for this. It’s like Array.reduce, but it emits as it’s reducing.

For example, if you have an array of numbers:

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

И хочется их сложить, reduce это отличный выбор.

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

Что делать, если вы хотите видеть каждое сложение, как оно происходит?

Введите scan. Вы можете запустить это на консоли нашей программы.

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)

Видите, как выбросы увеличиваются со временем? Мы можем это сделать с помощью Гоку, когда он пополнится!

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');
  });

Начинаем его уровень с 1 и увеличивайте его на 1 каждый раз keydown происшествия пожара.

Оператор крана позволяет нам быстро регистрировать значение, не нарушая конвейер.

1*CviOotpl-fpRm5qrI7INhQ

Моя сила бесконечно приближается к МАКСИМУМУ!

Идут Супер Саян

Мы упорно тренировались, пора трансформироваться.

The scan оператор отслеживает уровень мощности Гоку. Теперь нам нужно перейти к SSJ, когда он излучает 100.

Я построил карту levels: transformations. Вы можете поставить его прямо выше main.

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

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

Это лишнее, но должно упростить сложение будущих преобразований.

Когда уровень мощности достигает определенного числа в этом powerLevels карту, мы ее удалим current класс от sprite и добавить next класс.

Это позволяет нам плавно переходить от одной трансформации к другой.

Вот код.

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);
  });

Карта и фильтр

Добавление powerup класс теперь происходит внутри tap, потому что это должно происходить всегда. Однако трансформация SSJ, не следует всегда происходят.

Использование mapпоследний уровень мощности становится записью в powerLevels карта. Мы используем filter чтобы проверить, существует ли запись и имеет .next собственность.

Если это так, это означает, что Гоку может пойти еще дальше! Наши .subscribe будет обмениваться current и next как названия классов sprite.

Конечный результат?

1*q-ifHhrr8byMWPENjBpNyQ

Измеритель мощности

Вам так же весело, как и мне, да? К сожалению, наш пользователь этого не сделает.

Они не видят, насколько высок уровень силы Гоку! Они не знают, как открыть консоль DevTools. Мы должны это исправить!

Давайте улучшим наш UX, заполнив счетчик мощности. Вы можете поставить это выше 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`;
};

И позвоните внутрь tap.

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

А вот и мы:

1*qvr0L_5cmXzMI4g8sYtoyg

Идя еще дальше

Чтобы разблокировать больше преобразований, нужно только добавить пройти и обновить наши powerLevels карта. Если вы заинтересованы, отправьте PR на репо, и мы обязательно поговорим.

Вот оригинальный лист спрайтов. Наслаждайтесь!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *