Stagger issue

I am trying to see if I can replicate CSS stager animation in d3. This is what I tried.

The motion for blue and green circles was done through respectively CSS and d3. I was hoping to achieve the same animation for green circles as the blue ones. But they are not the same.

To make sure it is not due to easing, I have provided the exact same easing values in css as in d3. d3.easeSinOut= cubic-bezier(0.37, 0, 0.63, 1)

How can I achieve the exact same stagger motion in d3 as it is currently in d3.

My end goal is to use d3 for this motion and not CSS.

Thank you in advance.

@Fil

I don’t know… maybe a tiny bit of time is lost at the end of each loop?

Is there a way to fix it?

You’ll probably want to use a single transition for all elements and only change their easing offset?

@mootari can you please show what you are suggesting?

I don’t have the bandwidth right now, but the idea is that you animate all elements via the same transition instead of starting a separate one in .each(). If you specify a custom easing you can then offset t based on the element index, e.g. (t + i / 3) % 1.

In seems very odd to me to attempt to generate this animation as an infinite sequence of transitions when it’s very easy to describe the motions exactly as three sinusoids that happen to be slightly out of phase. More generally, CSS is not a programming language and I think it’s a mistake to try reproduce animations produced by CSS by emulating the code that you see there.

I would generate this motion using something like the techniques in this notebook. The key step is to describe the displacement of the i^{\text{th}} particle as

-Math.cos((t - T0 - 100 * i) / 400)

where t is the current time, T0 is the starting time, and i is the index of the particle. If you want to sync this with some other animation, of course, you’ll need to tweek the parameters.

Here it is in action:

1 Like

@mcmcclur many thanks for this. One follow up Q; in order for me to apply this in the prod item, I need to be generating the parameters programmatically. Hence, the question- how are you coming up with 100 and 400 in the following expression?

-Math.cos((t - T0 - 100 * i) / 400)

@mootari sounds like an interesting approach; however, I could not make that work. Whenever you have time and get a chance, if you can please demonstrate the idea you are proposing would be great.

Thanks again.

I’m guessing you know that in an expression involving time T like

-\cos(\omega T - \varphi)

that \varphi controls the phase and that \omega controls the frequency, which the reciprocal of the period. In the formula you refer to,

  • T = t-T_0,
  • \omega = 1/400 and
  • \varphi = i/4.

These are really just parameters to speed up/slow down or shift the motion. I didn’t do anything technical to compute those values; I just fiddled with them until the animation looked something like what you want. I suppose it might be worth mentioning that, if we take

\omega = 2\pi/1000,

then the frequency should be 1s. That is, we should observe one complete back and forth motion every second.

If you can obtain whatever frequency and phase arises from the process you’re modelling, you should be able to fit the motion.

1 Like

Here is a working example:

  const d3Circle = d3Grp
    .selectAll("circle")
    .data(rangeOne)
    .join("circle")
    .attr("cx", (d, i) => 100 + 30 * i)
    .attr("r", 10)
    .attr("fill", "green");

  const animate = selection => {
    selection
      .transition()
      .duration(2000)
      .ease(t => t) // linear
      .attrTween("cy", (d, i, arr) => {
        const offset = .15 * i / arr.length;
        const easeTri = t => t < .5 ? t * 2 : 2 - t * 2;
        return t => 50 + 600 * d3.easeQuadInOut(easeTri((offset + t) % 1));
      })
      .on("end", () => animate(selection));
  };
  animate(d3Circle);
1 Like