How can I animate scale and rotation of d3-geo projection at the same time with different durations and ease functions?

17 views Asked by At

The title is pretty descriptive of my question. I'm trying to find the most idiomatic/easy way to animate the scale and rotation property of a d3-geo projection (https://d3js.org/d3-geo/projection).

I'm fairly new to d3, so I don't know all the tools at my disposal yet, so please point me in a good direction if you have any answers/ideas. I'm also using React, so if that adds any complexity in your answer, please let me know.

My current attempt is using d3.tween() to manual call a function every tick t, that updates my current projection function and calls a function to redraw my Canvas. This works fine, but it doesn't allow me to use interpolations with different ease functions as I have both scale and rotation within the same .tween().

A trimmed version of my code:

/* input as array of arrays of form ["type", [interpolation range], duration, startTime] */
d3.transition()
      .duration(longestAnimation)
      .ease(d3.easePolyInOut)
      .tween("", function (d) {
        const interpolations = {};
        animationArray.forEach((animation) => {
          const [
            type,
            [interpolationStartVal, interpolationEndVal],
            duration,
            startTime,
          ] = animation;
          //console.log(animation);
          interpolations[animation] = [
            d3.interpolate(interpolationStartVal, interpolationEndVal),
            duration,
            startTime,
            type,
          ];
        });
        return function (t) {
          Object.entries(interpolations).forEach((interpolation) => {
            const [key, [interpolationFunc, duration, startTime, type]] =
              interpolation;
            const endTime = startTime + duration;
            let additionalFrame = false;
            if (startTime / longestAnimation <= t) {
              additionalFrame = true;
            }

            if (
              (startTime / longestAnimation <= t &&
                endTime / longestAnimation >= t) ||
              additionalFrame
            ) {
              if (endTime / longestAnimation >= t) additionalFrame = false;
              const newProjection = projection;
              const tempT = Math.min(endTime / longestAnimation, t);
              if (type === "rotate") {
              
                newProjection.rotate(
                  interpolationFunc(
                    (tempT - startTime / longestAnimation) *
                      (longestAnimation / duration)
                  )
                );
              }

              if (type === "scale") {
                newProjection.scale(
                  projectionScale *
                    interpolationFunc(
                      (tempT - startTime / longestAnimation) *
                        (longestAnimation / duration)
                    )
                );
              }
              drawCanvasFunc.current(newProjection);

This piece is a little long, but I wanted to illustrate the general idea I had so far, where I edit the current projection and then redraw the canvas every tick. Again, this works fine, but I can't use different easing functions.

Is my only option to write my own animation function using d3.timer?

Let me know - thanks a lot!

0

There are 0 answers