Initial delay and pausing transitions

Hi,

I’m currently experimenting with d3-graphviz, and have copied the demo at https://github.com/magjac/d3-graphviz/blob/master/examples/demo.html using that transitions mechanism. The following is a simple example of what I’m doing:

  let graphviz = d3.select("#graph").graphviz()

  const dots = [
    'digraph { a }',
    'digraph { a->b }',
    'digraph { a->b->c }',
  ]

  let dotIndex = 0;

  const render = function() {
      var dot = dots[dotIndex];
      graphviz
          .renderDot(dot)
          .on("end", function () {
              dotIndex++
              if (dotIndex < dots.length) {
                render()
              }
          })
  }

  graphviz
    .transition(function () {
      return d3.transition("main")
        .ease(d3.easeLinear)
        .delay(5000)
        .duration(500)
      })
      .on("initEnd", render)

The problem is that nothing happens for the first ‘delay’ seconds, whereas I would like the ‘a’ node to appear immediately, with the delay only affecting the later items. Is is possible to adjust the transition on a per-node basis?

As a bonus, is there a way to have aplay/pause button so that the user can start and stop the transition, and/or forward/back buttons to manually control things instead of automatic movement?

Thanks!

This easiest way to do it perhaps to make the delay conditional based on the dotIndex like so:


<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="../node_modules/d3/dist/d3.js"></script>
<script src="../node_modules/@hpcc-js/wasm/dist/index.js" type="javascript/worker"></script>
<script src="../build/d3-graphviz.js"></script>
<div id="graph" style="text-align: center;"></div>
<script>

  let graphviz = d3.select("#graph").graphviz();

  const dots = [
    'digraph { a }',
    'digraph { a->b }',
    'digraph { a->b->c }',
  ]

  let dotIndex = 0;

  const render = function() {
      var dot = dots[dotIndex];
      graphviz
          .renderDot(dot)
          .on("end", function () {
              dotIndex++
              if (dotIndex < dots.length) {
                render()
              }
          })
  }

  graphviz
    .transition(function () {
      return d3.transition("main")
        .ease(d3.easeLinear)
        .delay(dotIndex == 0 ? 0 : 5000)
        .duration(500)
      })
      .on("initEnd", render)

</script>

Ahh, that’s great - I hadn’t twigged that since the transition is returning a function that it could have different oparameters for each render! I’ll have a play around with this approach (I could also create a matching array that holds duration/delay values for each graph if needed).

I’ll have a play with also using this to toggle a play/pause state variable from a button, and then use that to affect transitions.

Many thanks!

1 Like

So for info, I realised that I was approaching this the ‘wrong way round’ by trying to work out how to ‘interrupt’ the automatic transitioning.

Instead I have refactored the code to move forward/back between graphs using buttons which call a function to increment or decrement ‘dotIndex’.

I then use a ‘play/pause’ button to set up or destroy a setInterval function that calls the ‘move’ function every X seconds.

The relevant functions are as follows:

// Move forward/backward through graphs
// 0 = start, '' = end
const move = function(val) {
  if (!val) {
    // Empty val, move to end
    dotIndex = dots.length - 1
  } else if (val == 0) {
     // 0 val - move to start
    dotIndex = 0
  } else {
    let newIndex = dotIndex + val
    if (newIndex >= 0 && newIndex < dots.length) {
      dotIndex = newIndex
    }
  }
  render()
}

let timer = false

const playPause = function() {
  if (!timer) {
    // Move immediately, then every 3 seconds
    move(1)
    timer = setInterval(() => move(1), 3000)
  } else {
    clearInterval(timer)
    timer = false
  }
}
1 Like