Coder Social home page Coder Social logo

d3-timer's Introduction

d3-timer

This module provides an efficient queue capable of managing thousands of concurrent animations, while guaranteeing consistent, synchronized timing with concurrent or staged animations.

Resources

d3-timer's People

Contributors

dependabot[bot] avatar fil avatar konradklimczak avatar mbostock avatar mef avatar stof avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

d3-timer's Issues

d3.interval().restart() restarts an interval as a timer instead

I ran into an issue with d3.interval not working as expected when calling restart. If I create a new interval, it works as expected -- firing the callback every delay milliseconds:

  const intervalCallback = function(t) {
    console.log('interval', t);
    if (t > 5000) intervalTest.stop();
  };
  const intervalTest = d3.interval(intervalCallback, 500);

This outputs:

> interval 515.0400000275113
> interval 1000.9299999801442
> interval 1503.1099999905564
> interval 2000.0850000069477

If I call restart on this instance, passing the same callback and delay, I would expect intervalTest to restart following the same behavior. However, it appears to start a timer (or an interval with 0ms delay) instead.

intervalTest.restart(intervalCallback, 500);

Outputs:

> interval 16.02500001899898
> interval 32.765000010840595
> interval 48.30000002402812
> interval 66.045000043232
> interval 82.05500000622123
...

And so on until the callback reaches its stopping point.

If I'm actually misunderstanding and this isn't how interval is supposed to be used, perhaps something could be added to the docs to clarify?

Let me know if you have any questions, and thanks for your work on this module!

Doighlight another **d3.interval**:

The current description of d3.interval:

Like timer, except the callback is invoked only every delay milliseconds; if delay is not specified, this is equivalent to timer. A suitable replacement for setInterval that is guaranteed to not run in the background. The callback is passed the elapsed time.

It might be also worth adding that unlike setInterval it is also not trying to catch up, which depending on the use good case might be a good thing or not (in my experience it's always better this way and relying on the catch-up behavior is IMHO likely a sign of a suboptimal approach).

Example that uses d3

This block is a popular example for d3.timer. However, it uses embedded code that isn't 100% compatible with the library.

I've updated a fork of the example here to use the actual library.

(I realize that I'm filing a bug for a seemingly unrelated gist, but it's the defacto example for this library)

D3 timer is behaving erratically with elapsed time even going backward.

I don't know if it is my browser or monitor or computer system but something is way off.

on this site. http://using-d3js.com/08_04_timers.html

I went to find out about interval. when I change the ms values to a lower value they just go fast and then you can't reset it. you have to refresh the browser. It could just be a bug on his webpage.

However, on my system I am trying to use it and look at the logs of what the elapsed time is returning time. It is like it is going backward in time. How is this possible. Is this a bug or just my system. I am developing on my localhost but still this seems very erratic

app.component.ts:626 TIGER 7 MADEN  94952.90000003576
00:18:50.260 app.component.ts:627 TIGER 8 MADEN  94944
00:18:50.261 app.component.ts:626 TIGER 7 MADEN  92480.40000003576
00:18:50.261 app.component.ts:627 TIGER 8 MADEN  92471.5
00:18:50.261 app.component.ts:626 TIGER 7 MADEN  7247.5
00:18:50.261 app.component.ts:627 TIGER 8 MADEN  7238.599999964237
00:18:50.270 app.component.ts:626 TIGER 7 MADEN  94962.40000003576
00:18:50.270 app.component.ts:627 TIGER 8 MADEN  94952.90000003576
00:18:50.270 app.component.ts:626 TIGER 7 MADEN  92489.90000003576
00:18:50.271 app.component.ts:627 TIGER 8 MADEN  92480.40000003576
00:18:50.271 app.component.ts:626 TIGER 7 MADEN  7257
00:18:50.271 app.component.ts:627 TIGER 8 MADEN  7247.5
00:18:50.281 app.component.ts:626 TIGER 7 MADEN  94973.30000001192
00:18:50.281 app.component.ts:627 TIGER 8 MADEN  94962.40000003576
00:18:50.281 app.component.ts:626 TIGER 7 MADEN  92500.80000001192
00:18:50.281 app.component.ts:627 TIGER 8 MADEN  92489.90000003576
00:18:50.282 app.component.ts:626 TIGER 7 MADEN  7267.899999976158
00:18:50.282 app.component.ts:627 TIGER 8 MADEN  7257
00:18:50.289 app.component.ts:626 TIGER 7 MADEN  94981.5
00:18:50.289 app.component.ts:627 TIGER 8 MADEN  94973.30000001192


app.component.ts:627 TIGER 8 MADEN  192203.5
00:20:27.520 app.component.ts:626 TIGER 7 MADEN  189739.10000002384
00:20:27.520 app.component.ts:627 TIGER 8 MADEN  189731
00:20:27.520 app.component.ts:626 TIGER 7 MADEN  104506.19999998808
00:20:27.520 app.component.ts:627 TIGER 8 MADEN  104498.09999996424
00:20:27.528 app.component.ts:626 TIGER 7 MADEN  192220
00:20:27.528 app.component.ts:627 TIGER 8 MADEN  192211.60000002384
00:20:27.528 app.component.ts:626 TIGER 7 MADEN  189747.5
00:20:27.528 app.component.ts:627 TIGER 8 MADEN  189739.10000002384
00:20:27.528 app.component.ts:626 TIGER 7 MADEN  104514.59999996424
00:20:27.528 app.component.ts:627 TIGER 8 MADEN  104506.19999998808
00:20:27.536 app.component.ts:626 TIGER 7 MADEN  192228.40000003576
00:20:27.536 app.component.ts:627 TIGER 8 MADEN  192220
00:20:27.536 app.component.ts:626 TIGER 7 MADEN  189755.90000003576
00:20:27.536 app.component.ts:627 TIGER 8 MADEN  189747.5
00:20:27.537 app.component.ts:626 TIGER 7 MADEN  104523
00:20:27.537 app.component.ts:627 TIGER 8 MADEN  104514.59999996424
00:20:27.544 app.component.ts:626 TIGER 7 MADEN  192236.69999998808
00:20:27.545 app.component.ts:627 TIGER 8 MADEN  192228.40000003576
00:20:27.545 app.component.ts:626 TIGER 7 MADEN  189764.19999998808
00:20:27.545 app.component.ts:627 TIGER 8 MADEN  189755.90000003576
00:20:27.545 app.component.ts:626 TIGER 7 MADEN  104531.29999995232
00:20:27.545 app.component.ts:627 TIGER 8 MADEN  104523
00:20:27.553 app.component.ts:626 TIGER 7 MADEN  192245.19999998808
00:20:27.553 app.component.ts:627 TIGER 8 MADEN  192236.69999998808
00:20:27.553 app.component.ts:626 TIGER 7 MADEN  189772.69999998808
00:20:27.553 app.component.ts:627 TIGER 8 MADEN  189764.19999998808
00:20:27.554 app.component.ts:626 TIGER 7 MADEN  104539.79999995232
00:20:27.554 app.component.ts:627 TIGER 8 MADEN  104531.29999995232
00:20:27.561 app.component.ts:626 TIGER 7 MADEN  192253.40000003576
00:20:27.561 app.component.ts:627 TIGER 8 MADEN  192245.199999988

Timer object memory leak

Hello,

i seem to have memory leak from using the transition which uses the Timer object.
it's seems to me like it doesn't clean it though i dont keep references to any transition.

I built a live updating graph with d3.js v4.4 and angularjs 1.4.8 displaying nodes and connections which uses transition on updates.

i found out i have memory leaks on my application whether it stays open when i'm on the tab & not on the tab for a long time, i also check with task manager.

i added a screenshot showing my memory heap snap shoot, the Timer object seems to not be cleaning up and i'm really stuck about this.
Heap snapshot
an update flow example:

$interval(() => {
    getSnapshot()
}, 30)

function getSnapshot(timestamp){
    let snapshotTs = timestamp ? timestamp : moment()
    getSnapshotFromServer(snapshotTs).then(data=>{
        graph = data.graph //contains all nodes and connections
        buildGraph(graph)
    })
}

buildGraph(){
    //build graph with connections and nodes using transition
    //we use tr
}

d3.stop() should return the time it was stopped at

I was looking into how I can gracefully resume a timer that might have been stopped and thought this would have been a natural choice, but noticed the function didn't return anything. This is helpful for pausing and resuming mid-way through an animation. If I restarted the timer, it will likely interrupt the timer and restart the animation.

const hello = (elapsedTime) => console.log("hello world", elapsedTime)
const timer = d3.timer(hello)

setTImeout(() => {
    const stoppedTime = timer.stop()
    setTImeout(() => {
        timer.restart(hello, null, stoppedTime)  
     }, 5000)
}, 5000)

Is there a way to achieve something similar?

when transitions are triggered in inactive tabs they accumulate and can cause page freeze

A little background:

The app I'm working is used for monitoring. A common use case is to have a tab open for days.
I noticed that when a tab containing a d3 component becomes active again after a long time being inactive it causes the page to freeze.

I've managed to create a minimal test case demonstrating this: https://bl.ocks.org/guykr/5072b773cd67e6b0bff61dc00d6418f1

A note about why it's implemented this way:

I hijack the native requestAnimationFrame and simulate the tab being inactive to speed up the manifestation of this issue (Since the browser also throttles setInterval calls), but this can be reproduces without these measures by setting up a setInterval loop that schedules transitions, switching to another tab and then waiting for ~1 hour or so.

I'm aware the test case is extreme, but this happens in our app with much slower updates (and longer inactive periods)

Any ideas how this can be mitigated?

Thanks,
Guy

*not sure if this should go to d3-transition or here, but it seems related to #17

pause / resume timer

In addition to the stop method, it would be useful to be able to pause and resume a timer.

Do you think that these functions would fit inside d3-timer?

"Invalid calling object" in Edge browser.

Hello,

I use victory library (for generating chart based on d3.js) and on unmounting process it uses timer.now(), which causes (sometimes) "Invalid calling object". From what I read it's connected with passing references to native functions.
I imagine that this is the problem:
setFrame = typeof requestAnimationFrame === "function" ? requestAnimationFrame : function(f) { setTimeout(f, 17); };

In my case calling setFrame(clearNow) results in "Invalid calling object",
but calling requestAnimationFrame(clearNow) works just fine.

Similar problem was described here:
https://stackoverflow.com/questions/1007340/javascript-function-aliasing-doesnt-seem-to-work

Regards,
Konrad

In wake() function, when time flush is called "clock now" variable is calculated wrongly.

Issue is:

Open a html page with d3 transition in browser.
Navigate away from page before transition happen.
Say away until file is loaded.
Come back and you will find the transition will take 10-20 sec to start giving impression that chart is broken.

I found the bug in timeflush() function which is not calculating the right clocknow variable.

function timerFlush() {
  now(); // Get the current time, if not already set.
  ++frame; // Pretend we’ve set an alarm, if we haven’t already.
  var t = taskHead, e;
  while (t) {
    if ((e = clockNow - t._time) >= 0) t._call.call(null, e);
    t = t._next;
  }
  --frame;
}

clocknow should be positive when tab wake up but its not causing next transition call to stop.

Please suggest.

[Violation] 'requestAnimationFrame' handler took Xms in timer.js

I'm utilizing d3js in an electron.js application and when the system is left unattended for a day or so, when it wakes up the application isn't responsive. Initially I thought it was related to the system going to sleep, but in my most recent test the system never went to sleep, it was just locked for a couple of days.

Eventually it becomes responsive again, the application is fine, and the chrome console reports
[Violation] 'requestAnimationFrame' handler took Xms in timer.js:67

In my most recent test it was 24046ms (24s) after leaving it locked for about 60 hours.

The location that it reports is in the wake() function on line 67 of timer.js.

Based looking at what wake() does, the implication is that there's a very large number of time tasks being iterated over when the application is being woken up or it's doing some other operation to "catch up". I'm going to try to add some more instrumentation to see more precisely where the time is going, curious if this is a known issue and if there's a workaround.

passing user-defined variables to the callback function

I have an ES6 Class function that I'd like to invoke as a callback function, but it loses the scope of this when I call it with timer(callback).

Any way we can pass a custom variable to the callback function? I'm not quite sure how the syntax would even look, as I wouldn't want to override the delay and time variables.

Timers are never called back in the background.

Related d3/d3#2211 d3/d3-transition#29.

In some browsers, requestAnimationFrame is paused in background tabs. However, setTimeout and setInterval may continue, albeit possibly throttled. This asymmetrical behavior can have undesired side-effects, such as an ever-growing timer queue that is never flushed because timers are being scheduled but never invoked.

One way to fix this might be to guarantee that the first tick of a scheduled, eligible timer is invoked after at most one second, even in a background tab, by using setTimeout or setInterval internally rather than relying solely on requestAnimationFrame. We could guarantee that timers receive at least one tick per second this way, but I think it would be sufficient to only have this special behavior for the first tick—it’s nice if we do as little as possible in the background.

Or, we could just tick at least once per second, no matter what.

Test fails nondeterministically.

Hello,
a simplenpm install; npm run test fails now:

# timer(callback, delay) first invokes the callback after the specified delay
not ok 47 should be in range
  ---
    operator: inRange
    expected: [ 140, 160 ]
    actual:   166
    at: Test.tape.Test.inRange (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/test/inRange.js:4:8)
    stack: |-
      Error: should be in range
          at Test.assert [as _assert] (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/node_modules/tape/lib/test.js:224:54)
          at Test.bound [as _assert] (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/node_modules/tape/lib/test.js:76:32)
          at Test.tape.Test.inRange (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/test/inRange.js:4:8)
          at Test.bound [as inRange] (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/node_modules/tape/lib/test.js:76:32)
          at /home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/test/timer-test.js:69:10
          at timerFlush (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/build/d3-timer.js:68:48)
          at Timeout.wake [as _onTimeout] (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/build/d3-timer.js:78:5)
          at ontimeout (timers.js:436:11)
          at tryOnTimeout (timers.js:300:5)
          at listOnTimeout (timers.js:263:5)
  ...
# timer(callback, delay) computes the elapsed time relative to the delay
ok 48 should be in range

Creating a timer during flush requests an unused frame.

For example:

timer(function(elapsed, time) {
  console.log("first timer");
  timer(function(elapsed) {
    console.log("second timer");
    return true;
  }, 0, time);
  return true;
});

The second timer is invoked in the same tick as the first timer, since it is added to the queue while it is being flushed. Yet, requestAnimationFrame is still called because frame == 0 during flush.

`d3.timer()` is much slower than `setInterval( ... ,0.00001)`

I have build a little test App and I hoped that d3.timer() would make it much faster by using requestAnimationFrame(), but sadly the opposite is true. It is even slower than using setInterval( ... ,0.00001).

Ok the version with d3.timer() is more responsive, but thats not too impressive since its overall speed is much slower. In the attached video I tried to generate a random walk from prime numbers. But with both methods it simply is too slow...
Maybe I've done something wrong in my algorithm, so that requestAnimationFrame() can not be used, but I would not know what that could be.

A video comparing d3.timer() and setInterval( ... ,0.00001) (setInterval() is on left side): setInterval_vs_d3.timer.zip

I have also made two fiddles:

  1. With d3.interval(): With d3
  2. And with setInterval, which is faster but still much slower than computing this without Graphics: Standard JS

Saving handle to call cancelAnimationFrame is required.

I got an exception like this.

Uncaught DOMException: Failed to read the 'value' property from 'SVGLength': Could not resolve relative length.
    at SVGSVGElement.defaultExtent (http://localhost:3000/webpack-internal:///2894:29:25)
    at new Gesture (http://localhost:3000/webpack-internal:///2894:198:26)
    at gesture (http://localhost:3000/webpack-internal:///2894:190:12)
    at SVGSVGElement.eval (http://localhost:3000/webpack-internal:///2894:164:40)
    at Dispatch.call (http://localhost:3000/webpack-internal:///2631:57:72)
    at start (http://localhost:3000/webpack-internal:///121:130:13)
    at schedule (http://localhost:3000/webpack-internal:///121:78:32)
    at timerFlush (http://localhost:3000/webpack-internal:///703:65:48)
    at wake (http://localhost:3000/webpack-internal:///703:75:5)
defaultExtent @ zoom.js?6f4f:20
Gesture @ zoom.js?6f4f:189
gesture @ zoom.js?6f4f:181
(anonymous) @ zoom.js?6f4f:155
call @ dispatch.js?8f20:57
start @ schedule.js?f489:118
schedule @ schedule.js?f489:66
timerFlush @ timer.js?a6a1:61
wake @ timer.js?a6a1:71
requestAnimationFrame (async)
sleep @ timer.js?a6a1:108
restart @ timer.js?a6a1:39
timer @ timer.js?a6a1:52
create @ schedule.js?f489:59
__webpack_exports__.e @ schedule.js?f489:19
__webpack_exports__.a @ transition.js?7712:36
...

I think d3 needs to call cancelAnimationFrame if possible when it is being removed. Otherwise, wake function below could be called while no element is mounted on DOM for doing something.

frame = 1, setFrame(wake);

Incompatibility with Rstudio internal browser

Hi,

I'm using RStudio, an R language IDE, for developing an htmlwidget with d3. I'm trying to port my code to d3 v4, and I'm facing a curious problem: transitions don't work anymore. They work fine with d3 v3 in RStudio or with d3 v4 in a browser, but not with d3 v4 in RStudio.

After a bit of digging, it seems that the problem comes from the toolkit used by RStudio (GWT 2.7.0 as far as I can tell, but I don't know what kind and version of browser it is. The user agent displayed in the "about" dialog is currently "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) RStudio Safari/538.1 Qt/5.4.0").

There seems to be an inconsistency in the results returned by performance.now and requestAnimationFrame :

> performance.now()
26015828.386478003
> requestAnimationFrame(function(timestamp) {console.log(timestamp)})
1
1472136450436.1724

If I understand correctly, It seems that performance.now() returns an high precision timestamp, whereas requestAnimationFrame gives a number of milliseconds since epoch.

Due to this inconsistency, timers and transitions created by d3 don't work at all.

I've found a quick and dirty workaround inspired by this link. Adding the following code in d3-timer.js just before the now() function definition seems to work :

  if (typeof performance === "object" && clock == performance && typeof requestAnimationFrame === "function") {
    requestAnimationFrame(function(timestamp){
      if (timestamp > 1e12) {
        clock = Date;
        setFrame = function(f) { requestAnimationFrame(function() { f(clock.now()); }); }
      }
    })
  }

I could generate a pull request for this if you wish, but I'm really not sure the code quality is good enough, neither that this kind of workaround would really have a place in d3 code. Or there may be a better solution.

Sorry for this quite long issue, and thanks a lot for this awesome library and the no less awesome documentation and examples !

memory leak

code below

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>d3-timer memory leak</title>

  <script src="https://cdn.jsdelivr.net/npm/d3-timer@3"></script>
  <script>
    const t = d3.timer((elapsed) => {
      // run your code
    }, 150);
  </script>

<body>
</body>
</html>
  • start in the browser

image

  • after 5 min

image

Namespace for timers

I've started using hot reloading to see how my code changes the page without triggering a full refresh.

This requires some bookkeeping if there is a timer on the page to remove old timers:

if (window.__animationTimer) window.__animationTimer.stop()
window.__animationTimer = d3.timer(function(t){
  // cool animation code
})

Event listeners don't have this problem:

d3.select(window).on('resize.redraw', drawChart)

Timers could do something similar if they took a fourth argument:

d3.timer(function(t){
  // cool animation code
}, 0, null, 'animationTimer')

Not sure if you've found a better solution while working on express; it looked like you were using while (true) in a generator instead of a timer.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.