rxj热血江hsf湖私服_如何使用RxJ进行React性思考和动画化移动对象
rxj熱血江hsf湖私服
These days, many software systems have to deal with asynchronous behaviors and time-related issues.
如今,許多軟件系統必須處理異步行為和與時間有關的問題。
Continuous connectivity, distributed systems, microservices-based architectures, the cloud, non blocking platforms — the consequence of all these things is that we somehow have to deal with asynchronicity and time. Our software systems have to learn how to deal with streams of events, which are, by their nature, asynchronous.
連續連接,分布式系統,基于微服務的體系結構,云,無阻塞平臺-所有這些事情的結果是,我們必須以某種方式處理異步性和時間。 我們的軟件系統必須學習如何處理事件流,這些事件流本質上是異步的。
Reactive programming provides powerful tools, based on a functional programming style, that help us model systems that work in such a world. But these systems require us to think reactively when we design our solutions.
響應式編程基于功能性編程風格提供了功能強大的工具,可幫助我們對在這樣的世界中工作的系統進行建模。 但是這些系統要求我們在設計解決方案時做出React。
Thinking reactively often represents a challenge, as does any change of perspective. At the same time, it may be easier than you expect. Just look at what happens in the real world and try to map it in a straightforward way.
與觀點的任何改變一樣,被動地思考通常代表著挑戰。 同時,它可能比您預期的要容易。 只需看看現實世界中發生的事情,然后嘗試以一種簡單的方式將其映射即可。
In this article, I aim to show you how to apply reactive and functional thinking to solve a very well-known problem in a natural way: how to animate an object with controlled motion. The metaphor I’ll use is that of a vehicle which can accelerate and brake, following the commands issued by a remote controller.
在本文中,我旨在向您展示如何運用React性和功能性思維以一種自然的方式解決一個非常著名的問題:如何以受控的運動為對象設置動畫。 我將使用的隱喻是按照遙控器發出的命令可以加速和制動的車輛的隱喻。
In the implementation we’ll be using RxJs, the JavaScript version of ReactiveX, and Typescript.
在實現中,我們將使用RxJ,ReactiveXJavaScript版本和Typescript。
The code for a full demo implementation can be found here.
完整的演示實現代碼可在此處找到。
If you like this, this is a second article around these themes.
如果您愿意, 這是圍繞這些主題的第二篇文章 。
快速回顧動力學的簡單基礎 (A quick recap of the simple basics of dynamics)
If you want to change the velocity of an object, you need to apply a force to it which in turn impresses an acceleration to the same object. If you know the value of acceleration A of the object, you can calculate the variation of its velocity dV in a certain time interval dT with the formula
如果要更改對象的速度,則需要對其施加力,從而對同一個對象施加加速度。 如果知道對象的加速度A的值,則可以使用公式計算在特定時間間隔dT中其速度dV的變化
dV = A * dT
dV = A * dT
Similarly, if you know the velocity V, then you can calculate the variation in space dS in a time interval dT with the formula
同樣,如果知道速度V,則可以使用以下公式計算時間間隔dT中空間dS的變化:
dS = V * dT
dS = V * dT
Conclusion: if you have an acceleration A impressed to an object whose initial velocity is V0, you can approximate the velocity of the object in the time interval dT with its average, like this:
結論:如果您對初始速度為V0的物體施加了加速度A ,則可以在時間間隔dT中以其平均值來近似物體的速度,如下所示:
averageVel = (V0 + V1) / 2 = (V0 + V0 + dV) / 2 = V0 + A/2 * dT
averageVel =(V0 + V1)/ 2 =(V0 + V0 + dV)/ 2 = V0 + A / 2 * dT
and then calculate the approximate variation of space dS in the same interval dT with the formula
然后用公式計算在相同間隔dT中空間dS的近似變化
dS = averageVel * dT = V0 * dT + A/2 * dT2
dS = averageVel * dT = V0 * dT + A / 2 *dT2
The shorter the time interval dT, the better the approximation.
時間間隔dT越短,則近似值越好。
“用運動為物體動畫”的含義 (What “animating an object with movement” means)
If we want to animate an object with a movement controlled by acceleration, (that is, if we want to simulate how an object would move if subject to forces), we have to introduce the dimension of time.
如果要通過加速度控制的運動為對象設置動畫(即,如果要模擬對象在受力作用下的運動方式),則必須引入時間的維度。
We have to divide the time in intervals, dT, calculate the space travelled for every dT, and show the new position at every interval.
我們必須將時間劃分為間隔dT,計算每個dT的行進空間,并顯示每個間隔的新位置。
使用PULL方法-詢問信息 (Using the PULL approach — ask for information)
We can use the above function, and pull from it the information we need (how much the object moved during the last time interval dT given a certain acceleration A and initial velocity V). We would take the result of the function and use it to calculate the new position, as long as we are able to somehow remember the previous position.
我們可以使用上面的功能, 然后從它我們需要的信息(對象期間給予一定的加速A 和初始速度V的最后時間間隔的dT多少移動)。 只要我們能夠以某種方式記住前一個位置,我們就可以使用該函數的結果并將其用于計算新位置。
If we rely on a pull approach, it is the caller (the SW component) calling the function that does most of the work. It keeps and updates state, controls time, and manages the entire movement.
如果我們依靠拉方法,則調用方(SW組件)會調用函數來完成大部分工作。 它保持并更新狀態,控制時間并管理整個運動。
React方式:PUSH(和命令)方式 (The reactive way: the PUSH (and command) approach)
If you think of a vehicle which is controlled remotely by someone, then you would probably imagine that:
如果您想到某人遠程控制的車輛,那么您可能會想到:
- the vehicle transmits at a regular frequency its position and velocity to the controller 車輛以固定的頻率將其位置和速度傳輸到控制器
- the controller can change the acceleration of the vehicle (steering and braking are just changes in the accelerations along the space axis) to guide the vehicle’s movement 控制器可以改變車輛的加速度(轉向和制動只是沿空間軸的加速度的變化)來引導車輛的運動
Such an approach has the advantage to clearly separate responsibilities:
這種方法的優點是可以明確區分職責:
Reactive programming provides the tools to build a software solution to this problem mirroring exactly this model. This is probably what you would expect in the real world:
React式編程提供了工具,以針對此問題構建軟件解決方案,以準確地反映此模型。 這可能是您在現實世界中所期望的:
- a vehicle that transmits the details of its dynamics (for example, speed, position, direction) — the Observable 傳遞其動力學細節(例如,速度,位置,方向)的車輛-Observable
- a controller that listens to such transmissions and issues commands to accelerate, decelerate, steer, and brake — the Observer 監聽此類傳輸并發出命令以進行加速,減速,轉向和制動的控制器—觀察者
React式實施-RxJ (Reactive implementation — RxJs)
To develop the solution, we use Typescript as our programming language and the ReactiveX model via RxJs implementation. But the concepts can be easily transposed to many of the other languages supported by ReactiveX.
為了開發解決方案,我們使用Typescript作為我們的編程語言,并通過RxJs實現使用ReactiveX模型。 但是,這些概念可以輕松地轉換為ReactiveX支持的許多其他語言。
MobileObject類-在空間中移動的對象的表示形式 (The MobileObject class — a representation of objects that move in space)
We are going to build our simulator using reactive techniques with a functional programming style. But we’ll still use good old object-oriented (OO) concepts to build a clear frame for our implementation. So let’s start with the MobileObject class:
我們將使用功能性編程風格的React技術來構建模擬器。 但是,我們仍將使用良好的舊的面向對象(OO)概念為我們的實現建立清晰的框架。 讓我們從MobileObject類開始:
export class MobileObject {}This class will represent the objects that transmit at regular intervals of time all relevant data about their dynamics, like speed, position, and acceleration. Within this class we will work reactively.
此類將表示以固定的時間間隔傳輸有關其動力學的所有相關數據(如速度,位置和加速度)的對象。 在本課程中,我們將進行被動式工作。
讓我們介紹一下Observable先生,它是我們MobileObject的核心 (Let’s introduce Mr. Observable, the core of our MobileObject)
As we know, to be controlled remotely, a vehicle must continuously transmit to its controller data about itself, namely:
我們知道,要進行遠程控制,車輛必須連續地向其控制器傳輸有關其自身的數據,即:
- its current velocity 當前速度
- its current position 當前位置
- how much its position and velocity varied since the last interval of time 自上次間隔以來,其位置和速度有多少變化
This is just a stream of data over time emitted by the vehicle. The ReactiveX Observable is a way to model streams of events carrying data over time. So we can use Observables to model the data transmitted by our vehicle.
這只是車輛發出的隨時間推移的數據流 。 ReactiveX Observable是一種對隨時間推移承載數據的事件流進行建模的方法。 因此,我們可以使用Observables對我們的車輛傳輸的數據進行建模。
我們的時鐘:一系列時間間隔 (Our clock: a sequence of time intervals)
The first thing we need to create is a sequence of time intervals. Each event emitted in this sequence knows the time elapsed since its predecessor, as illustrated in the following diagram:
我們需要創建的第一件事是一系列時間間隔。 如下圖所示,此序列中發出的每個事件都知道自其上一個事件以來所經過的時間:
With RxJs we can create such a clock with an Observable using the following function:
使用RxJ,我們可以使用以下函數創建帶有Observable的時鐘 :
private buildClock(frameApproximateLenght: number) {let t0 = Date.now();let t1: number;return Observable.timer(0, frameApproximateLenght).do(() => t1 = Date.now()).map(() => t1 - t0).tap(() => t0 = t1).share(); } const clock = buildClock(xxx);Let’s call this observable clock. Our clock emits approximatively every xxx milliseconds. Each event emitted by clock will carry the exact number of milliseconds elapsed since the previous emission.
我們將此稱為可觀察時鐘 。 我們的時鐘大約每xxx毫秒發出一次。 時鐘發出的每個事件將攜帶自上一次發出以來經過的確切毫秒數。
We will see later, when talking about animation frames, why this method for creating an observable of time intervals is convenient. Later we will also cover why it is important to use the share operator while creating the clock.
稍后我們將在討論動畫幀時看到,為什么這種創建可觀察時間間隔的方法很方便。 稍后我們還將介紹為什么在創建時鐘時使用share運算符很重要。
計算時間間隔內速度和空間的變化 (Calculate the variation of speed and space in a time interval)
Let’s assume MobileObject is subject to an acceleration A. Now that we a clock, we can calculate the variation of speed dV using the formula dV = A * dT. Using this formula and the map operator of RxJs, we can create an Observable that emits the variation of speed over time:
假設MobileObject的加速度為A。 現在我們有了一個時鐘 ,我們可以使用公式dV = A * dT計算速度dV的變化。 使用此公式和RxJs的map運算符,我們可以創建一個Observable,它發出速度隨時間的變化:
If we store in a variable velocity vel at time tX, we can calculate the approximate variation in space at the next time interval t(X+1) with the formula dS = vel * dT + A / 2 * dT2. Again, using the map operator, we can obtain an Observable that emits the variation of space over time.
如果我們在時間tX處以可變速度vel存儲,則可以使用公式dS = vel * dT + A / 2 *dT2計算下一個時間間隔t(X + 1)的空間近似變化。 同樣,使用map運算符,我們可以獲得一個Observable,它發出空間隨時間的變化。
Using the same approach, we can build an observable that emits at every tick of the clock all the relevant information about the dynamics of MobileObject, starting just from its acceleration A. We call this observable dynamics.
使用相同的方法,我們可以構建一個可觀察的對象,該對象在時鐘的每一個滴答聲中都發出有關MobileObject動態的所有相關信息,僅從其加速度A開始 。 我們稱之為可觀察的動態 。
But acceleration can change — so what?
但是加速度可以改變-那又如何呢?
This works if we know the acceleration A and if A is a constant.
如果我們知道加速度A且A為常數,則此方法有效。
What happens though if the acceleration changes over time? Maybe we start with an acceleration A0, then after a period of time P0 a force changes it to A1, then after P1 it changes to A2, and then to A3, like in the following diagram.
但是,如果加速度隨時間變化會怎樣? 也許我們從加速度A0開始,然后在一段時間P0之后 ,力將其更改為A1 ,然后在P1之后將其更改為A2 ,然后更改為A3 ,如下圖所示。
acceleration looks like an Observable, doesn’t it? Each event represents a change in the acceleration of the MobileObject (that is, the fact that a new force has been applied to MobileObject).
加速看起來像是可觀察的,不是嗎? 每個事件都表示MobileObject的加速度發生了變化(也就是說,已經向MobileObject施加了新的力量)。
Knowing A0 we can calculate the speed and position of MobileObject for the period P0 using an observable dyn0, built according to the logic described above. When the acceleration changes, we can still calculate speed and position, but we have to abandon dyn0 and switch to a new Observable dyn1, which is built with the same logic as dyn0, but now using the new acceleration A1. The same switching is repeated when acceleration becomes A2 and then A3.
知道了A0,我們就可以使用可觀測的dyn0來計算周期P0的MobileObject的速度和位置,該dyn0是根據上述邏輯構建的。 當加速度發生變化時,我們仍然可以計算速度和位置,但是我們必須放棄dyn0并switch到新的Observable dyn1 ,它與dyn0具有相同的邏輯,但是現在使用的是新的加速度A1 。 當加速度變為A2然后變為A3時,將重復相同的切換。
This is where the operator switchMap comes in handy. Via switchMap we can transform the acceleration observable into a new version of the dynamics observable. It can receive a new value emitted by acceleration, start off a new observable dynX, complete the previous observable dynX-1, and emit all the events generated by the various observables of type dynX which it has spun off during this processing. The following diagram illustrates the switchMap mechanism.
這是操作員switchMap派上用場的地方。 通過switchMap我們可以將可觀察到的加速度轉換為可觀察到的動力學的新版本。 它可以接收由加速度發射的新值,啟動新的可觀察的dynX,完成先前的可觀察的dynX-1 ,并發射由dynX類型的各種可觀察的對象生成的所有事件,該事件在此處理過程中已分離出來。 下圖說明了switchMap機制。
現在歡迎主題先生-MobileObject的加速踏板 (Welcome now Mr. Subject — the accelerator pedal of MobileObject)
For this to work, we need to create the accelerator pedal. This is a mechanism that allows external controllers to change the acceleration of MobileObject.
為此,我們需要創建油門踏板。 這是一種允許外部控制器更改MobileObject加速度的機制。
Acceleration needs to be controlled, so we need a command mechanism.
加速需要控制,因此我們需要一種命令機制。
To change the acceleration of MobileObject, we need to cause the acceleration observable to emit events when the controller decides so. If we need to control when an Observable emits, we need to look at Subject, another type provided by RxJs.
要更改MobileObject的加速度,我們需要在控制器決定時使可觀察到的加速度發出事件。 如果我們需要控制Observable何時發出,則需要查看Subject ,這是RxJs提供的另一種類型。
A Subject is an Observable which offers the following methods:
主題是可觀察對象,它提供以下方法:
next(val) : emits an event with val as value
next(val) :發出一個以val為值的事件
error() : terminates itself with an error
error() :以錯誤終止自身
complete() : completes gracefully
complete() :正常完成
So, if we want to change the acceleration over time, we can create the acceleration observable as a Subject, and then use the next() method to emit the event when needed.
因此,如果我們想隨時間改變加速度,則可以創建可觀察到的加速度作為Subject,然后在需要時使用next()方法發出事件。
將所有內容包裝到MobileObject類中 (Wrap everything into the MobileObject class)
Now that we have all the parts required, we have just to assemble them into a coherent MobileObject class.
現在我們已經擁有了所需的所有部分,我們只需要將它們組裝成一個連貫的MobileObject類。
In a nutshell, this is how a MobileObject is modeled in a reactive world. There are:
簡而言之,這就是在React世界中對MobileObject建模的方式。 有:
some observables, dynamicsX and dynamicsY from the example, that emit data about its dynamics along the various dimensions of space (in the above example just 2, X and Y, in a bi-dimensional plan)
該示例中的一些可觀察對象, dynamicsX和dynamicsY ,它們沿空間的各個維度發出有關其動力學的數據(在上面的示例中,二維平面中只有2,X和Y)
some subjects, accelerationX and accelerationY from the example, that allow controllers to change acceleration along the various dimensions
示例中的一些主題, accelerationX和accelerationY ,允許控制器沿各個維度更改加速度
- an internal clock that establishes the frequency of the time intervals 建立時間間隔頻率的內部時鐘
In a 2 dimensional space, we have 2 different observables emitting the variation of space. Such observables need to share the same clock if we want a coherent movement. And clock is in itself an observable. So that they can share the same observable, we have added the share() operator at the end of the buildClock() function we described previously.
在二維空間中,我們有2個不同的可觀測量,它們發出了空間的變化。 如果我們要進行連貫的運動,這些可觀測對象需要share相同的時鐘 。 時鐘本身就是可觀察的。 為了使它們可以共享相同的可觀察對象,我們在前面描述的buildClock()函數的末尾添加了share()運算符。
最后接觸:剎車 (Final touch: brake)
Let’s look at this very simplistically. If you want to stop or slow down a car that moves with velocity V0, you have to apply to the car an acceleration in the direction opposite that of its velocity.
讓我們非常簡單地看一下。 如果要停止或減速以速度V0移動的汽車,則必須向汽車施加與速度相反的加速度。
After a period of time, the velocity of the car will become 0, and at that point no further acceleration is applied to the car.
一段時間后,轎廂的速度將變為0,并且此時不再對轎廂施加進一步的加速度。
To obtain a brake effect, we therefore have to know the direction of the MobileObject and stop the negative acceleration when the MobileObject reaches velocity 0.
因此,為了獲得制動效果,我們必須知道MobileObject的方向,并在MobileObject達到速度0時停止負加速度。
Knowing the direction is easy. We have just to take the first event emitted by the dynamicsX or dynamicsY observable, depending on the axis we are interested in, and check if the velocity of the last event is positive or negative. The sign of the velocity is the direction.
知道方向很容易。 我們只需要根據所關注的軸來獲取dynamicsX或dynamicsY可觀察到的第一個事件,并檢查最后一個事件的速度是正還是負。 速度的標志是方向。
directionX = mobileObject.dynamicsX .take(1) .map(dynamics => dynamics.vel > 0 ? 1 : -1)directionX is an observable which emits only one event. The value emitted is 1 if the velocity is positive, -1 otherwise.
directionX是可觀察的對象,僅發出一個事件。 如果速度為正,則發出的值為1,否則為-1。
So, when MobileObject receives the command to brake, all it has to do is to get the direction and apply an opposite acceleration, like this:
因此,當MobileObject接收到制動命令時,它要做的就是獲取方向并施加相反的加速度,如下所示:
directionX .switchMap(// BRAKE is a constant of acceleration when mobileObject brakesdir => mobileObject.accelerationX.next(-1 * dir * BRAKE) )We are almost there. We just need to make sure that once the velocity reaches 0, or close to 0, we remove any acceleration. And this is how we can get what we want.
我們就快到了。 我們只需要確保一旦速度達到0或接近0,就可以消除任何加速度。 這就是我們可以得到想要的東西的方式。
directionX .switchMap(// BRAKE is a constant of acceleration when mobileObject brakesdir => {mobileObject.accelerationX.next(-1 * dir * BRAKE);return mobileObject.dynamicsX// VEL_0 is a small value below which we consider vel as 0.filter(dynamics => Math.abs(dynamics.vel) < VEL_0).do(() => mobileObject.accelerationX.next(0).take(1)} ).subscribe()Here, after issuing the brake acceleration command, we simply select the first event of dynamicsX observable where the velocity is sufficiently small to be considered 0. Then we issue a command to apply an acceleration equal to zero. The last take(1) operator is added to make sure that we immediately unsubscribe, since the brake observable has completed its job.
在此,在發出制動加速度命令后,我們僅選擇可觀察到的動力學 X的第一事件,其中速度足夠小以至于可以視為0。然后發出命令以施加等于0的加速度。 添加了最后一個take(1)運算符,以確保我們立即退訂,因為可觀察的制動器已完成其工作。
This code needs some refinement to work really smoothly, but it is enough to convey the basics of braking reactively.
該代碼需要進行一些改進才能真正平穩地運行,但是足以傳達被動制動的基礎。
回到開始:動畫 (Back to the start: animation)
All this may look good, but we still want to animate our MobileObject. For instance, we want to create an application where a user can issue acceleration commands via a 4-button console and see the MobileOject move accordingly.
所有這些看起來都不錯,但是我們仍然要為MobileObject設置動畫。 例如,我們要創建一個應用程序,用戶可以在其中通過4按鈕控制臺發出加速命令,并查看MobileOject相應地移動。
Such an app acts as the controller of MobileObject and as the monitor to show the animation.
這樣的應用程序充當MobileObject的控制器 ,并充當顯示動畫的監視器。
發出命令 (Issuing commands)
Controlling the movement of MobileObject means that we need to apply acceleration. The browser app can do this using the accelerationX subject provided by MobileObject, as shown in the following snippet.
控制MobileObject的運動意味著我們需要應用加速。 瀏覽器應用程序可以使用MobileObject提供的加速 X主題來執行此操作,如以下代碼片段所示。
<button id="positiveAccX" (mousedown)="pAccX()" (mouseup)="releaseAccX()"/>// mobileObject contains the instance we want to control const accelerationValue = 100; pAccX() {mobileObject.accelerationX.next(accelerationValue); } releaseAccX() {mobileObject.accelerationX.next(0); }An acceleration of 100 is applied when the mouse button is down and acceleration is set to 0 when the mouse button is released, simulating the accelerator pedal.
當按下鼠標按鈕時,將施加100的加速度;當釋放鼠標按鈕時,將加速度設置為0,以模擬油門踏板。
顯示動畫動作 (Show animated movement)
MobileObject exposes dynamicsX and dynamicsY, 2 Observables that continuously emit data about the movement along the respective axis (for example, deltaSpace, current velocity, acceleration along X and Y). So the browser app has to subscribe to them to receive this streams of events and change the position of MobileObject at every event emitted, as shown in this sample snippet:
MobileObject公開dynamicsX和dynamicsY ,這兩個Observables連續發出有關沿相應軸的運動的數據(例如,deltaSpace,當前速度,沿X和Y的加速度)。 因此,瀏覽器應用必須訂閱它們才能接收事件流,并在發出的每個事件處更改MobileObject的位置,如以下示例代碼所示:
interface Dynamics {deltaVel: number; vel: number; deltaSpace: number; space: number} const mobileObjectElement = document.querySelector('.mobileobj'); mobileObject.dynamicsX.subscribe((dyn: Dynamics) => {const currentPositionX = mobileObjectElement.style.left;const deltaSpaceX = dyn.deltaSpace;mobileObjectElement.style.left = currentPositionX + deltaSpace;} )動畫框架 (Animation Frame)
The browser works asynchronously, and it is not possible to predetermine when it is ready to display a new frame. The animation, or the simulation of movement, is provided by changing the position of an object over time. A smooth animation changes the position at every frame displayed by the browser.
瀏覽器是異步運行的,因此無法預先確定何時可以顯示新的框架。 通過隨時間改變對象的位置來提供動畫或運動模擬。 平滑的動畫會更改瀏覽器顯示的每一幀的位置。
RxJs provides a Scheduler called animationFrame which wraps the requestAnimationFrame browser API. A Scheduler is a type of RxJs that controls when the events emitted by an observable really occur.
RxJs提供了一個名為animationFrame的調度程序 ,該調度程序包裝了requestAnimationFrame瀏覽器API。 調度程序是一種RxJ,它控制可觀察對象發出的事件何時真正發生。
We can use animationFrame and the interval static method of Observable to create an observable that emits one event every time the browser is ready to display a new frame.
我們可以使用animationFrame和Observable的interval靜態方法來創建一個observable,它在每次瀏覽器準備顯示新幀時都發出一個事件。
Observable.interval(0, animationFrame)Now we just need to add the length of time passed since the last frame to the events emitted by the this observable, and we have what we needed: an observable that emits every time the browser is ready to display a new frame with the amount of time passed since the last frame was displayed.
現在,我們只需要將自上一幀以來經過的時間添加到此observable發出的事件中,我們便有了所需的東西:每當瀏覽器準備顯示一個新的幀時,此observable就會發出,其數量為自顯示最后一幀以來經過的時間。
This is the new clock which we use in MobileObject to provide a stream of events relative to the movements (dynamicsX and dynamicsY). These movements are synchronized with when the browser is ready to show a new frame.
這是我們在MobileObject中使用的新時鐘 ,用于提供與運動有關的事件流( dynamicsX和dynamicsY )。 當瀏覽器準備顯示新框架時,這些移動與同步。
You may have noticed that, in this last code example, the syntax has slightly changed. We are now using the “pipeable” operators. We did not use them before, since they don’t add anything to our reasoning. Still, it is worth introducing them since they represent new syntax you can use since RxJS 6.
您可能已經注意到,在上一個代碼示例中,語法略有更改。 我們現在正在使用“管道”運算符。 我們以前沒有使用過它們,因為它們不會在我們的推理中添加任何內容。 盡管如此,還是值得介紹它們,因為它們代表了自RxJS 6起可以使用的新語法。
You may also notice the defer function. This is an RxJs function that returns an Observable, but makes sure that the logic defined within the function passed as a parameter to defer is executed only when the Observable is subscribed.
您可能還會注意到defer功能。 這是一個RxJs函數,它返回一個Observable,但要確保僅在訂閱Observable時,才執行在函數中定義為參數傳遞的邏輯以進行defer 。
This allows us to execute the buildClock() method at any time, maybe while initializing a UI component. It also allows us to be sure that the clock will start ticking only when subscribed and with the right timing. More specifically let startOfPreviousFrame = animationFrame.now(); will be executed only when the clock observable is subscribed.
這使我們可以在初始化UI組件時隨時執行buildClock()方法。 它還使我們可以確保只有在訂閱時并在正確的時間,時鐘才會開始計時。 更具體地說, let startOfPreviousFrame = animationFrame.now(); 僅當訂閱了可觀察的時鐘時才會執行。
最后但并非最不重要的一點,關于函數式編程風格的幾句話 (Last but not least, a few words about the functional programming style)
At the beginning of our discussion, we talked about building the stream of data representing the movement of MobileObject over time. We called this the dynamics observable, and used the following transformation logic:
在我們的討論開始時,我們討論了構建表示MobileObject隨時間變化的數據流。 我們稱其為動態可觀察的,并使用了以下轉換邏輯:
map(dT => {const dV = A * dT;vel = vel + dV;const dS = vel * dT + A / 2 * dT * dT; space = space + dS;return {dV, vel, dS, space}; })This assumes that we have defined the variables vel and space somewhere so that they are visible within the scope of the function passed as a parameter to the map operator.
假設我們已經在某個地方定義了變量vel和space ,以便它們在作為參數傳遞給map運算符的函數范圍內可見。
The first solution that might come to mind for a traditional OO programmer is to define such variables as properties of the MobileObject class. But this would mean storing state information at the object level that should only be changed by the transformation defined within the map operator shown above.
傳統OO程序員可能想到的第一個解決方案是將此類變量定義為MobileObject類的屬性。 但這意味著將狀態信息存儲在對象級別,該狀態信息只能通過上面顯示的map運算符中定義的轉換來更改。
If you make this state information accessible to potentially any piece of logic within MobileObject, you risk changing it by mistake, making the entire object inconsistent. Plus, any time such state is changed, we have to think about other parts of logic that are potentially relying on this state. We need to consider the consequences of such dependencies, which sometimes may be pretty well hidden.
如果使此狀態信息可能對MobileObject中的任何邏輯都可訪問,則可能會錯誤地更改它,從而使整個對象不一致。 另外,無論何時更改這種狀態,我們都必須考慮可能依賴于此狀態的邏輯的其他部分。 我們需要考慮這種依賴的后果,有時可能會掩蓋得很深。
Here is where functional programming comes to our rescue.
這是函數式編程為我們提供幫助的地方。
更高級別的功能 (Higher level functions)
A higher level function is a function which returns a function. The name might reminds you of higher level observables, which are observables that emit other observables.
較高級別的函數是返回函數的函數。 該名稱可能使您想起更高級別的可觀察物,它們是發出其他可觀察物的可觀察物。
The dynamics observable of MobileObject can be built if we have the clock observable and we know the acceleration A. So we can say that dynamics is function of the clock observable and the acceleration value A.
如果我們可以觀察時鐘并且知道加速度A ,則可以構建MobileObject的可觀察動力學 。 因此,可以說動力學是可觀察到的時鐘和加速度值A的函數。
We can also create a function, dynamicsF, which returns a function dF. It in turn, when called, returns the dynamics observable, as shown in the snippet below.
我們還可以創建一個函數dynamicsF ,該函數返回一個函數dF。 依次調用時,它返回可觀察到的動態 ,如下面的代碼片段所示。
Notice that in dynamicsF, we have defined the variables vel and space, which are perfectly visible from within dF, making our code consistent and correct.
注意,在dynamicsF中,我們定義了變量vel和space ,這些變量在dF中是完全可見的,從而使我們的代碼一致且正確。
If we have a variable clock where we store the clock observable and a variable acc where we store the value of the acceleration A, we can use the function dynamicsF, which we have just defined, to build our dynamics observable as shown in the following snippet.
如果我們有一個可變的clock存儲可觀測的時鐘,而一個可變的acc存儲加速度A的值,則可以使用剛剛定義的dynamicsF函數來構建可觀測的動態 ,如以下代碼片段所示。 。
const dynFunction = dynamicsF(); const dynamics = dynFunction(clock, A);The key point is that now dynFunction contains in its internals the variables vel and space. It stores them internally in its own state, a state which is not visible to anything outside the function.
關鍵是現在dynFunction內部包含變量vel和space 。 它在內部以它們自己的狀態存儲它們,該狀態對于函數外部的任何對象都不可見。
Assuming that dynamicsF is a method of MobileObject class, the final version of the code that creates the dynamics observable in MobileObject constructor can be written as
假設dynamicsF是MobileObject類的一種方法,則可將創建可在MobileObject構造函數中觀察到的動態的代碼的最終版本編寫為:
const dfX = this.dynamicsF(); this.dynamicsX = this.accelerationX.swithMap(a => dfX(this.clock, a));In doing so, we have confined the state information about current velocity and space into the function dfX. We’ve also removed the need to define properties for current velocity and space in MobileObject. And we have improved reuse, since dynamicsF() does not have any reference to any axis and can be used to calculate both dynamicsX and dynamicsY via function composition.
這樣,我們將有關當前速度和空間的狀態信息限制在函數dfX 。 我們也不再需要為MobileObject中的當前速度和空間定義屬性。 而且,由于dynamicsF()沒有對任何軸的引用,并且可以用于通過函數組合來計算dynamicsX和dynamicsY ,因此我們改善了重用性。
By applying a functional programming style (in this case higher isolation), we have gained higher security for our code and higher reuse.
通過應用函數式編程風格(在這種情況下,更高的隔離度),我們為代碼獲得了更高的安全性以及更高的重用性。
結論 (Conclusion)
It has been a pretty long journey. We have seen the use of some of the most important RxJs operators and how Subjects can be handy. We have seen also how to use a functional programming style to increase the security of our code as well as its reusability.
這是一段相當長的旅程。 我們已經看到了一些最重要的RxJs運算符的用法以及如何方便使用Subject。 我們還看到了如何使用函數式編程風格來提高代碼的安全性和可重用性。
I hope I’ve been able to show how, using a reactive thinking approach to this problem, it is possible to build a software solution which very naturally mirrors a real life model for objects that are remotely controlled.
我希望我已經能夠展示出使用React性思考方法解決此問題的方法,從而有可能構建一個軟件解決方案,該解決方案非常自然地反映遠程控制對象的真實模型。
Any time you have to face a problem where time and asynchronicity play a role, then reactive thinking supported by reactive libraries such as RxJs can lead you to a simpler and more solid design. In this world of constant connectivity, the cloud, non-blocking platforms, and microservices, time and asynchronicity are going to play an ever-increasing role.
每當您不得不面對時間和異步性都起著作用的問題時,React性庫(例如RxJs)所支持的React性思維將使您的設計更簡單,更可靠。 在這個不斷連接的世界中,云,無阻塞平臺和微服務,時間和異步性將扮演越來越重要的角色。
If you liked what you have just read, you may be interested in reading also this article, where I describe how to build a distributed system to control and display in action multiple MobileObjects in a distributed environment.
如果您喜歡剛剛閱讀的內容,那么您可能也有興趣閱讀這篇文章 ,在此我將介紹如何構建一個分布式系統來控制和在分布式環境中實際顯示多個MobileObject。
The entire code base can be found here.
完整的代碼庫可以在這里找到 。
I want to thank Ben Lesh who inspired this piece with one of his talks.
我要感謝本·萊什(Ben Lesh) ,他的演講之一啟發了這篇文章。
翻譯自: https://www.freecodecamp.org/news/thinking-reactively-how-to-animate-with-movement-objects-using-rxjs-692518b6f2ac/
rxj熱血江hsf湖私服
總結
以上是生活随笔為你收集整理的rxj热血江hsf湖私服_如何使用RxJ进行React性思考和动画化移动对象的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: If you are using Web
- 下一篇: 生活就是忘记