Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

reusable transitions #1168

Closed
jfirebaugh opened this Issue · 4 comments

2 participants

@jfirebaugh

We've found ourselves duplicating some common transitions (fade in, fade out, slide in, slide out) in multiple places in iD. Are there any good patterns for encapsulating these in a reusable way?

I think some discussion along these lines happened in #400. If I understand it correctly, some reuse is available through transition.each, but is limited to delay, duration and ease. I'm would like to do something along these lines:

selection
  .call(d3.effect.fadeIn()
    .duration(400)
    .each('end', function() {
      console.log('finished');
    });

My first try at an implementation was:

d3.effect = {
  fadeIn: function () {
    return function (selection) {
      return selection.transition()
        .each('start', function () {
          d3.select(this)
            .style('opacity', 0)
            .style('display', 'block');
        })
        .style('opacity', 1);
    }
  }

  // fadeOut, etc.
};

But of course, this doesn't work. The result of d3.effect.fadeIn() doesn't have duration and each methods; only the result of calling it does.

Time to revive the d3.transitioner concept?

d3.effect = {
  fadeIn: function () {
    return d3.transitioner()
      .each('start', function () {
        d3.select(this)
          .style('opacity', 0)
          .style('display', 'block');
      })
      .style('opacity', 1);
  }
};
@mbostock
Owner

Hmm. Have you considered using transition.call?

function fadeIn(transition) {
  return transition
      .each("start.fadein", function() {
        d3.select(this)
            .style("opacity", 0)
            .style("display", "block");
      })
      .style("opacity", 1);
}

selection.transition()
    .duration(400)
    .call(fadeIn)
    .each("end", function() { console.log("finished"); });

Though, I'm not entirely sure you want to set the opacity to zero on start, rather than immediately, since that could cause a brief flicker. So alternatively:

function fadeIn(selection) {
  return selection
      .style("opacity", 0)
      .style("display", "block")
    .transition()
      .style("opacity", 1);
}

selection.call(fadeIn);

And, if you want to customize delay, duration and easing:

d3.transition()
    .duration(500)
    .each(function() { selection.call(fadeIn); })
    .each("end", function() { console.log("finished"); });
@jfirebaugh

Yes, transition.call is probably what I want. Effects should be composable, which is easy:

selection.transition()
    .duration(400)
    .call(fadeIn)
    .call(slideIn);

With transition.call, fadeIn would need a way to retrieve the original selection in order to apply the opacity and display immediately. In #400 you proposed transition.selection, which would fit that bill.

Is composition of transitions possible with selection.call? If so, like many uses of d3.transition() + each, it is non-obvious to me.

@mbostock
Owner

Sure, you can create multiple transitions within transition.each and they will all refer to the same transition, thus allowing composition. The main difference is that within transition.each, selection.transition reselects the existing transition (if any) rather than creating a new transition (that would supersede the previous one). For example:

function fadeIn(selection) {
  return selection.style("opacity", 0).transition().style("opacity", 1);
}

function slideIn(selection) {
  return selection.style("margin-left", "-100px").transition().style("margin-left", "0px");
}

d3.transition()
    .duration(500)
    .each(function() { selection.call(fadeIn).call(slideIn); })
    .each("end", function() { console.log("finished"); });

And actually, these examples are using selection.call (not transition.call), so you already have access to the selection. You don’t need transition.call here because the transition parameters are automatically inherited due to the enclosing transition.each.

Also, I apologize for not yet documenting transition.each more thoroughly. I started with Working with Transitions but left it as a to-do at the bottom.

@mbostock
Owner

I’ve now documented transition.each and I believe I’ve addressed your questions with my earlier comments, so I’m closing this bug. However, if you have suggestions on how to improve the API, I’m happy to continue the discussion.

@mbostock mbostock closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.