d3 / d3-transition

Animated transitions for D3 selections.
https://d3js.org/d3-transition
ISC License
223 stars 65 forks source link

dynamic transitions or a way to control transition progress #51

Closed mckennapsean closed 5 years ago

mckennapsean commented 7 years ago

I would like to request a new feature (and can work on a pull request) for the already amazing d3 transition module: transition.dynamic(progress) where progress is a function or value between 0 and 1 that yields how far into the transition it should be. I have already written this as a working function in my fork.

Adding this feature into the API would provide support for user-controlled animations, such as a slider that is tied to the progress of a transition or by controlling the transition's pace as the user scrolls down the page (e.g. scrollytelling). I originally asked about this on the Google Groups page, but got no response so I implemented it.

I have talked to other visualization developers who would use such a feature and have seen others recreate this effect in the wild by using custom tweens, interpolators, and scales, though that code is not as clean/readable as it could be. Having support in the API would be fantastic!

The exact functionality (and name of the function) could vary, but my quick hack was to hook into the transition life cycle (#14 - number 5) and always keep a dynamic transition alive (unless transition.interrupt() is called). There could be better ways to do this, but I figured this was cleaner than having to specify an infinite duration transition as well. The function or value given by the user would then be used in schedule.js right on this line.

Here is how GreenSock's animation library supports this concept, through animation.progress(value [0-1]).

If you like the idea, I would be more than happy to submit a pull request with what I have to match the code style and include some documentation of the feature. I also wanted to see if there are other useful related features worth considering: e.g. perhaps a cleaner way to cancel it via #32, a simple way to reverse the transition, or to more easily pause/play the transition for reuse.

mbostock commented 7 years ago

Sorry to give a hasty response, but I wanted to respond promptly. I see the attraction of manual control of animations, but I think this is probably not a good fit with the transition API due to the destructive nature of transitions: transitions start, tick repeatedly, and then end, destroying their state. It might make sense to freeze an individual transition on a single element, but much more commonly you have many transitions running on many elements with staggered delays and durations. So the first question is whether you want to control each transition individually, or if you want to control a “master” transition and then synchronize all concurrent transitions to that master transition. My guess is you want the latter, but then the problem is how you deal with re-creating transitions that were destroyed. Rather than trying to fit this into the existing transition API I think it might make more sense to consider a separate d3-animation API (built on-top of d3-timer), and how’d you implement something like keyframed animation with the ability to manually control time.

mckennapsean commented 7 years ago

Thanks Mike! I really appreciate the quick response.

I totally see where you are coming from, and what I whipped together is just a hack anyways. My use-case was rather simple, and the concept of delays and multiple transitions (and inheritance) would definitely complicate matters. I actually used d3.timer() to call these dynamic transitions, since it wraps support around requestAnimationFrame() and works very smoothly for my use case. I could see a benefit to having that all wrapped up together in a nice API.

A d3-animation module would be awesome but would require some significant thought and effort on what to include and tie into existing code. At least that is my guess! Happy to help brainstorm on this, but feel free to punt on this for now. :smile: