How do you set periodic timers in d3.js? - d3.js

I want to run a particular function every 5 minutes. If I write code like this:
function f() {
console.log("hi");
d3.timer(f, 5*60*1000);
return true;
}
d3.timer(f, 5*60*1000);
then f seems to run once and then never again.
I achieved the desired behavior by creating a clone of f called f2: f calls d3.timer(f2) and f2 call d3.timer(f). This seems like an ugly hack. Is there a better way?

I think #nrabinowitz's answer is probably the best and simplest, but if you'd really like to use d3.timer, here's how you'd do it.
var interval = 1000; // one second in milliseconds
var makeCallback = function() {
// note that we're returning a new callback function each time
return function() {
console.log('OH HAI!!');
d3.timer(makeCallback(), interval);
return true;
}
};
d3.timer(makeCallback(), interval);
Your code isn't working as expected because d3.js maps timers to function instances (see the code here: https://github.com/mbostock/d3/blob/master/d3.v2.js#L4073), so your code was doing the following:
set timer with callback f()
f() is called after five minutes
f() logs to the console, creates a new timer which also uses f() as its callback, and then cancels that timer by returning true.
The code in my answer solves the problem by returning a new function instance each time.
Of course, this is way more complicated and harder to understand that just using setInterval, so you should do that.

This sounds like a job for the standard JavaScript setInterval() method:
setInterval(f, 5*60*1000);
If you need it to run an animation at each invocation, that's where d3.timer would be useful - otherwise, the standard setInterval and setTimeout methods are likely to be easier.

Looks like d3.interval is a thing, and is meant to be a replacement for setInterval: https://github.com/d3/d3-timer#interval
var interval = d3.timeout(callback, interval_time, optional_delay);

Related

Chaining animations in SwiftUI

I'm working on a relatively complex animation in SwiftUI and am wondering what's the best / most elegant way to chain the various animation phases.
Let's say I have a view that first needs to scale, then wait a few seconds and then fade (and then wait a couple of seconds and start over - indefinitely).
If I try to use several withAnimation() blocks on the same view/stack, they end up interfering with each other and messing up the animation.
The best I could come up with so far, is call a custom function on the initial views .onAppear() modifier and in that function, have withAnimation() blocks for each stage of the animation with delays between them. So, it basically looks something like this:
func doAnimations() {
withAnimation(...)
DispatchQueue.main.asyncAfter(...)
withAnimation(...)
DispatchQueue.main.asyncAfter(...)
withAnimation(...)
...
}
It ends up being pretty long and not very "pretty". I'm sure there has to be a better/nicer way to do this, but everything I tried so far didn't give me the exact flow I want.
Any ideas/recommendations/tips would be highly appreciated. Thanks!
As mentioned in the other responses, there is currently no mechanism for chaining animations in SwiftUI, but you don't necessarily need to use a manual timer. Instead, you can use the delay function on the chained animation:
withAnimation(Animation.easeIn(duration: 1.23)) {
self.doSomethingFirst()
}
withAnimation(Animation.easeOut(duration: 4.56).delay(1.23)) {
self.thenDoSomethingElse()
}
withAnimation(Animation.default.delay(1.23 + 4.56)) {
self.andThenDoAThirdThing()
}
I've found this to result in more consistently smoother chained animations than using a DispatchQueue or Timer, possibly because it is using the same scheduler for all the animations.
Juggling all the delays and durations can be a hassle, so an ambitious developer might abstract out the calculations into some global withChainedAnimation function than handles it for you.
Using a timer works. This from my own project:
#State private var isShowing = true
#State private var timer: Timer?
...
func askQuestion() {
withAnimation(Animation.easeInOut(duration: 1).delay(0.5)) {
isShowing.toggle()
}
timer = Timer.scheduledTimer(withTimeInterval: 1.6, repeats: false) { _ in
withAnimation(.easeInOut(duration: 1)) {
self.isShowing.toggle()
}
self.timer?.invalidate()
}
// code here executes before the timer is triggered.
}
I'm afraid, for the time being, there is no support for something like keyframes. At least they could have added a onAnimationEnd()... but there is no such thing.
Where I did manage to have some luck, is animating shape paths. Although there aren't keyframes, you have more control, as you can define your "AnimatableData". For an example, check my answer to a different question: https://stackoverflow.com/a/56885066/7786555
In that case, it is basically an arc that spins, but grows from zero to some length and at the end of the turn it progressively goes back to zero length. The animation has 3 phases: At first, one end of the arc moves, but the other does not. Then they both move together at the same speed and finally the second end reaches the first. My first approach was to use the DispatchQueue idea, and it worked, but I agree: it is terribly ugly. I then figure how to properly use AnimatableData. So... if you are animating paths, you're in luck. Otherwise, it seems we'll have to wait for the possibility of more elegant code.

AJAX and Leaflet: Inspect feature properties before styling/adding to map

I'm using leaflet-ajax to load geoJSON on demand. I want to find the maximum theProperty value so I can use that to scale the feature's fill colors before I add them to the map.
Here's my general approach:
function maxBinPropertyValue(theProperty) {
var theGeoJson = null;
var maxPropertyValue = 0;
var propertyValue = null;
var theGeoJson = new L.GeoJSON.AJAX(binsFileName());
theGeoJson.on('data:loaded', function() {
console.log('The data is loaded');
theGeoJson.eachLayer(function(layer) {
console.log('Looping through the layers');
propertyValue = feature.properties[theProperty];
if (propertyValue > maxPropertyValue) {
maxPropertyValue = propertyValue;
console.log('Max so far: ' + maxPropertyValue);
};
});
});
theGeoJson = null;
console.log('The final maximum value: ' + maxPropertyValue);
return maxPropertyValue;
};
I'm trying to wait for the data:loaded event, then loop through all the features to find the maximum value of theProperty, which is returned to the calling routine.
Except it doesn't work. The first console.log says 'The data is loaded'. The second and third console.logs are never reached, and the fourth and final one reports a value of 0 for maxPropertyValue.
How can I examine all the features in a featureset before styling them, in a way guaranteed to not have asynchronous problems?
PS: I'm pretty sure I can't use onEachFeature: instead of the above approach, because I need to examine every feature's property to determine the maximum value in the set before I can style any of the features.
As for your issue about inspecting your data and retrieving the maximum value, you are indeed facing the classic asynchronous concept of JavaScript.
See How do I return the response from an asynchronous call?
Asynchronism is a problem if not dealt with properly, but an advantage if correctly handled.
To put the concept shortly, you do not manage asynchronism in a "standard" sequential way, but you should rather consider parts of code (callbacks) that are executed at a later time based on events.
Whenever you provide a function as an argument, it is certainly a callback that will be executed at a later time, but very probably much later than the next instructions.
So in your case, your 2nd and 3rd console.log are within a callback, and will be executed once your data is loaded, which will happen much later than your 4th console.log.
As for your next step (styling and adding to map), you actually do not need to perform an extra AJAX call, since you already have all data available in theGeoJson variable. You simply need to refactor / restyle it properly.
It is a good approach to break your problem in small steps indeed.
Good luck!
PS: that being said, ES7 provides async and await functionalities that will emulate a sequential execution for asynchronous functions. But to be able to use those, you need latest browser versions or transpilation, which is probably more work to learn and configure as of today for a beginner than understanding how to work with async JS.
I also had this problem and had to wrap my head around this, so giving an explicit example for solution here;
// make a request with your "url"
var geojsonLayer = new L.GeoJSON.AJAX("url");
// define your functions to interact with data
function thingToDoBeforeLoadingStarts () {
// do stuff
}
function thingToDoForEachFileDownloaded () {
// do stuff
}
function thingToDoAfterAllDownloadEnds () {
// do stuff
}
// attach listeners
geojsonlayer.on("data:loading",thingToDoBeforeLoadingStarts);
geojsonLayer.on("data:progress",thingToDoForEachFileDownloaded)
geojsonLayer.on("data:loaded",thingToDoAfterAllDownloadEnds);

Controlling animations in an animator via parameters, in sequences

So I am animating an avatar, and this avatar has its own animator with states and such.
When interacting with props, the props itself has an animator with states in it. In both case, I transition to some animations through parameters in the animator (bool type).
For example, for a door, the character will have "isOpeningDoor", while the door will have "isOpen".
Now the question: when I change the value on an animator on GO1, and then change the bool on GO2; do the first animation finish and then the second start? Because in my case, it does not happen; they start almost at the same time.
void OnTriggerEnter (collider door)
{
if (door.gameObject.tag=="door")
{
GOAnimator1.SetBool("isOpeningDoor", true);
GOAnimator2.SetBool("isOpen", true);
}
}
I believe that I am doing it wrong, since I change the parameter on the animator, but I do not check for the animation to end; is this even possible or am I doing something not kosher?
I really think it might be doable!
As you have it in your code now, the animations on GO1 and GO2 start at almost the same time because that's how it's written. The OnTriggerEnter() function will complete the execution in the frame it is called, and return the control to Unity.
What I think that might help you are coroutines and sendMessage between gameobjects:
http://docs.unity3d.com/Manual/Coroutines.html
http://docs.unity3d.com/ScriptReference/GameObject.SendMessage.html
The idea is to:
Create a coroutine in GO2 that waits an amount of time until it sets the GOAnimator2 variable to activate the door animation.
Create a function in GO2 that calls the aforementioned coroutine
From the OnTriggerEnter() send a message to GO2 to execute the newly created function
It reads complicated, but it's fairly simple. The execution would be like this:
1.Code for the coroutine:
function GO2coroutine(){
float timeToWait = 0.5f; //Tweak this
for ( float t = 0f; t < timeToWait; t+=time.deltaTime)
yield;
GetComponent<Animator>().SetBool("isOpen",true);
}
Code for the function calling it:
function callCoroutine() {
StartCoroutine("Fade");
}
And the code modification for your OnTriggerEnter():
void OnTriggerEnter (collider door)
{
if (door.gameObject.tag=="door")
{
GOAnimator1.SetBool("isOpeningDoor", true);
GO2.SendMessage("callCoroutine");
}
}
I didn't have a chance to test the code, so please don't copy paste it, there might be slight changes to do.
There is another way, but I don't like it much. That is making the animation longer with an idle status to wait for the first game object animation to end... but it will be a hassle in case you shorten the animation because you have to, or have any other models or events.
Anyway, I think the way to go is with the coroutine! Good Luck!

wxTimerEvent does not always call at the expected interval

I am using wxTimerEvent() to call a function at a certain frequency. But it does not always call the function as expected.
For example, it works fine for a random time duration, and then it does not call the function at all for a few seconds. SHere is my block of code. Please help me figure out this issue...
class MyFrame : public wxFrame
{
public:
...
void OnTimer(wxTimerEvent& event);
private:
wxTimer *mTimer
wxDECLARE_EVENT_TABLE();
};
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_TIMER(TIMER_ID, MyFrame::OnTimer)
wxEND_EVENT_TABLE()
MyFrame::MyFrame()
{
mTimer = new wxTimer(this, TIMER_ID)
m_timer.Start(100); // 100 millisecond interval
}
void MyFrame::OnTimer(wxTimerEvent& event)
{
// my logic -this is working fine... just set a flag.
}
Timers are not guaranteed to fire at exactly the specified interval, but for a 100ms timer to not be called for several seconds is not normal at all. I suspect your code doesn't return to the event loop, preventing it from dispatching the timer events. If you're sure this is not the case, you would really need to produce an example reproducing the problem (ideally the smallest possible change to the wxWidgets minimal sample) and open a bug report about it (please be sure to mention your wxWidgets version and platform if you do this).

Basic syntax for an animation loop?

I know that jQuery, for example, can do animation of sorts. I also know that at the very core of the animation, there must me some sort of loop doing the animation. What is an example of such a loop?
A complete answer should ideally answer the following questions:
What is a basic syntax for an effective animation recursion that can animate a single property of a particular object at a time? The function should be able to vary its target object and property of the object.
What arguments/parameters should it take?
What is a good range of reiterating the loop? In milliseconds? (Should this be a parameter/argument to the function?)
REMEMBER:
The answer is NOT necessarily language specific, but if you are writing in a specific language, please specify which one.
Error handling is a plus. {Nothing is more irritating (for our purposes) than an animation that does something strange, like stopping halfway through.}
Thanks!
typically (for jQuery at least) this is not done in a loop, but rather in a series of callbacks.
pseudojavascript:
function startAnimation(element, endPosition, duration) {
var startPosition = element.position;
var startTime = getCurrentTime();
function animate() {
var timeElapsed = getCurrentTime() - startTime;
if (timeElapsed > duration) {
element.position = endPosition;
stopTimer();
} else {
// interpolate based on time
element.position = startPosition +
(endPosition - startPosition) * timeElapsed / duration;
}
}
startRepeatingTimerWithCallbackAndInterval(animate, 1.0 / 30.0);
}
It's also possible to use objects to store starting data instead of closures.
This doesn't completely answer all the points in the question, but it's a starting point.

Resources