Using requestAnimationFrame in React - reactjs

I am new to react native and I trying to optimize performance.
My Touch events are very slow and I was going through RN documentation on performance and they have mentioned to use requestAnimationFrame with this example
handleOnPress() {
// Always use TimerMixin with requestAnimationFrame, setTimeout and
// setInterval
this.requestAnimationFrame(() => {
this.doExpensiveAction();
});
}
Now, this description sounds very vague and hard for me to comprehend its usage
Like, I have this touchable event in my RN app
<TouchableWithoutFeedback onPress={() => this.touched(this.props.coinShortName)}>
Which calls this method
touched = (id) => {
this.setState({selectedPostId: id})
if (this.props.coinShortName == this.state.selectedPostId ) {
this.setState({stateToDisplay: !this.state.stateToDisplay})
}
}
Question: Can someone please tell me on how I need to/should use it in my app?

I'm going to convert my comment into an answer, so I can format it better and hopefully help someone in the future too. I don't expressly recommend marking my answer as correct, but I think this answer should be here alongside this question.
This article here should give you some backstory on requestAnimationFrame: http://www.javascriptkit.com/javatutors/requestanimationframe.shtml.
I would recommend reading the article I linked above and then read my answer after.
I will just explicitly mention that requestAnimationFrame could appear similar to setTimeout(() => {}, 0), but if you had a Zack Morris phone made in 1985, its "as soon as possible" might be 5 seconds later, thus making your animation look terrible, similar to when your character lags across the screen in a video game. The function may have been called at the correct time, but it did not actually execute at the correct time.
It can be helpful to imagine a collection phase and a rendering phase. I'm sorry, I don't know the exact terms for this stuff, but human eyes see smooth movement at I think 20 FPS, but what that means is you have 20 "frames", so it's like calculating something 20 times. It's like collecting a bunch of kids and pushing them into a bus 20 times per second. Pushing them into the bus is an event, and it's analogous to repainting your screen. Sometimes kids can get left behind and extra kids picked up next time, so you can imagine the gains to perceived smoothness of flow over time.
It's important to note that optimizations are being made with respect to the next repaint, or the next time the screen 'changes'. requestAnimationFrame does work under the hood to ensure the animation occurs at the correct time and is smooth, meaning the pixels were where they were supposed to be at the right time. (I think you would derive a lot of meaning if you checked out the definitions for "what is a janky animation", and look at some of the discussion around that. I mention that because we want to understand more about the repainting process and what kinds of things are important and why)
I recall that requestAnimationFrame can ditch calculations that would occur too late. For example, if you click the button and a pixel goes from 0% to 25% to 50% to 75% to 100% (some arbitrary distance calculation). We could say that after 1 second, the pixel should have travelled 50% of the distance and after 2 seconds, it should be at 100%, the final resting place.
It's more important that the pixels are in the correct place at the correct time than it is for them to travel to exactly every place they were supposed to. requestAnimationFrame is helping you do this. If the screen is about to repaint, and "it" needs to run a calculation that would take too long, "it" just ignores it and skips to the next frame. It's like trimming fat to keep on pace and therefore, avoid jank.
requestAnimationFrame is a solution for the same challenges whether it's in your web browser or iOS or Android. They all do this process of painting the screen over and over again. You could start calculating something needed for the next repaint but start too late so it's not done when the next repaint occurs.
Imagine your animation was smooth but your phone received 20 push notifications all of a sudden that bogged down the CPU, causing your animation to be delayed by 16.7 milliseconds. Rather than display the pixel at the correct place at the wrong time, requestAnimationFrame helps by making the pixel be in the correct place at the correct time, but it may do some magic and not even try to paint the pixel sometimes when it would have otherwise, thus saving performance and increasing perceived smoothness.
I just realized this is a wall of text, but I think it will be informational.
These repaints occur about 60 frames per second, so requestAnimationFrame could fire like 60 times a second when it calculates is the most optimal time. There are 1000 milliseconds in 1 second, so 60 FPS is one frame every 16.7ms. If the human eye perceives smoothness at 20FPS, then it means you could in theory repaint every 45ms or 30% as much, and the animation would still be smooth.
My definitions may be inaccurate, but I hope they can help give you a sense what is happening.

As mentioned , you need to wrap your expensive action in an instance of requestAnimationFrame.
Usage
<TouchableWithoutFeedback onPress={() => this.touched(this.props.coinShortName)}>
touched = (id) => {
requestAnimationFrame(() => {
this.setState({selectedPostId: id})
if (this.props.coinShortName == this.state.selectedPostId ) {
this.setState({stateToDisplay: !this.state.stateToDisplay})
}
});
}

<TouchableWithoutFeedback onPress={this.handlePress}>
handlePress = () => {
this.requestAnimationFrame(() => {
this.touched(this.props.coinShortName)
});
}
it would be event better if you removed the useless id parameter in the touched function by using directly this.props.coinShortName inside so you could write
handlePress = () => {
this.requestAnimationFrame(this.touched);
}
Anyway, the touched function doesn't seem to be really expensive so I don't know if it will solve your performance issue

Related

RxJS DOM pause observable while another "is dragging"?

UPDATE
I've tried to make a standalone version here: https://codepen.io/neezer/pen/pPRJar
It doesn't work quite like my local copy, but I'm hoping it similar enough that you can see where I'm trying to go.
I'm not getting quite the same behavior as well because I changed the listener target to document, which seemed to help some.
Also, I'm using RxJS v5 and the latest version of React.
Still getting the hang of RxJS...
I have two Observables: one subscribed to mouseover x coordinates on a table to show a resize column, and the other to allow the user to drag on that column.
Roughly speaking, the first one looks like this (all of the below defined in a componentDidUpdate lifecycle method in a React component):
Rx.DOM.mouseover(tableEl)
.map(/* some complicated x coordinate checking */)
.distinctUntilChanged()
.subscribe(/* setState call */)
That works great, and gives me this:
So now I want to provide the actual "drag" behavior, and I tried setting up a new Observable like so
// `resizerEl` is the black element that appears on hover
// from the previous observable; it's just a div that gets
// repositioned and conditionally created
Rx.DOM.mousedown(resizerEl)
.flatMap(md => {
md.preventDefault()
return Rx.DOM.mousemove(tableEl)
.map(mm => mm.x - md.x)
.takeUntil(Rx.DOM.mouseup(document))
})
.subscribe(/* do column resizing stuff */)
There are three problems with that:
Once I've done my first "drag", I can't do any more. My understanding is that takeUntil completes the Observable, and I'm not sure how I can "restart" it.
The mousemove from the first observable is still active while I'm dragging, so my black div will disappear once my x position changes enough to trigger that behavior.
The binding on the second Observable doesn't always seem to trigger (it's unreliable). I think there might be a race condition or something happening here because sometimes I'll refresh the page and I'll get the drag once (from #1), and other times I won't get it at all.
Note at first after a clean refresh I can't drag the handle (#3), then I refresh, and I can't drag the handle past the bounds setup from the first Observable--and the black resizer bar disappears and reappears as my mouse's x coordinate enters and leaves that envelope (#2).
I've been head-banging on this for quite some time now and would really appreciate any insight as to what I'm doing wrong here. In short, I want
the first Observable to "pause" when I'm dragging, then resume when I'm done dragging
the second Observable to not "complete" (or "restart") once a drag is done
the second Observable to reliably work
As I mentioned earlier, I currently have this logic setup in a React component's componentDidUpdate lifecycle method, the shape of which looks roughly like this:
componentWillUpdate() {
// bail if we don't have the ref to our table
if (!tableEl) {
return;
}
// try not to have a new Observable defined on each component update
if (!this.resizerDrag$ && this.resizer) {
this.resizerDrag$ = // second Observable from above
}
// try not to have a new Observable defined on each component update
if (!this.resizerPos$) {
this.resizerPos$ = // first Observable from above
}
}
I've played around with this a bit now, I don't think this answer will be complete, but I'd like to share my insights. Hopefully a more advanced RxJS mind will chime in, and we can all work together to figure it out :).
I recreated a "lite-er" version of this in CodePen, using some light jQuery manipulation as opposed to React. Here's what I have so far:
"the first Observable to "pause" when I'm dragging, then resume when I'm done dragging"
Solving the first point helps with the other two. Based on what I had to do to get my resizerEl, I get the feeling it is rendered in the render method of the component based on something in this.state. If this is true, that means that when the first observable still has the ability to create and destroy resizerEl even while the second observable is listening. This means that resizerEl will no longer be able to generate any events, even though the observable doesn't complete until you've moused up.
In my case, I noticed that if you moved the mouse fast enough to go outside of width of what you were trying to drag, it would eliminate resizerEl, which is of what we want, but not while we're trying to drag something!
My solution: I introduced another variable to the "state" of the "component". This would set to true when we moused down on resizerEl, and then false when we moused up again.
Then we use switchMap.
Rx.DOM.mousemove(tableEl)
.switchMap(function(event) {
return this.state.mouseIsDown ? Rx.Observable.never() : Rx.Observable.of(event);
})
.map(...
There's probably a better way to do it rather than just sticking event back in an Observable, but this was the last part of it I worked on and my brain is kind of fried hehe. The key here is switching to Observable.never while the mouse is down, that way we don't go any further down the operator chain.
Actually, one nice thing is that this may not even need to be put in this.state, since that would cause a re-render. You can probably just use an instance variable, since the variable is only essential to the Observables functionality, and not any rendering. So, using this.mouseIsDown would be just as good.
How do we handle the mouse being down or up?
Part 1:
...
Rx.DOM.mousedown(resizerEl)
.do(() => this.mouseIsDown = true)
Better to abstract this to a function of course, but this is the gist of what it does.
Part 2:
...
return Rx.DOM.mousemove(tableEl)
.map(mm => mm.x - md.x)
.takeUntil(Rx.DOM.mouseup(document))
.doOnCompleted(() => this.mouseIsDown = false)
Here we take advantage of doOnComplete to perform this side-effect once the observable has completed, which in this case, would be on mouseup.
"the second Observable to not "complete" (or "restart") once a drag is done"
Now here's the tricky one, I never ran into this problem. You see, every time Rx.DOM.mousedown(resizerEl) emits an event, inside of flatMap, a new Observable is created each time with return Rx.DOM.mousemove(tableEl).... I used RxJS 4.1 when making this, so it's possible that there could be behavioral differences, but I found that just because the inner observable completed didn't mean the outer one would complete as well.
So what could be happening? Well, I'm thinking that since you're using React, that resizerEl is being created/destroyed respectively when the component is rendering. I haven't seen the rest of your code of course, but please correct me if I'm wrong about this assumption.
This wasn't a problem for me because, for the sake of simplicity, I simply re-used the same element as a dragger, only hiding it when I wasn't hovering over a draggable element.
So the important question is: how is resizerEl being defined and used in your component? I'm assuming the actual reference to it is made using, well, a ref. But if it that DOM element is ever destroyed or recreated, then the Rx.Dom binding needs to be repeated all over again.
I see you're doing this with componentDidUpdate. However, the Rx.Dom.mousedown event may still be bound to an old copy of the ref to resizerEl. Even if the component destroys the resizer in the DOM, and sets the ref (I assume that is this.resizer) to null or undefined, that does not destroy the Observable that is bound to that element. In fact, I don't even think it removes it from memory, even if it's removed from the DOM! That means that this.resizerDrag$ will never evaluate to false/null, and it will still be listening to an element that is no longer in the DOM.
If that is the case, something like this in componentWillUpdate might help:
if (!this.resizerDrag$ && this.resizer) {
this.resizerDrag$ = // second Observable from above
}
else if (!this.resizer && this.resizerDrag$) {
this.resizerDrag$ = null;
}
This will remove the Observable if the resizer object ceases to exist, that way we can properly reinitialise it upon it's return. There's a better to way to do with Subjects, keeping the subscription to one subject and just subscribing the subject to different mousedown streams once they become available, but let's keep this simple :).
This is something where we'd have to see the rest of your code (for this component) tell what's going on, and figure how to address it. But, my hypothesis is that you'd need to intentionally destroy the Observable if this.resizer is ever removed.
the second Observable to reliably work
Pretty sure that once the above two issues work, this one goes away. Nice and easy!
The CodePen
Here is the very naive mockup I made of this problem:
https://codepen.io/anon/pen/KmapYZ
Drag the blue circles back and forth along the X axis. (Has some little problems and bugs unrelated to the scope of this question, so I'm not worried about them.)
I made some slight changes of course just to keep it in-step with the more dumb downed approach I used. But all the concepts are there, as well as most of the code you wrote, modified to match this approach.
As I mentioned before, I didn't encounter the problem of the dragging only working once, so this better demonstrates the solution to pausing the first Observable. I re-use the dragging element, which I assume is why I didn't run into the 'drag-only-once' problem.
I hope that you or anyone else can comment with some improvements to this approach, or just show us a better (potentially more idiomatic) approach.

How to avoid timer stacking

I'm sorry if his question has already been asked in some way.
I'm currently re-writing a vibration driver in a Linux kernel. The reason why I changed it was due to overly strong vibrations caused by gaining a specific velocity of the vibration motor. To work that around I've implemented a PWM like controller that simply stops the motor at a specific time before reaching it's max acceleration, finally it keeps repeating this action.
There is one big issue that is mainly noticeable when using the keyboard. If the vibrator gets toggled too often in a very short time, the timer tends to stack up times, causing lag and vibration delays. This flaw can be easily achieved when typing multiple keys at once.
To demonstrate you this event visually I created a small graph.
The red area indicates timer overlap. The overlap between vibration 1 and 2 causes a delay for the second vibration, moving it out of place.
My main idea to prevent this issue is to merge vibrations into one if the previous vibration hasn't finished yet. For instance vibration 2 would simply join vibration 1.
Another way would be to simply use a single vibration for stacked vibrations, for instance, vibration 2 could simply use the last remaining bit of vibration 1. Why would this work? Well because the vibration controller that I've implemented only applies to times under 100ms, meaning vibration time differences would not be noticeable if one was to spam to keystrokes at once, instead both keystrokes should form and share single vibration.
Finally to my question, how could I make a function check itself it it's being called again. Or at least add a time for the function to check if keystrokes are being spammed multiple times in a short period?
int foo ()
{
static foo_counter;
if(foo_counter)
{
// If function has been called again
}
return(0);
foo_counter++;
}

How do I reliably pause the state of a game?

So I have a couple instances where I want to be able to 'freeze' the state of my game. It's a top-down scroller, and I want to give the player the ability to pause the scrolling of the screen for a short time by using a powerup (if you fall to the bottom of the screen you die). I also want to pause the game as it is starting, and draw a 3, 2, 1, go! to give the player time to get ready, because right now as soon as you hit play, the screen starts scrolling.
I have been using Timer to accomplish this, however it doesn't work if I want to freeze the screen on consecutive occasions. Like if a player uses a freeze, the screen sucesssfully freezes, but if they quickly use another freeze, it doesn't work. There seems to be an unintended cool-down. I have a similar problem for the 'intro delay' I explained earlier. For some reason it only works on the first 2 levels. Here is how I am using Timer.
if(gameState != STATE.frozen) {
camera.translate(0, (float) scrollSpeed);
staminaBar.setPosition(staminaBar.getX(), (float) (staminaBar.getY()+scrollSpeed));
staminaMeter.setPosition(staminaMeter.getX(), (float) (staminaMeter.getY()+scrollSpeed));
healthBar.setPosition(healthBar.getX(), (float) (healthBar.getY()+scrollSpeed));
healthMeter.setPosition(healthBar.getX(), (float) (healthMeter.getY()+scrollSpeed));
boostBar.setPosition(boostBar.getX(), (float) (boostBar.getY()+scrollSpeed));
boostMeter.setPosition(boostMeter.getX(), (float) (boostMeter.getY()+scrollSpeed));
screenCeiling += (float) scrollSpeed;
screenFloor += (float) scrollSpeed;
}
else {
Timer.schedule(new Task() { //freeze the screen for 5 seconds
#Override
public void run() {
gameState = STATE.playing;
}
}, 5);
}
From what I understand, it waits 5 second before resuming the game to the 'playing' state. But like I said, this only works when activated between large intervals and I don't know why. Is there a better way I can be doing this?
As for the intro delay, this may be a question better asked seperate, but I use the same method, but it doesn't let me draw sprites over my tiledmap, so if anyone knows how to do that please include it in your response
Assuming the code you posted is in your render loop, then whenever you are not in the frozen state, you are creating a new timer task on every frame. So if you freeze for 5 seconds and your game is running at 60fps, you will create 300 timer tasks, each of which is going to force the game to go back to playing state. The last one won't fire until 5 seconds after the first one fires, so there will be a five second "cooldown" during which you cannot change the state to anything besides playing, because there will be another timer task firing on every frame during that time.
You need to ensure that you only create one timer task, only when you first enter frozen state.
I do have a suggestion...instead of using a state to freeze the game, use a variable that's multiplied by scrollSpeed. Change that variable from one to zero when the player uses the powerup. Then you can do fancy stuff like quickly interpolating from one to zero so the speed change isn't so abrupt. And it will probably make your code simpler since there would be one less state that must be handled differently in the algorithm.
Check your gameState variable in the render method and if the game is playing, then update the game as usual and draw it.
If the game is not playing then skip the game's update method and create a time delay from the current time:
endTime = TimeUtils.millis()+5000;
Then each time through the render method check to see if current time is greater than the end time. When the current time is past your delay time, set gameState back to playing and have the game go back to updating.
You'll have to have another boolean flag so you only set the endTime once (you don't want to keep resetting this each time through the render loop), or if "STATE" is an enum, then include an option for "justPaused" for the exact frame that you pause the game, set the end time, then set STATE to "notPlaying".
You can also use this to create an alternative "update" method where you can update your countdown sprites, but not update the game itself. When the game is playing this other update method will be skipped.

XNA 2D Deformed Terrain Collision Detection without GetData()

I'm currently working on a Worms game which involves terrain deformation. I used to do it with .GetData, modifying the color array, then using .SetData, but I looked into changing it to make the work done on the GPU instead (using RenderTargets).
All is going well with that, but I have come into another problem. My whole collision detection against the terrain was based on a Color array representing the terrain, but I do not have that color array anymore. I could use .GetData every time I modify the terrain to update my Color array, but that would defeat the purpose of my initial changes.
What I would be okay with is using GetData once at the beginning, and then modifying that array based on the changes I make to the terrain later on by some other means. I do not know how I would do this though, can anyone help?
I've done a bit of research, and I have yet to find a solution to getting rid of any GetData calls every time my terrain is modified, but I have found ways to "optimize" it, or at least reduce the GetData calls as much as possible.
Crater drawing is batched, meaning that rather than draw each one as it’s created, I add them to a list and draw all of them every few frames. This reduces the number of GetData calls – one per batch of craters rather than one per crater.
After drawing craters to the render target, I wait a few frames before calling GetData to make sure the GPU has processed all of the drawing commands. This minimizes pipeline stalls.
If I have a pending GetData call to make and more craters come in, the craters will stay batched until the GetData call is complete. In other words, the drawing and getting are synchronized so that a GetData call always happens several frames after drawing a batch of craters, and any new crater draw requests wait until after a pending GetData.
If anyone else has any other suggestions I would still be glad to hear them.

silverlight math performance question

Is there some reason that identical math operations would take significantly longer in one Silverlight app than in another?
For example, I have some code that takes a list of points and transforms them (scales and translates them) and populates another list of points. It's important that I keep the original points intact, hence the second list.
Here's the relevant code (scale is a double and origin is a point):
public Point transformPoint(Point point) {
// scale, then translate the x
point.X = (point.X - origin.X) * scale;
// scale, then translate the y
point.Y = (point.Y - origin.Y) * scale;
// return the point
return point;
}
Here's how I'm doing the loop and timing, in case it's important:
DateTime startTime = DateTime.Now;
foreach (Point point in rawPoints) transformedPoints.Add(transformPoint(point));
Debug.Print("ASPX milliseconds: {0}", (DateTime.Now - startTime).Milliseconds);
On a run of 14356 points (don't ask, it's modeled off a real world number in the desktop app), the breakdown is as follows:
Silverlight app #1: 46 ms
Silverlight app #2: 859 ms
The first app is an otherwise empty app that is doing the loop in the MainPage constructor. The second is doing the loop in a method in another class, and the method is called during an event handler in the GUI thread, I think. But should any of that matter, considering that identical operations are happening within the loop itself?
There maybe something huge I'm missing in how threading works or something, but this discrepancy doesn't make sense to me at all.
In addition to the other comments and answers I'm going to read between the lines a little.
In the first app you have pretty much this code in isolation running in the MainPage constructor. IWO you've create a fresh Silverlight app and slapped this code in it and thats it.
In the second app you have more actual real world stuff. At the very least you have this code running as the result of a button click on a rudimentory UI. Therein lies the clue.
Take a blank app and drop a button on it. Run it and click the button, what does the button do? There are animations attached to visual states of the button. This animation (or other animations or loops) are likely running in parrallel with your code when you click the button. Timers (whether you do it properly with StopWatch or not) record elapsed time, not just the time your thread takes. Hence when other threads are doing other things (like animations) your timing will be off.
My first suspicion would be that Silverlight App #2 triggers a garbage collection. Scaling ~15,000 points should be taking a millisecond, not nearly a second.
Try to reduce memory allocations in your code. Can transformedPoints be an array, rather than a dynamically grown data structure?
You can also look at the GC performance counters, but simply reducing the memory allocation may turn out to be simpler.
Could it be possible your code is not being inlined in the CLR by the app that is running slower?
I'm not sure how the CLR in SL handles inlining, but here is a link to some of the prerequisites for inlining in 3.5 SP1.
http://udooz.net/blog/2009/04/clr-improvements-in-net-35-sp1/

Resources