rxjs 怎么使用
I loved DragonBall Z as a kid, and still love it as an adult.
我从小就爱DragonBall Z,但从小到大仍然喜欢它。
Among the ludicrous number of transformations, the original Super Saiyan remains my favorite.
在可笑的转换数量中,原始的超级赛亚人仍然是我的最爱。
Nothing quite like the original
没有什么比原来的
I’m also loving RxJS the more I level up with it, so why not combine these two for the ultimate showdown?
我越喜欢RxJS,就越喜欢RxJS,那么为什么不将两者结合起来进行最终的对决呢?
我们去超级赛亚人 (Let’s Go Super Saiyan)
With four sprite sheets and a bit of HTML, CSS, and RxJS, we can recreate this legendary transformation!
通过四个Sprite工作表以及一些HTML,CSS和RxJS,我们可以重新创建这个传奇的转换!
This is what we’ll be making. Exciting, right?! ?
这就是我们要做的。 令人兴奋,对吗? ?
建立 (Setup)
Everything’s on my GitHub.
一切都在我的GitHub上 。
cd ./wherever-you-want
git clone [https://github.com/yazeedb/dbz-rxjs](https://github.com/yazeedb/dbz-rxjs)
cd dbz-rxjs
Open index.html
in your favorite browser, and the project in your favorite text editor, and you’re ready to go!
在您喜欢的浏览器中打开index.html
,然后在您喜欢的文本编辑器中打开该项目,就可以开始了!
No npm install
s today ?
今天没有npm install
吗?
And going forward, I’ll use the acronym “SSJ” instead of “Super Saiyan” for brevity.
为了简便起见,我将使用缩写词“ SSJ”代替“ Super Saiyan”。
训练的第一天 (First Day of Training)
You’ll notice that Goku’s already moving. Since we’re focusing on RxJS, we’ll just skim the project’s starting point.
您会注意到悟空已经在移动了。 由于我们专注于RxJS,因此我们将略过项目的起点。
Here’s the main HTML:
这是主要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:
底部div
具有class="base"
,它对应于以下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.
如果您查看他的base / ssj Sprite表,它是两个不同的位置,我们每0.8秒切换一次。
The switching’s handled towards the bottom of style.css
:
切换处理到style.css
的底部:
@keyframes stand {from {background-position: 0px;}to {background-position: -255px;}
}
Same thing for power up:
上电相同:
@keyframes powerup {from {background-position: 0px;}to {background-position: -513px;}
}
We’ll cover the power up meter when we manipulate it.
在操作电表时,我们将对其进行介绍。
掌握DOM元素 (Mastering the DOM Elements)
index.html
already includes RxJS@6.2.1
via CDN, so you’re covered.
index.html
已经通过CDN包含RxJS@6.2.1
,因此您已RxJS@6.2.1
。
In app.js
, let’s capture the DOM elements we’re interested in:
在app.js
,让我们捕获我们感兴趣的DOM元素:
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.
我更喜欢给document.querySelector
加上别名,因此使用它不会引起手腕疼痛。
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.
接下来,我们将创建一个main
函数并立即调用它。
// ...const main = () => {// do something
};
main();
上电 (Powering Up)
Here is main
’s first code snippet:
这是main
的第一个代码段:
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:
按下某个键时,悟空应该上电,放开该键时应该停止。 我们可以使用fromEvent
运算符来创建两个可观察对象:
begin
: Notifies when the user presses a key down.begin
:通知当用户按下一个键下来 。end
: Notifies whenever the user lets go of a key.end
:在用户放开任何按键时通知。
Then we can subscribe to these emissions and act upon them. To get the power up animation, give sprite
the powerup
class name.
然后,我们可以订阅这些排放并对其采取行动。 要获得加电动画,请给sprite
加powerup
类别名称。
begin.subscribe(() => {sprite.classList.add('powerup');
});
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
,以便我们知道何时释放密钥。
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…).
任何DBZ粉丝都曾经看到过一个窥探者,那是用来追踪功率水平的小眼镜(直到第20集…)。
Obligatory > 9000 joke
强制性> 9000笑话
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.
我们需要一种方法来跟踪悟空在他上升时的功率水平,并在说出100分之后触发SSJ转换。
We can start his power off at 1, and increase it while the user holds a key down.
我们可以从1开始关闭电源,然后在用户按住键的同时增加电源。
RxJS运算符 (RxJS Operators)
Operators are where RxJS really shines. We can use pure functions to describe how data should transform through the stream.
操作员是RxJS真正发挥作用的地方。 我们可以使用纯函数来描述数据应如何在流中进行转换。
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.
扫描运算符非常适合此操作。 这就像Array.reduce
,但它发出的,因为它的减少 。
For example, if you have an array of numbers:
例如,如果您有一个数字数组:
nums = [1, 2, 3, 4, 5];
And wish to add them up, reduce
is a great choice.
并希望将它们加起来, reduce
是一个不错的选择。
nums.reduce((a, b) => a + b, 0);
// 15
What if you want to see each addition as it happens?
如果您希望每次添加时都能看到该怎么办?
Enter scan
. You can run this in our app’s console.
输入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)
See how the emissions increase over time? We can do that with Goku as he powers up!
看看排放量如何随时间增加? 我们可以通过悟空加电来做到这一点!
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');});
We start his level at 1
and increase it by 1 every time the keydown
event fires.
我们将他的级别从1
开始,并在每次keydown
事件触发时将其级别提高1。
And the tap operator operator lets us quickly log the value without disturbing the pipeline.
轻敲运算符运算符使我们可以快速记录该值,而不会打扰管道。
My power infinitely approaches MAXIMUM!
我的力量无限接近最大值!
超级赛亚人 (Going Super Saiyan)
We’ve trained hard, it’s time to transform.
我们经过艰苦的训练,是时候转型了。
The scan
operator tracks Goku’s power level. Now we need to go SSJ when it emits 100.
scan
操作员跟踪悟空的功率水平。 现在我们需要在SSJ发出100信号时使用它。
I built a map of levels: transformations
. You can put it right above main
.
我建立了一个levels: transformations
。 您可以将其放在main
上方。
const powerLevels = {100: {current: 'base',next: 'ssj'}
};const main = () => {// ...
};
It’s overkill, but should simplify adding future transformations.
这太过分了,但是应该简化添加将来的转换的过程。
When the power level reaches a number in that powerLevels
map, we’ll remove its current
class from sprite
and add the next
class.
当功率级别达到该powerLevels
映射中的一个数字时,我们将从sprite
删除其current
类并添加next
类。
This lets us smoothly go from one transformation to the next.
这使我们能够顺利地从一种转换过渡到另一种转换。
Here’s the code.
这是代码。
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);});
地图和过滤器 (Map and Filter)
Adding the powerup
class now happens inside of tap
, because it should always happen. The SSJ transformation however, shouldn’t always happen.
现在添加powerup
类发生在tap
,因为它应该总是发生。 但是,SSJ转换不应该总是发生。
Using map
, the latest power level becomes an entry in the powerLevels
map. We use filter
to check if the entry exists and has a .next
property.
使用map
,最新功率级别将成为powerLevels
映射中的一个条目。 我们使用filter
检查条目是否存在并具有.next
属性。
If it does, that means Goku can go even further beyond! Our .subscribe
will swap current
and next
as class names on sprite
.
如果可以,那就意味着悟空可以走得更远! 我们的.subscribe
将在sprite
上交换current
名称和next
作为类名。
The end result?
最终结果?
功率计 (Power Meter)
You’re having as much fun as I am, right? Unfortunately, our user won’t.
你和我一样开心,对吧? 不幸的是,我们的用户不会。
They can’t see how high Goku’s power level is! They won’t know how to open the DevTools console. We must remedy this!
他们看不到悟空的功率等级有多高! 他们不知道如何打开DevTools控制台。 我们必须对此进行补救!
Let’s improve our UX by filling the power meter. You can put this above main
.
让我们通过填充功率计来改善用户体验。 您可以将其放在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`;
};
And call it inside tap
.
并在tap
调用它。
tap((level) => {console.log({ level });sprite.classList.add('powerup');fillMeter(level);
});
And here we go:
现在我们开始:
走得更远 (Going Even Further Beyond)
Unlocking more transformations is just a matter of adding sprites, and updating our powerLevels
map. If you’re interested, submit a PR on the repo and we’ll definitely talk.
解锁更多转换只是添加精灵,并更新我们的powerLevels
映射。 如果您有兴趣,请在仓库中提交PR,我们一定会谈。
Here’s the original sprite sheet. Enjoy!
这是原始的精灵表 。 请享用!
翻译自: https://www.freecodecamp.org/news/go-super-saiyan-with-rxjs-observables-d4681ae51930/
rxjs 怎么使用