Skip to content

Instantly share code, notes, and snippets.

@chenglou
Last active March 13, 2024 12:14
Show Gist options
  • Save chenglou/40b75d820123a9ed53d8 to your computer and use it in GitHub Desktop.
Save chenglou/40b75d820123a9ed53d8 to your computer and use it in GitHub Desktop.

Revisions

  1. chenglou revised this gist Nov 24, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -145,7 +145,7 @@ We can literally clone the image (markup string, canvas pixels, etc.), move it s
    ### TransitionGroup's Way
    The problem with TransitionGroup is that it reimplemented a small, non-customizable reconciler in itself. This is the source of many bugs, and many unfixable ones if we don't expose the reconciler's diffing step and let user control it. A simple way to break it is to pass `[1, 2, 3]` then `setState` to `[2, 4]`. Currently the rendered result will be `[1, 2, 4, 3]`. `setState` again during this period and... you get the point. We have no saying over how the diffing's done.

    The goes deeper as TransitionGroup assumes that things don't get out of sync _too_ long. During this period of transitioning, it reports a different child count than the parent.
    This goes deeper as TransitionGroup assumes that things don't get out of sync _too_ long. During this period of transitioning, it reports a different child count than the parent.

    So, the current execution timeline:

  2. chenglou revised this gist Nov 24, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -105,7 +105,7 @@ Create an array that describes the animation, where each cell corresponds to one
    We can view our `setState` as a special case of `setStateArray(repeat(stateObj))`. This is kind of like FRP in that it uses common functional idioms to express your animation's progression. But in this case, we can simply use normal arrays (expensive), or lazy streams a-la Haskell/Clojure. Maybe even core.async.

    ## Animating Multiple Things
    Storing the animation configurations in the parent, in a state, quickly becomes tedious. Lots of previous values that can be computer on-the-fly will now be stored inside the state (e.g. children position. Layout also becomes a headache here as we move again from relative and start manually placing everything).
    Storing the animation configurations in the parent, in a state, quickly becomes tedious. Lots of previous values that can be computed on-the-fly will now be stored inside the state (e.g. children position. Layout also becomes a headache here as we move again from relative and start manually placing everything).

    This can potentially get messy (but maybe I'm wrong). This might either be a conceptual problem or something that a good API can solve.

  3. chenglou revised this gist Nov 24, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -40,7 +40,7 @@ The problem with this is that unmounting potentially becomes hacky. furthermore,
    At the beginning, it wasn't even clear whether the top-down approach worked well for animation. Layout certainly didn't. However, so far, React's top-down approach seems to translate well. Whatever API it is, it seems that it's better for the owner to control the animation of the children. This way, children stay totally oblivious of being animated. For practical purposes, we can wrap the children in a `PredefinedAnimationConvenienceWrapper`.

    ## Describe UI Through Time
    Every time I seem to end up putting animation configurations in lifecycle events. This is no better than jQuery, and it feels like no amount of sugar and helpers can transform these APIs into a truly declarative one. Based on the assumptions above, the better way to do it would be to find way to tell React what the UI looks like not at one single point, but through a period of time. But every time I try to describe the render through this "period of time", I end up automating the render for a while, a-la pete's `tweenState`. Note that this might not be a bad thing. I've implemented this here: https://github.com/chenglou/react-tween-state/blob/master/example.jsx#L32-L36
    Every time I seem to end up putting animation configurations in lifecycle events. This is no better than jQuery, and it feels like no amount of sugar and helpers can transform these APIs into a truly declarative one. Based on the assumptions above, the better way to do it would be to find way to tell React what the UI looks like not at one single point, but through a period of time. But every time I try to describe the render through this "period of time", I end up automating the render for a while, a-la pete's `tweenState`. Note that this might not be a bad thing. I've implemented this [here](https://github.com/chenglou/react-tween-state/blob/2077d6de533131ce05eb981cf81674d19b916386/examples/example1.jsx#L17-L21).

    This solves some problems and is even capable enough to emulate some physics. Imo, the problem with these kind of jQuery-ish APIs is that you have to go out of your way to stop the animation, queue them, etc., no matter how short and sugary they might look like. Manually coordinating feels wrong when React potentially offers something better. Other frameworks probably (no research here) just set some flags to prevent the same animation (judged based on, say, equal destination and interpolation method).

  4. chenglou revised this gist Nov 24, 2014. 1 changed file with 22 additions and 1 deletion.
    23 changes: 22 additions & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -99,6 +99,11 @@ The catch with this is that it changes the logic we put in some lifecycle events
    ### Push Everything Down to DOMComponent
    The second way is to stay in the current paradigm and only make `ReactDOMComponent`s have a timeline every frame. This isn't just an implementation/optimization detail compared to the previous point. All the animation configurations are squashed to the leaves. The interesting part with this paradigm is that instead of relying on repeatedly rendering through raf, we can plug an existing animation engine, e.g. CSS or something else. More on this and the API in the API section, first proposal.

    ### setStateArray
    `setStateArray(bla.map(...))`
    Create an array that describes the animation, where each cell corresponds to one frame.
    We can view our `setState` as a special case of `setStateArray(repeat(stateObj))`. This is kind of like FRP in that it uses common functional idioms to express your animation's progression. But in this case, we can simply use normal arrays (expensive), or lazy streams a-la Haskell/Clojure. Maybe even core.async.

    ## Animating Multiple Things
    Storing the animation configurations in the parent, in a state, quickly becomes tedious. Lots of previous values that can be computer on-the-fly will now be stored inside the state (e.g. children position. Layout also becomes a headache here as we move again from relative and start manually placing everything).

    @@ -179,6 +184,22 @@ All the API proposals here revolve around automating `render` through `requestAn

    ### General

    ### `setStateArray`
    ```js
    {
    handleClick: function() {
    var newStates = range(0, 1000).map(function(tick) {
    return {
    posX: interpolate(50, 150, tick, 'easeOut')
    };
    });
    this.setStateArray(newStates);
    }
    ...
    }
    ```
    Ideally, when we ditch `setState` in favor of returning it, this'd give a pretty nice API. This is what I'm gonna explore next.

    #### "Squashing everything down to DOMComponent"
    ```js
    render: function() {
    @@ -191,7 +212,7 @@ render: function() {
    }
    ```

    Looks neat. Only works with style values.
    Looks neat. Only works with style values. A bit informal.

    #### tweenState & variations
    https://github.com/chenglou/react-tween-state
  5. chenglou revised this gist Jul 13, 2014. 1 changed file with 4 additions and 49 deletions.
    53 changes: 4 additions & 49 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -40,40 +40,11 @@ The problem with this is that unmounting potentially becomes hacky. furthermore,
    At the beginning, it wasn't even clear whether the top-down approach worked well for animation. Layout certainly didn't. However, so far, React's top-down approach seems to translate well. Whatever API it is, it seems that it's better for the owner to control the animation of the children. This way, children stay totally oblivious of being animated. For practical purposes, we can wrap the children in a `PredefinedAnimationConvenienceWrapper`.

    ## Describe UI Through Time
    Every time I seem to end up putting animation configurations in lifecycle events. This is no better than jQuery, and it feels like no amount of sugar and helpers can transform these APIs into a truly declarative one. Based on the assumptions above, the better way to do it would be to find way to tell React what the UI looks like not at one single point, but through a period of time. But every time I try to describe the render through this "period of time", I end up automating the render for a while, a-la pete's `tweenState`. Note that this might not be a bad thing.

    ```js
    var Counter = React.createClass({
    getInitialState: function() {
    return {counter: 0};
    },

    componentDidMount: function() {
    this.tweenState({
    method: easeOut,
    stateName: 'counter',
    params: {
    duration: 1000,
    beginValue: this.state.counter,
    endValue: this.state.counter + 1000
    }
    }, function(introspectableAnimationParams) {
    // Each frame callback. This is essentially a thin raf wrapper (nothing
    // wrong with that!).
    }, function() {
    // End callback.
    });
    },

    render: function() {
    return <div>{this.state.counter}</div>;
    }
    });
    ```
    Every time I seem to end up putting animation configurations in lifecycle events. This is no better than jQuery, and it feels like no amount of sugar and helpers can transform these APIs into a truly declarative one. Based on the assumptions above, the better way to do it would be to find way to tell React what the UI looks like not at one single point, but through a period of time. But every time I try to describe the render through this "period of time", I end up automating the render for a while, a-la pete's `tweenState`. Note that this might not be a bad thing. I've implemented this here: https://github.com/chenglou/react-tween-state/blob/master/example.jsx#L32-L36

    This solves some problems and is even capable enough to emulate some physics. Imo, the problem with these kind of jQuery-ish APIs is that you have to go out of your way to stop the animation, queue them, etc., no matter how short and sugary they might look like. Manually coordinating feels wrong when React potentially offers something better. Other frameworks probably (no research here) just set some flags to prevent the same animation (judged based on, say, equal destination and interpolation method).

    This might be hard. I'd even say maybe as hard as unmounting, because this might change how we think of render. I've found a few ways to approach this.
    A sensible default would be [additive animation](http://ronnqvi.st/multiple-animations/), which my repo above implements.

    ### Timeline
    (Relevant: [new web animation standard proposal](https://github.com/web-animations/web-animations-js)). The standard unfortunately doesn't cover animation on unmounting (and rightfully so, since _current_ React's the only library experiencing this), and doesn't provide hooks for making things like physics easier. It's mostly a more sane API for the current possible animations, so this proposal won't go over it.
    @@ -223,23 +194,7 @@ render: function() {
    Looks neat. Only works with style values.

    #### tweenState & variations
    ```js
    this.tweenState({
    method: easeOut,
    stateName: 'counter',
    params: {
    duration: 1000,
    beginValue: this.state.counter,
    endValue: this.state.counter + 1000
    }
    }, function(introspectableAnimationParams) {
    // Each frame callback.
    }, function() {
    // End callback.
    });
    ```

    Hard to scale up to animating a series of children, each of which has, say, two props to animate. In these cases where we need to animate values stored potentially deeply inside a collection, we could do `stateName: ['a', 1, 'b']` to instruct the API to tween the value in the state `{a: [{...}, {b: 5}, ...}]}`. Basically [json pointers](https://tools.ietf.org/html/rfc6901) or Haskell's lenses. Feels weird, but maybe only because it's unfamiliar to JS and thus feels forced.
    https://github.com/chenglou/react-tween-state

    #### Event handlers

    @@ -381,7 +336,7 @@ Assuming we do go down the road of animating through using rAF on the render, we
    ## Other Thoughts
    Can FRP's map/reduce/filter composition play a role in animation? http://youtu.be/XRYN2xt11Ek?t=13m5s

    Additive animation: the default that CSS animation should have. https://developer.apple.com/videos/wwdc/2014/, "Building Interruptible and Responsive Interactions". Must watch. It's a very elegant solution to transitioning between animations, for non-physics animations.
    Additive animation: the default that CSS animation should have. https://developer.apple.com/videos/wwdc/2014/, "Building Interruptible and Responsive Interactions". Somewhere in the middle. Must watch. It's a very elegant solution to transitioning between animations, for non-physics animations.

    core.async might be interesting. Maybe this is a better way to coordinate stuff than pure promises for, say, `setState`.

  6. chenglou revised this gist Jun 20, 2014. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -381,6 +381,8 @@ Assuming we do go down the road of animating through using rAF on the render, we
    ## Other Thoughts
    Can FRP's map/reduce/filter composition play a role in animation? http://youtu.be/XRYN2xt11Ek?t=13m5s

    Additive animation: the default that CSS animation should have. https://developer.apple.com/videos/wwdc/2014/, "Building Interruptible and Responsive Interactions". Must watch. It's a very elegant solution to transitioning between animations, for non-physics animations.

    core.async might be interesting. Maybe this is a better way to coordinate stuff than pure promises for, say, `setState`.

    `key` (identity in general) needs to be more powerful: https://groups.google.com/forum/#!topic/reactjs/ySPp8ksaDAc not just for perf reasons, but also to further help the reconciler to know where we moved items.
  7. chenglou revised this gist May 22, 2014. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -163,6 +163,9 @@ This approach never lets data get out of sync. The data isn't changed until the

    The problem is animation concurrency. Every `setState` will forcefully be queued. This is not desirable, so scratching `setState` promise for the purpose of animation.

    ### Clone and Animate Somewhere Else
    We can literally clone the image (markup string, canvas pixels, etc.), move it somewhere else and position it correctly, and animate that after the original child's been removed. This does hacky stuff under the hood so I don't like it. More than cloning the node, we'd have to simulate everything that depended on it, notably its siblings' layout positioning. Potentially violates stress test 5 and doesn't look like something we can build on top of.

    ### TransitionGroup's Way
    The problem with TransitionGroup is that it reimplemented a small, non-customizable reconciler in itself. This is the source of many bugs, and many unfixable ones if we don't expose the reconciler's diffing step and let user control it. A simple way to break it is to pass `[1, 2, 3]` then `setState` to `[2, 4]`. Currently the rendered result will be `[1, 2, 4, 3]`. `setState` again during this period and... you get the point. We have no saying over how the diffing's done.

    @@ -236,7 +239,7 @@ this.tweenState({
    });
    ```

    Hard to scale up to animating a series of children, each of which has, say, two props to animate. In these cases where we need to animate values stored potentially deeply inside a collection, we could do `stateName: ['a', 1, 'b']` to instruct the API to tween the value in the state `{a: [{...}, {b: 5}, ...}]}`. Basically [json pointers](https://tools.ietf.org/html/rfc6901) or Haskell's lenses. Feels weird.
    Hard to scale up to animating a series of children, each of which has, say, two props to animate. In these cases where we need to animate values stored potentially deeply inside a collection, we could do `stateName: ['a', 1, 'b']` to instruct the API to tween the value in the state `{a: [{...}, {b: 5}, ...}]}`. Basically [json pointers](https://tools.ietf.org/html/rfc6901) or Haskell's lenses. Feels weird, but maybe only because it's unfamiliar to JS and thus feels forced.

    #### Event handlers

  8. chenglou revised this gist May 12, 2014. 1 changed file with 10 additions and 2 deletions.
    12 changes: 10 additions & 2 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -11,6 +11,8 @@ This animation proposal is just an attempt. In case it doesn't work out, I've ga

    4. A millisecond counter that uses easeout to increase its value at every second. This tests the system's ability to interpolate arbitrary values rather than, say, only CSS props.

    5. The animation should be (naturally) rewindable. I'll explain this further in the API section.

    ## Goal
    Find an animation API that leverages React's declarative paradigm rather than the traditional imperative one (e.g. jQuery). Note that jQuery's animations themselves aren't that imperative; it's more because of the traditional way these methods fit with the library.

    @@ -32,7 +34,7 @@ TransitionGroup's does 80% of the job with 20% of the effort. We'll have to rely
    ### What We're Not Going to Do
    One prototype used something conceptually similar to CSS animations: the render is not touched. Under the hood, we track the styles applied and interpolate them. This is already much more powerful than CSS animations because we can specify custom animations.

    The problem with this is that unmounting potentially becomes hacky. furthermore, it just feels more _right_ to automate render to achieve animation (also see stress test 4).
    The problem with this is that unmounting potentially becomes hacky. furthermore, it just feels more _right_ to automate render to achieve animation (also see stress test 4). See more explanation in the API section.

    ## Where The Controls Belong
    At the beginning, it wasn't even clear whether the top-down approach worked well for animation. Layout certainly didn't. However, so far, React's top-down approach seems to translate well. Whatever API it is, it seems that it's better for the owner to control the animation of the children. This way, children stay totally oblivious of being animated. For practical purposes, we can wrap the children in a `PredefinedAnimationConvenienceWrapper`.
    @@ -74,7 +76,7 @@ This solves some problems and is even capable enough to emulate some physics. Im
    This might be hard. I'd even say maybe as hard as unmounting, because this might change how we think of render. I've found a few ways to approach this.

    ### Timeline
    (Relevant: [new web animation standard proposal](https://github.com/web-animations/web-animations-js)).
    (Relevant: [new web animation standard proposal](https://github.com/web-animations/web-animations-js)). The standard unfortunately doesn't cover animation on unmounting (and rightfully so, since _current_ React's the only library experiencing this), and doesn't provide hooks for making things like physics easier. It's mostly a more sane API for the current possible animations, so this proposal won't go over it.

    In Flash, the components have their respective timeline. A ball that loops and spins over 30 frames can be nested inside an owner that spins over 60 frames. This is Flash's main way of composing.

    @@ -195,6 +197,12 @@ This concepts seems nice from a first glance. Providing that the diffing works w

    ## API

    All the API proposals here revolve around automating `render` through `requestAnimationFrame`. There are a few reasons why I didn't go with the CSS route of animating things under the hood:

    - It only works for DOM. We want better abstractions. Ideally, we should be able to take the animation system/hooks and easily apply them to canvas, svg, etc. This might still be possible in the CSS animation paradigm if we do something like `React.Animation.inject(canvasImplicitAnimationStrategy)`. But I don't like it...

    - It feels very situation-specific and not CS-y. See stress test #5. Animation should be snapshottable, akin to Om's natural undo. THe fact that undo is made effortless through persistent data structures + React's optimization should be a goal of animation too; I should be able to undo the animation by reversing it. This feels theoretically solid.

    ### General

    #### "Squashing everything down to DOMComponent"
  9. chenglou revised this gist May 7, 2014. 1 changed file with 24 additions and 0 deletions.
    24 changes: 24 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -255,6 +255,30 @@ render: function() {

    Not sure about this one.

    #### Obligatory Physics Engine

    ```js
    var App = React.createClass({
    getInitialState: function() {
    return {
    params1: {weight: 10, friction: 5, ...}
    params2: {...}
    };
    },

    handleClick: function() {
    params1 = {weight: 20, friction: 10};
    this.setState({params1: params1});
    },

    render: function() {
    return <div><Child physicsParams={this.state.params1} /></div>;
    }
    });
    ```

    Mostly a no-brainer. Should plug well into, say, Box2d.

    ### Unmounting

    ```js
  10. chenglou revised this gist May 7, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -228,7 +228,7 @@ this.tweenState({
    });
    ```

    Hard to scale up to animating a series of children, each of which has, say, two props to animate. In these cases where we need to animate values stored potentially deeply inside a collection, we could do `stateName: ['a', 1, 'b']` to instruct the API to tween the value in the state `{a: [{...}, {b: 5}, ...}]}`. Feels weird.
    Hard to scale up to animating a series of children, each of which has, say, two props to animate. In these cases where we need to animate values stored potentially deeply inside a collection, we could do `stateName: ['a', 1, 'b']` to instruct the API to tween the value in the state `{a: [{...}, {b: 5}, ...}]}`. Basically [json pointers](https://tools.ietf.org/html/rfc6901) or Haskell's lenses. Feels weird.

    #### Event handlers

  11. chenglou revised this gist May 6, 2014. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -344,11 +344,13 @@ Would be great if animation doesn't get in the way of render, even in this situa
    Assuming we do go down the road of animating through using rAF on the render, we should leverage React's upcoming rAF batching to avoid layout thrashing. Not developing this section until we nail down an API.

    ## Other Thoughts
    Can FRP's map/reduce/filter composition play a role in animation? http://youtu.be/XRYN2xt11Ek?t=13m5s

    core.async might be interesting. Maybe this is a better way to coordinate stuff than pure promises for, say, `setState`.

    `key` (identity in general) needs to be more powerful: https://groups.google.com/forum/#!topic/reactjs/ySPp8ksaDAc not just for perf reasons, but also to further help the reconciler to know where we moved items.

    Layout is a headache because it adds a bit of manual management each time. Any current layout system isn't flexible/powerful enough to cooperate with animated physics like chat heads.
    [Layout](https://gist.github.com/chenglou/caff72e06e0c3d72c9b4) is a headache because it adds a bit of manual management each time. Any current layout system isn't flexible/powerful enough to cooperate with animated physics like chat heads.

    `setState` promise might still be useful for batched rendering, partial reconcile, layout, etc.

  12. chenglou revised this gist May 6, 2014. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -74,6 +74,8 @@ This solves some problems and is even capable enough to emulate some physics. Im
    This might be hard. I'd even say maybe as hard as unmounting, because this might change how we think of render. I've found a few ways to approach this.

    ### Timeline
    (Relevant: [new web animation standard proposal](https://github.com/web-animations/web-animations-js)).

    In Flash, the components have their respective timeline. A ball that loops and spins over 30 frames can be nested inside an owner that spins over 60 frames. This is Flash's main way of composing.

    A component can have multiple timelines: https://wiki.brown.edu/confluence/download/attachments/4415/Flash_timeline.gif?version=1&modificationDate=1152908704000.
  13. chenglou revised this gist May 6, 2014. 1 changed file with 5 additions and 3 deletions.
    8 changes: 5 additions & 3 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -172,11 +172,11 @@ There has to be a way to include unmounting in a general concept rather than spe

    TransitionGroup's idea itself is not bad; we just have to implement this at React level and make it leverage the actual (customizable) reconciler. The new flow:

    parent setState -> render promise -> child willReceiveProps (+ render preview) -> automated forked render -> render
    parent setState -> child willReceiveProps (+ render preview) -> parent render -> child automated forked render -> child final render

    The "automated render" part starts at the point _before_ the new, shrunken array has been written as `this.props`: (could be done in `willReceiveProps`). The magical part here is to _preview the next render_ result and apply a diff on top of the current one. We then target these diffed components and command animation configurations on them.
    The magical part here is to _preview the next render_ result and apply a diff on top of the current one. We then target these diffed components and command animation configurations on them. Doing diffing/patching on render result rather than text sounds crazy.

    At the end of this, the final, actual child `render` with the new props from parent is called. Note that the parent's `state.items` is already updated at step 2.
    Note that the parent's `state.items` is already updated at step 3.
    A few assumptions are made here:

    - Things stay as immutable as possible. Screwing around deeply nested objects in props/state will not end well.
    @@ -348,4 +348,6 @@ core.async might be interesting. Maybe this is a better way to coordinate stuff

    Layout is a headache because it adds a bit of manual management each time. Any current layout system isn't flexible/powerful enough to cooperate with animated physics like chat heads.

    `setState` promise might still be useful for batched rendering, partial reconcile, layout, etc.

    Om leverages Clojure's persistent data structures so well that it's basically a better React without the handicap of JS. We might want to look more at it in the future for layout and animation (instruments, tx-listen, etc.
  14. chenglou revised this gist May 6, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -122,7 +122,7 @@ var Counter = React.createClass({
    The catch with this is that it changes the logic we put in some lifecycle events such as `willReceiveProps`. The task of "playing animation when the parent rerenders" doesn't make sense anymore (aka, you can't start an animation configuration in `willReceiveProps`), since the parent could be rerendering at every frame. People have pointed out that we can still `setAnimationConfig` and ignore its subsequent, redundant calls, but imo this doesn't feel right.

    ### Push Everything Down to DOMComponent
    The second way is to stay in the current paradigm and only make `ReactDOMComponent`s have a timeline every frame. This isn't just an implementation/optimization detail compared to the previous point. All the animation configurations are squashed to the leaves. The interesting part with this paradigm is that instead of relying on repeatedly rendering through raf, we can plug an existing animation engine, e.g. CSS or something else. More on this and the API later.
    The second way is to stay in the current paradigm and only make `ReactDOMComponent`s have a timeline every frame. This isn't just an implementation/optimization detail compared to the previous point. All the animation configurations are squashed to the leaves. The interesting part with this paradigm is that instead of relying on repeatedly rendering through raf, we can plug an existing animation engine, e.g. CSS or something else. More on this and the API in the API section, first proposal.

    ## Animating Multiple Things
    Storing the animation configurations in the parent, in a state, quickly becomes tedious. Lots of previous values that can be computer on-the-fly will now be stored inside the state (e.g. children position. Layout also becomes a headache here as we move again from relative and start manually placing everything).
  15. chenglou revised this gist May 6, 2014. 1 changed file with 100 additions and 43 deletions.
    143 changes: 100 additions & 43 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -3,13 +3,13 @@ Interesting part (unmounting & API) is at the end if you're not interested in th
    ## Stress Tests
    This animation proposal is just an attempt. In case it doesn't work out, I've gathered a few examples that can test the power of a future animation system.

    - Parent is an infinitely spinning ball, and has a child ball that is also spinning. Clicking on the parent causes child to reverse spinning direction. This tests the ability of the animation system to compose animation, not in the sense of applying multiple interpolations to one or more variables passed onto the child (this should be trivial), but in the sense that the parent's constantly updating at the same time as the child, and has to ensure that it passes the animation commands correctly to it. This also tests that we can still intercept these animations (the clicking) and immediately change their configuration instead of queueing them.
    1. Parent is an infinitely spinning ball, and has a child ball that is also spinning. Clicking on the parent causes child to reverse spinning direction. This tests the ability of the animation system to compose animation, not in the sense of applying multiple interpolations to one or more variables passed onto the child (this should be trivial), but in the sense that the parent's constantly updating at the same time as the child, and has to ensure that it passes the animation commands correctly to it. This also tests that we can still intercept these animations (the clicking) and immediately change their configuration instead of queueing them.

    - Typing letters and let them fly in concurrently. This tests concurrency, coordination of an array of children, whose positions depend on each other, and mounting and unmounting. Pretty much _the_ stress test.
    2. Typing letters and let them fly in concurrently. This tests concurrency, coordination of an array of children, whose positions depend on each other, and mounting and unmounting. Pretty much _the_ stress test.

    - Three boxes, from different hierarchies/owners, colliding and falling. The animating API doesn't need to (and shouldn't) implement its own physics engine; it should be able to reuse existing ones. Additionally, these animations might need a global knowledge of other components' animation configurations.
    3. Three boxes, from different hierarchies/owners, colliding and falling. The animating API doesn't need to (and shouldn't) implement its own physics engine; it should be able to reuse existing ones. Additionally, these animations might need a global knowledge of other components' animation configurations.

    - A millisecond counter that uses easeout to increase its value at every second. This tests the system's ability to interpolate arbitrary values rather than, say, only CSS props.
    4. A millisecond counter that uses easeout to increase its value at every second. This tests the system's ability to interpolate arbitrary values rather than, say, only CSS props.

    ## Goal
    Find an animation API that leverages React's declarative paradigm rather than the traditional imperative one (e.g. jQuery). Note that jQuery's animations themselves aren't that imperative; it's more because of the traditional way these methods fit with the library.
    @@ -38,53 +38,38 @@ The problem with this is that unmounting potentially becomes hacky. furthermore,
    At the beginning, it wasn't even clear whether the top-down approach worked well for animation. Layout certainly didn't. However, so far, React's top-down approach seems to translate well. Whatever API it is, it seems that it's better for the owner to control the animation of the children. This way, children stay totally oblivious of being animated. For practical purposes, we can wrap the children in a `PredefinedAnimationConvenienceWrapper`.

    ## Describe UI Through Time
    Every time I seem to end up putting animation configurations in lifecycle events. This is no better than jQuery, and it feels like no amount of sugar and helpers can transform these APIs into a truly declarative one. Based on the assumptions above, the better way to do it would be to find way to tell React what the UI looks like not at one single point, but through a period of time. But every time I try to describe the render through this "period of time", I end up automating the render for a while:
    Every time I seem to end up putting animation configurations in lifecycle events. This is no better than jQuery, and it feels like no amount of sugar and helpers can transform these APIs into a truly declarative one. Based on the assumptions above, the better way to do it would be to find way to tell React what the UI looks like not at one single point, but through a period of time. But every time I try to describe the render through this "period of time", I end up automating the render for a while, a-la pete's `tweenState`. Note that this might not be a bad thing.

    ```js
    // Milliseconds counter, except instead of increasing at a regular interval, it
    // does a cool slowing-down effect near each 1000 ms.
    var Counter = React.createClass({
    getInitialState: function() {
    return {
    counter: 0
    };
    return {counter: 0};
    },

    incrementCounter: function() {
    // Promise, because.
    return this.interpolate({
    componentDidMount: function() {
    this.tweenState({
    method: easeOut,
    stateName: 'counter',
    params: {
    duration: 1000,
    beginValue: this.state.counter,
    endValue: this.state.counter + 1000
    }
    }, function(animationParams) {
    }, function(introspectableAnimationParams) {
    // Each frame callback. This is essentially a thin raf wrapper (nothing
    // wrong with that!).
    this.setState({counter: animationParams.currentValue});
    }.bind(this));
    },

    componentDidMount: function() {
    function repeat() {
    this.incrementCounter().then(repeat);
    }

    repeat();
    }, function() {
    // End callback.
    });
    },

    render: function() {
    return (
    <div>
    {this.state.counter}
    </div>
    );
    return <div>{this.state.counter}</div>;
    }
    });
    ```

    This was one of the initial APIs. It solves some problems and is even capable enough to emulate some physics. Imo, the problem with these kind of jQuery-ish APIs is that you have to go out of your way to stop the animation, queue them, etc., no matter how short and sugary they might look like. Manually coordinating feels wrong when React potentially offers something better. Other frameworks probably (no research here) just set some flags to prevent the same animation (judged based on, say, equal destination and interpolation method).
    This solves some problems and is even capable enough to emulate some physics. Imo, the problem with these kind of jQuery-ish APIs is that you have to go out of your way to stop the animation, queue them, etc., no matter how short and sugary they might look like. Manually coordinating feels wrong when React potentially offers something better. Other frameworks probably (no research here) just set some flags to prevent the same animation (judged based on, say, equal destination and interpolation method).

    This might be hard. I'd even say maybe as hard as unmounting, because this might change how we think of render. I've found a few ways to approach this.

    @@ -99,12 +84,9 @@ Owner owns child, Layer2 belongs to owner. Child has its own internal timeline.
    You can have code on arbitrary frames and they'll get executed when it's reached. This might be appealing or repulsive:

    ```js
    // Same counter as above.
    var Counter = React.createClass({
    getInitialState: function() {
    return {
    stepper: 0
    };
    return {stepper: 0};
    },

    componentDidMount: function() {
    @@ -140,9 +122,9 @@ var Counter = React.createClass({
    The catch with this is that it changes the logic we put in some lifecycle events such as `willReceiveProps`. The task of "playing animation when the parent rerenders" doesn't make sense anymore (aka, you can't start an animation configuration in `willReceiveProps`), since the parent could be rerendering at every frame. People have pointed out that we can still `setAnimationConfig` and ignore its subsequent, redundant calls, but imo this doesn't feel right.

    ### Push Everything Down to DOMComponent
    The second way is to stay in the current paradigm and only make `ReactDOMComponent`s have a timeline every frame. This isn't just an implementation/optimization detail compared to the previous point. All the animation configurations are squashed to the leaves. The interesting part with this paradigm is that instead of relying on repeatedly rendering through raf, we can plug an existing animation engine, e.g. CSS or something else.
    The second way is to stay in the current paradigm and only make `ReactDOMComponent`s have a timeline every frame. This isn't just an implementation/optimization detail compared to the previous point. All the animation configurations are squashed to the leaves. The interesting part with this paradigm is that instead of relying on repeatedly rendering through raf, we can plug an existing animation engine, e.g. CSS or something else. More on this and the API later.

    ## Animating Multiple Instances
    ## Animating Multiple Things
    Storing the animation configurations in the parent, in a state, quickly becomes tedious. Lots of previous values that can be computer on-the-fly will now be stored inside the state (e.g. children position. Layout also becomes a headache here as we move again from relative and start manually placing everything).

    This can potentially get messy (but maybe I'm wrong). This might either be a conceptual problem or something that a good API can solve.
    @@ -166,11 +148,19 @@ Physics need to know more than the little information about begin/duration/whate
    See "Describing UI Through Time" above. If we take the first route then we might have to rethink about how some lifecycle methods work, and expose new ones.

    ## Unmounting
    Huge headache, as this is the only case where data can go potentially out of sync.
    Huge headache, as this is the only case where data potentially goes out of sync with the view.

    I've found that one heuristic that leads us onto the right path is if the animation's reversible. Just like Om can easily implement undo through persistent data structures, unmounting animation (and animation in general) should theoretically be something that we can rewind. This further explains the section "What We're Not Going to Do".
    Side note: I've found that one heuristic that leads us onto the right path is if the animation's reversible. Just like Om can easily implement undo through persistent data structures, unmounting animation (and animation in general) should theoretically be something that we can rewind. This further explains the section "What We're Not Going to Do".

    TransitionGroup is one way to do it. The problem with it is that it reimplemented a small, non-customizable reconciler in itself. This is the source of many bugs, and many unfixable ones if we don't expose the reconciler's diffing step and let user control it. A simple way to break it is to pass `[1, 2, 3]` then `setState` to `[2, 4]`. Currently the rendered result will be `[1, 2, 4, 3]`. `setState` again during this period and... you get the point. We have no saying over how the diffing's done.
    There are two major ways to solve this. Say from the perspective of the parent that passes a `[1, 2]` (soon to be `[1]`) state to child and the child renders it. Either we keep the data consistent and turn parent `setState` into a promise that resolves when the child animation is done, or we let parent immediately update its `[1, 2]`, and do the TransitionGroup's trick of keeping a copy of the old data and animate it, hoping the data won't stay out of sync for too long.

    ### `setState` Promise
    This approach never lets data get out of sync. The data isn't changed until the unmounting animation's over.

    The problem is animation concurrency. Every `setState` will forcefully be queued. This is not desirable, so scratching `setState` promise for the purpose of animation.

    ### TransitionGroup's Way
    The problem with TransitionGroup is that it reimplemented a small, non-customizable reconciler in itself. This is the source of many bugs, and many unfixable ones if we don't expose the reconciler's diffing step and let user control it. A simple way to break it is to pass `[1, 2, 3]` then `setState` to `[2, 4]`. Currently the rendered result will be `[1, 2, 4, 3]`. `setState` again during this period and... you get the point. We have no saying over how the diffing's done.

    The goes deeper as TransitionGroup assumes that things don't get out of sync _too_ long. During this period of transitioning, it reports a different child count than the parent.

    @@ -184,7 +174,7 @@ TransitionGroup's idea itself is not bad; we just have to implement this at Reac

    parent setState -> render promise -> child willReceiveProps (+ render preview) -> automated forked render -> render

    The "automated render" part starts at the point _before_ the new, shrunken array has been written as `this.props`: (could be done in `willReceiveProps`). The magical part here is to _preview the next render_ result and apply a diff on top of the current one. We then target these diffed components and command animation configurations on them. API-wise this gets tricky, as I'm still trying to figure out where to actually put this configuration.
    The "automated render" part starts at the point _before_ the new, shrunken array has been written as `this.props`: (could be done in `willReceiveProps`). The magical part here is to _preview the next render_ result and apply a diff on top of the current one. We then target these diffed components and command animation configurations on them.

    At the end of this, the final, actual child `render` with the new props from parent is called. Note that the parent's `state.items` is already updated at step 2.
    A few assumptions are made here:
    @@ -193,16 +183,78 @@ A few assumptions are made here:

    - `key` plays a very critical role here, diffing incorrectly in the past was fine: the render can unmount/re-mount the same thing and the result still looks correct. But animation will expose the flaw very visually if you don't diff the items correctly, e.g. the wrong item might be moving around.

    - To enable preview, `render` should become a function of `props` and `state` and should not rely on `this.props` and `this.state`: https://github.com/facebook/react/issues/1387. As a side note, in an ideal world, `render` will return a plain JS object without actually changing the view. Other rising libraries might even take these objects and render them differently.
    - To enable preview, `render` should become a function of `props` and `state` and should not rely on `this.props` and `this.state`: https://github.com/facebook/react/issues/1387.

    **Note**: The magical (and expensive) part is if we can stack all the diffs through time: `[1, 2]` -> `[1]` -> `[1, 2]` should bring the almost unmounted `2` back visually, even though the instance is destroyed and recreated.

    In this case, `key` wouldn't just be the identity of the current component, but the identity of this component at _any moment_; It's effectively a hash that allows you to identify the component that might unmount halfway and come back before disappearing. if it's not `key`, we need _some_ way to tell the reconciler that the component coming back is the same that was being unmounted (diffing through time!).

    Interesting way to make diff efficient: http://youtu.be/DMtwq3QtddY?t=20m32s
    This concepts seems nice from a first glance. Providing that the diffing works well, this can be infinitely scalable, with as many concurrent mount/unmount. The beef I have against this approach is that it doesn't follow well the heuristic I've mentioned at the beginning (reversiblility). This seems less coherent from a CS standpoint.

    ## API

    ### General

    #### "Squashing everything down to DOMComponent"
    ```js
    render: function() {
    var style = {
    height: 20,
    width: 50,
    transition: customFunc(a, b, c)
    };
    return <div style={style} />;
    }
    ```

    Looks neat. Only works with style values.

    #### tweenState & variations
    ```js
    this.tweenState({
    method: easeOut,
    stateName: 'counter',
    params: {
    duration: 1000,
    beginValue: this.state.counter,
    endValue: this.state.counter + 1000
    }
    }, function(introspectableAnimationParams) {
    // Each frame callback.
    }, function() {
    // End callback.
    });
    ```

    Hard to scale up to animating a series of children, each of which has, say, two props to animate. In these cases where we need to animate values stored potentially deeply inside a collection, we could do `stateName: ['a', 1, 'b']` to instruct the API to tween the value in the state `{a: [{...}, {b: 5}, ...}]}`. Feels weird.

    #### Event handlers

    ```js
    handleDivEachFrame: function() {
    if (someAnimationCondition) {
    return {
    a: animationConfigForA,
    b: animationConfigForB
    };
    }
    // No animation.
    return null;
    },

    render: function() {
    return (
    <MyComp a={5} b={6} c={7} onEachFrame={this.handleDivEachFrame}>
    <div>bla</div>
    </MyComp>
    );
    }
    ```

    Not sure about this one.

    ### Unmounting

    ```js
    var App = React.createClass({
    componentWillUpdate: function(nextProps, nextState) {
    @@ -234,7 +286,7 @@ var App = React.createClass({
    });
    ```

    Animation is almost always a reaction to props/state change. We probably don't even need to cater to the other edge cases.
    Not sure how this fits in the general animation API.

    It'd be nice to make use of lifecycle return values, although I'm not sure whether passing the animation diff config is its best use (in case it's not, we can use the commented out API). This forks the render into whatever config we've passed (nice thing about returning it is it might be easier to have control over when to actually start/stop the animation, etc.), automate it until it reaches the normal, post-animation `render`.

    @@ -286,9 +338,14 @@ var App = React.createClass({

    Would be great if animation doesn't get in the way of render, even in this situation. In general, this screws up stuff like `this.props.children` so the whole thing's just food for thought for now.

    ## Perf concerns
    Assuming we do go down the road of animating through using rAF on the render, we should leverage React's upcoming rAF batching to avoid layout thrashing. Not developing this section until we nail down an API.

    ## Other Thoughts
    core.async might be interesting. Maybe this is a better way to coordinate stuff than pure promises for, say, `setState`.

    `key` (identity in general) needs to be more powerful: https://groups.google.com/forum/#!topic/reactjs/ySPp8ksaDAc not just for perf reasons, but also to further help the reconciler to know where we moved items.

    Layout is a headache because it adds a bit of manual management each time. Any current layout system isn't flexible/powerful enough to cooperate with animated physics like chat heads.

    Om leverages Clojure's persistent data structures so well that it's basically a better React without the handicap of JS. We might want to look more at it in the future for layout and animation (instruments, tx-listen, etc.
  16. chenglou revised this gist Apr 14, 2014. 1 changed file with 10 additions and 4 deletions.
    14 changes: 10 additions & 4 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -29,6 +29,11 @@ CSS doesn't let us specify custom transitions beyond ease/linear/etc., and can o
    ### TransitionGroup
    TransitionGroup's does 80% of the job with 20% of the effort. We'll have to rely on it for a while. See last section on unmounting.

    ### What We're Not Going to Do
    One prototype used something conceptually similar to CSS animations: the render is not touched. Under the hood, we track the styles applied and interpolate them. This is already much more powerful than CSS animations because we can specify custom animations.

    The problem with this is that unmounting potentially becomes hacky. furthermore, it just feels more _right_ to automate render to achieve animation (also see stress test 4).

    ## Where The Controls Belong
    At the beginning, it wasn't even clear whether the top-down approach worked well for animation. Layout certainly didn't. However, so far, React's top-down approach seems to translate well. Whatever API it is, it seems that it's better for the owner to control the animation of the children. This way, children stay totally oblivious of being animated. For practical purposes, we can wrap the children in a `PredefinedAnimationConvenienceWrapper`.

    @@ -137,9 +142,6 @@ The catch with this is that it changes the logic we put in some lifecycle events
    ### Push Everything Down to DOMComponent
    The second way is to stay in the current paradigm and only make `ReactDOMComponent`s have a timeline every frame. This isn't just an implementation/optimization detail compared to the previous point. All the animation configurations are squashed to the leaves. The interesting part with this paradigm is that instead of relying on repeatedly rendering through raf, we can plug an existing animation engine, e.g. CSS or something else.

    ### CSS-style
    Render stays render. No extra render executed. We just bring CSS animation to JS and expose better hooks and a more flexible API. I'm not keen on this one: see stress test 4.

    ## Animating Multiple Instances
    Storing the animation configurations in the parent, in a state, quickly becomes tedious. Lots of previous values that can be computer on-the-fly will now be stored inside the state (e.g. children position. Layout also becomes a headache here as we move again from relative and start manually placing everything).

    @@ -166,6 +168,8 @@ See "Describing UI Through Time" above. If we take the first route then we might
    ## Unmounting
    Huge headache, as this is the only case where data can go potentially out of sync.

    I've found that one heuristic that leads us onto the right path is if the animation's reversible. Just like Om can easily implement undo through persistent data structures, unmounting animation (and animation in general) should theoretically be something that we can rewind. This further explains the section "What We're Not Going to Do".

    TransitionGroup is one way to do it. The problem with it is that it reimplemented a small, non-customizable reconciler in itself. This is the source of many bugs, and many unfixable ones if we don't expose the reconciler's diffing step and let user control it. A simple way to break it is to pass `[1, 2, 3]` then `setState` to `[2, 4]`. Currently the rendered result will be `[1, 2, 4, 3]`. `setState` again during this period and... you get the point. We have no saying over how the diffing's done.

    The goes deeper as TransitionGroup assumes that things don't get out of sync _too_ long. During this period of transitioning, it reports a different child count than the parent.
    @@ -195,6 +199,8 @@ A few assumptions are made here:

    In this case, `key` wouldn't just be the identity of the current component, but the identity of this component at _any moment_; It's effectively a hash that allows you to identify the component that might unmount halfway and come back before disappearing. if it's not `key`, we need _some_ way to tell the reconciler that the component coming back is the same that was being unmounted (diffing through time!).

    Interesting way to make diff efficient: http://youtu.be/DMtwq3QtddY?t=20m32s

    ## API

    ```js
    @@ -285,4 +291,4 @@ core.async might be interesting. Maybe this is a better way to coordinate stuff

    `key` (identity in general) needs to be more powerful: https://groups.google.com/forum/#!topic/reactjs/ySPp8ksaDAc not just for perf reasons, but also to further help the reconciler to know where we moved items.

    Layout is a headache because it adds a bit of manual management each time. Any current layout system isn't flexible/powerful enough to cooperate with physics like chat heads.
    Layout is a headache because it adds a bit of manual management each time. Any current layout system isn't flexible/powerful enough to cooperate with animated physics like chat heads.
  17. chenglou revised this gist Apr 11, 2014. 1 changed file with 134 additions and 134 deletions.
    268 changes: 134 additions & 134 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -36,47 +36,47 @@ At the beginning, it wasn't even clear whether the top-down approach worked well
    Every time I seem to end up putting animation configurations in lifecycle events. This is no better than jQuery, and it feels like no amount of sugar and helpers can transform these APIs into a truly declarative one. Based on the assumptions above, the better way to do it would be to find way to tell React what the UI looks like not at one single point, but through a period of time. But every time I try to describe the render through this "period of time", I end up automating the render for a while:

    ```js
    // Milliseconds counter, except instead of increasing at a regular interval, it
    // does a cool slowing-down effect near each 1000 ms.
    var Counter = React.createClass({
    getInitialState: function() {
    return {
    counter: 0
    };
    },

    incrementCounter: function() {
    // Promise, because.
    return this.interpolate({
    method: easeOut,
    params: {
    duration: 1000,
    beginValue: this.state.counter,
    endValue: this.state.counter + 1000
    }
    }, function(animationParams) {
    // Each frame callback. This is essentially a thin raf wrapper (nothing
    // wrong with that!).
    this.setState({counter: animationParams.currentValue});
    }.bind(this));
    },

    componentDidMount: function() {
    function repeat() {
    this.incrementCounter().then(repeat);
    }

    repeat();
    },

    render: function() {
    return (
    <div>
    {this.state.counter}
    </div>
    );
    // Milliseconds counter, except instead of increasing at a regular interval, it
    // does a cool slowing-down effect near each 1000 ms.
    var Counter = React.createClass({
    getInitialState: function() {
    return {
    counter: 0
    };
    },

    incrementCounter: function() {
    // Promise, because.
    return this.interpolate({
    method: easeOut,
    params: {
    duration: 1000,
    beginValue: this.state.counter,
    endValue: this.state.counter + 1000
    }
    });
    }, function(animationParams) {
    // Each frame callback. This is essentially a thin raf wrapper (nothing
    // wrong with that!).
    this.setState({counter: animationParams.currentValue});
    }.bind(this));
    },

    componentDidMount: function() {
    function repeat() {
    this.incrementCounter().then(repeat);
    }

    repeat();
    },

    render: function() {
    return (
    <div>
    {this.state.counter}
    </div>
    );
    }
    });
    ```

    This was one of the initial APIs. It solves some problems and is even capable enough to emulate some physics. Imo, the problem with these kind of jQuery-ish APIs is that you have to go out of your way to stop the animation, queue them, etc., no matter how short and sugary they might look like. Manually coordinating feels wrong when React potentially offers something better. Other frameworks probably (no research here) just set some flags to prevent the same animation (judged based on, say, equal destination and interpolation method).
    @@ -94,42 +94,42 @@ Owner owns child, Layer2 belongs to owner. Child has its own internal timeline.
    You can have code on arbitrary frames and they'll get executed when it's reached. This might be appealing or repulsive:

    ```js
    // Same counter as above.
    var Counter = React.createClass({
    getInitialState: function() {
    return {
    stepper: 0
    };
    },

    componentDidMount: function() {
    this.play();
    },

    componentWillUpdate: function() {
    switch (this.currentFrame) {
    case 1:
    this.setNextState({stepper: this.state.stepper + 1000});
    break;
    case 1000:
    // Best API or worst API?
    this.gotoAndPlay(1);
    break;
    // Same counter as above.
    var Counter = React.createClass({
    getInitialState: function() {
    return {
    stepper: 0
    };
    },

    componentDidMount: function() {
    this.play();
    },

    componentWillUpdate: function() {
    switch (this.currentFrame) {
    case 1:
    this.setNextState({stepper: this.state.stepper + 1000});
    break;
    case 1000:
    // Best API or worst API?
    this.gotoAndPlay(1);
    break;
    }
    },

    render: function() {
    var stepper = this.state.stepper;
    return (
    <div>
    {
    // Begin, end, currentTime, duration
    easeOut(stepper, stepper + 1000, this.currentFrame, 1000)
    }
    },

    render: function() {
    var stepper = this.state.stepper;
    return (
    <div>
    {
    // Begin, end, currentTime, duration
    easeOut(stepper, stepper + 1000, this.currentFrame, 1000)
    }
    </div>
    );
    }
    });
    </div>
    );
    }
    });
    ```

    The catch with this is that it changes the logic we put in some lifecycle events such as `willReceiveProps`. The task of "playing animation when the parent rerenders" doesn't make sense anymore (aka, you can't start an animation configuration in `willReceiveProps`), since the parent could be rerendering at every frame. People have pointed out that we can still `setAnimationConfig` and ignore its subsequent, redundant calls, but imo this doesn't feel right.
    @@ -198,34 +198,34 @@ In this case, `key` wouldn't just be the identity of the current component, but
    ## API

    ```js
    var App = React.createClass({
    componentWillUpdate: function(nextProps, nextState) {
    var render1 = this.render(this.props, this.state);
    var render2 = this.render(nextProps, nextState);
    var diff = React.Reconciler.diff(render1, render2);
    var configDiff = diff.map((item) => {
    if (item.transitioning) {
    return;
    }
    if (item.updateType === UPDATE) {
    return someAnimationConfigObject;
    } else if (item.updateType === REMOVE) {
    return someOtherNotSoDifferentConfigObject;
    }
    });

    return configDiff;
    // this.automateForkedRenderWithConfig(configDiff);
    },

    render: function() {
    return (
    <div>
    {this.props.items.map((item) => <div>{item}</div>)}
    </div>
    );
    var App = React.createClass({
    componentWillUpdate: function(nextProps, nextState) {
    var render1 = this.render(this.props, this.state);
    var render2 = this.render(nextProps, nextState);
    var diff = React.Reconciler.diff(render1, render2);
    var configDiff = diff.map((item) => {
    if (item.transitioning) {
    return;
    }
    if (item.updateType === UPDATE) {
    return someAnimationConfigObject;
    } else if (item.updateType === REMOVE) {
    return someOtherNotSoDifferentConfigObject;
    }
    });

    return configDiff;
    // this.automateForkedRenderWithConfig(configDiff);
    },

    render: function() {
    return (
    <div>
    {this.props.items.map((item) => <div>{item}</div>)}
    </div>
    );
    }
    });
    ```

    Animation is almost always a reaction to props/state change. We probably don't even need to cater to the other edge cases.
    @@ -238,44 +238,44 @@ We should avoid declaring a duration in the API; rather, for each animation conf
    Providing that the above unmounting idea is realistic, we'll have this concept of combined renders, where the same component, seen only once in a render, can now appear twice. Ignoring the fact that this is a massive diffing fail (can be avoided), we can intentionally cause this and use it for crossfading and swiping between images. For the latter example, at one point during the transitioning out of an image and the transitioning in of the next one, we see two images on the screen. Without animation, our code would look like:

    ```js
    var App = React.createClass({
    getInitialState: function() {...},

    handleClick: function() {
    this.setState({...});
    },

    render: function(props, state) {
    return (
    <div onClick={this.handleClick}>
    <img key={state.currImageIndex} src={imageURLs[state.currImageIndex]} />
    </div>
    );
    }
    });
    var App = React.createClass({
    getInitialState: function() {...},

    handleClick: function() {
    this.setState({...});
    },

    render: function(props, state) {
    return (
    <div onClick={this.handleClick}>
    <img key={state.currImageIndex} src={imageURLs[state.currImageIndex]} />
    </div>
    );
    }
    });
    ```

    With animation, our render logic doesn't change!

    ```js
    var App = React.createClass({
    getInitialState: function() {...},

    handleClick: function() {
    var render1 = this.render({currImageIndex: this.state.currImageIndex});
    var render2 = this.render({currImageIndex: this.state.currImageIndex + 1});
    var mergedRender = React.Reconciler.merge(render1, render2);
    this.transitionWithThisRender(mergedRender, moreAnimationConfig, cb);
    },

    render: function(props, state) {
    return (
    <div onClick={this.handleClick}>
    <img key={state.currImageIndex} src={imageURLs[state.currImageIndex]} />
    </div>
    );
    }
    });
    var App = React.createClass({
    getInitialState: function() {...},

    handleClick: function() {
    var render1 = this.render({currImageIndex: this.state.currImageIndex});
    var render2 = this.render({currImageIndex: this.state.currImageIndex + 1});
    var mergedRender = React.Reconciler.merge(render1, render2);
    this.transitionWithThisRender(mergedRender, moreAnimationConfig, cb);
    },

    render: function(props, state) {
    return (
    <div onClick={this.handleClick}>
    <img key={state.currImageIndex} src={imageURLs[state.currImageIndex]} />
    </div>
    );
    }
    });
    ```

    Would be great if animation doesn't get in the way of render, even in this situation. In general, this screws up stuff like `this.props.children` so the whole thing's just food for thought for now.
  18. chenglou revised this gist Apr 11, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -86,7 +86,7 @@ This might be hard. I'd even say maybe as hard as unmounting, because this might
    ### Timeline
    In Flash, the components have their respective timeline. A ball that loops and spins over 30 frames can be nested inside an owner that spins over 60 frames. This is Flash's main way of composing.

    A component can have multiple timelines: ![Timelines](https://wiki.brown.edu/confluence/download/attachments/4415/Flash_timeline.gif?version=1&modificationDate=1152908704000).
    A component can have multiple timelines: https://wiki.brown.edu/confluence/download/attachments/4415/Flash_timeline.gif?version=1&modificationDate=1152908704000.
    (Arrows are interpolations. Dots are keyframes.)

    Owner owns child, Layer2 belongs to owner. Child has its own internal timeline. Owner also assigns a timeline to it. _Both owner and child are oblivious of each other's timeline_. Aka, there's no way for a parent to stop its child's from playing its animations. It can only control the timeline it assigned to the child (unless the child expose an API through props).
  19. chenglou revised this gist Apr 11, 2014. 1 changed file with 12 additions and 2 deletions.
    14 changes: 12 additions & 2 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    Unmounting is at the end if you're not interested in the rest =).
    Interesting part (unmounting & API) is at the end if you're not interested in the rest =).

    ## Stress Tests
    This animation proposal is just an attempt. In case it doesn't work out, I've gathered a few examples that can test the power of a future animation system.
    @@ -35,6 +35,7 @@ At the beginning, it wasn't even clear whether the top-down approach worked well
    ## Describe UI Through Time
    Every time I seem to end up putting animation configurations in lifecycle events. This is no better than jQuery, and it feels like no amount of sugar and helpers can transform these APIs into a truly declarative one. Based on the assumptions above, the better way to do it would be to find way to tell React what the UI looks like not at one single point, but through a period of time. But every time I try to describe the render through this "period of time", I end up automating the render for a while:

    ```js
    // Milliseconds counter, except instead of increasing at a regular interval, it
    // does a cool slowing-down effect near each 1000 ms.
    var Counter = React.createClass({
    @@ -76,6 +77,7 @@ Every time I seem to end up putting animation configurations in lifecycle events
    );
    }
    });
    ```

    This was one of the initial APIs. It solves some problems and is even capable enough to emulate some physics. Imo, the problem with these kind of jQuery-ish APIs is that you have to go out of your way to stop the animation, queue them, etc., no matter how short and sugary they might look like. Manually coordinating feels wrong when React potentially offers something better. Other frameworks probably (no research here) just set some flags to prevent the same animation (judged based on, say, equal destination and interpolation method).

    @@ -84,13 +86,14 @@ This might be hard. I'd even say maybe as hard as unmounting, because this might
    ### Timeline
    In Flash, the components have their respective timeline. A ball that loops and spins over 30 frames can be nested inside an owner that spins over 60 frames. This is Flash's main way of composing.

    A component can have multiple timelines: https://wiki.brown.edu/confluence/download/attachments/4415/Flash_timeline.gif?version=1&modificationDate=1152908704000
    A component can have multiple timelines: ![Timelines](https://wiki.brown.edu/confluence/download/attachments/4415/Flash_timeline.gif?version=1&modificationDate=1152908704000).
    (Arrows are interpolations. Dots are keyframes.)

    Owner owns child, Layer2 belongs to owner. Child has its own internal timeline. Owner also assigns a timeline to it. _Both owner and child are oblivious of each other's timeline_. Aka, there's no way for a parent to stop its child's from playing its animations. It can only control the timeline it assigned to the child (unless the child expose an API through props).

    You can have code on arbitrary frames and they'll get executed when it's reached. This might be appealing or repulsive:

    ```js
    // Same counter as above.
    var Counter = React.createClass({
    getInitialState: function() {
    @@ -127,6 +130,7 @@ You can have code on arbitrary frames and they'll get executed when it's reached
    );
    }
    });
    ```

    The catch with this is that it changes the logic we put in some lifecycle events such as `willReceiveProps`. The task of "playing animation when the parent rerenders" doesn't make sense anymore (aka, you can't start an animation configuration in `willReceiveProps`), since the parent could be rerendering at every frame. People have pointed out that we can still `setAnimationConfig` and ignore its subsequent, redundant calls, but imo this doesn't feel right.

    @@ -193,6 +197,7 @@ In this case, `key` wouldn't just be the identity of the current component, but

    ## API

    ```js
    var App = React.createClass({
    componentWillUpdate: function(nextProps, nextState) {
    var render1 = this.render(this.props, this.state);
    @@ -221,6 +226,7 @@ In this case, `key` wouldn't just be the identity of the current component, but
    );
    }
    });
    ```

    Animation is almost always a reaction to props/state change. We probably don't even need to cater to the other edge cases.

    @@ -231,6 +237,7 @@ We should avoid declaring a duration in the API; rather, for each animation conf
    ## Crazier Stuff
    Providing that the above unmounting idea is realistic, we'll have this concept of combined renders, where the same component, seen only once in a render, can now appear twice. Ignoring the fact that this is a massive diffing fail (can be avoided), we can intentionally cause this and use it for crossfading and swiping between images. For the latter example, at one point during the transitioning out of an image and the transitioning in of the next one, we see two images on the screen. Without animation, our code would look like:

    ```js
    var App = React.createClass({
    getInitialState: function() {...},

    @@ -246,9 +253,11 @@ Providing that the above unmounting idea is realistic, we'll have this concept o
    );
    }
    });
    ```

    With animation, our render logic doesn't change!

    ```js
    var App = React.createClass({
    getInitialState: function() {...},

    @@ -267,6 +276,7 @@ With animation, our render logic doesn't change!
    );
    }
    });
    ```

    Would be great if animation doesn't get in the way of render, even in this situation. In general, this screws up stuff like `this.props.children` so the whole thing's just food for thought for now.

  20. chenglou revised this gist Apr 11, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -84,7 +84,7 @@ This might be hard. I'd even say maybe as hard as unmounting, because this might
    ### Timeline
    In Flash, the components have their respective timeline. A ball that loops and spins over 30 frames can be nested inside an owner that spins over 60 frames. This is Flash's main way of composing.

    A component can have multiple timelines: ![Timelines](https://wiki.brown.edu/confluence/download/attachments/4415/Flash_timeline.gif?version=1&modificationDate=1152908704000).
    A component can have multiple timelines: https://wiki.brown.edu/confluence/download/attachments/4415/Flash_timeline.gif?version=1&modificationDate=1152908704000
    (Arrows are interpolations. Dots are keyframes.)

    Owner owns child, Layer2 belongs to owner. Child has its own internal timeline. Owner also assigns a timeline to it. _Both owner and child are oblivious of each other's timeline_. Aka, there's no way for a parent to stop its child's from playing its animations. It can only control the timeline it assigned to the child (unless the child expose an API through props).
  21. chenglou created this gist Apr 11, 2014.
    278 changes: 278 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,278 @@
    Unmounting is at the end if you're not interested in the rest =).

    ## Stress Tests
    This animation proposal is just an attempt. In case it doesn't work out, I've gathered a few examples that can test the power of a future animation system.

    - Parent is an infinitely spinning ball, and has a child ball that is also spinning. Clicking on the parent causes child to reverse spinning direction. This tests the ability of the animation system to compose animation, not in the sense of applying multiple interpolations to one or more variables passed onto the child (this should be trivial), but in the sense that the parent's constantly updating at the same time as the child, and has to ensure that it passes the animation commands correctly to it. This also tests that we can still intercept these animations (the clicking) and immediately change their configuration instead of queueing them.

    - Typing letters and let them fly in concurrently. This tests concurrency, coordination of an array of children, whose positions depend on each other, and mounting and unmounting. Pretty much _the_ stress test.

    - Three boxes, from different hierarchies/owners, colliding and falling. The animating API doesn't need to (and shouldn't) implement its own physics engine; it should be able to reuse existing ones. Additionally, these animations might need a global knowledge of other components' animation configurations.

    - A millisecond counter that uses easeout to increase its value at every second. This tests the system's ability to interpolate arbitrary values rather than, say, only CSS props.

    ## Goal
    Find an animation API that leverages React's declarative paradigm rather than the traditional imperative one (e.g. jQuery). Note that jQuery's animations themselves aren't that imperative; it's more because of the traditional way these methods fit with the library.

    ## Why It Is Hard
    A major problem is that animation is tied to layout. Without a powerful layout system, everything seems much more tedious to write. I won't be solving layout problems here.

    A bigger problem is that React's current model of render as snapshots doesn't play well with time. Realistically, JS' mutative nature also doesn't play well with time and comparison between past & present & future. Lots of logics in the animation system will depend on knowing about the past and future.

    ## Why It Is Relatively Easy for Imperative Frameworks
    In imperative frameworks, time isn't even a concern (which is not a good thing, but it does make hacking around it easier). The state of the app can end up somewhere else after a tween and the frameworks don't seek to control that. Time is simulated manually where needed, same for OOP in general: http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey

    ## Current Solutions
    ### CSS
    CSS doesn't let us specify custom transitions beyond ease/linear/etc., and can only awkwardly specify animation. Since it doesn't tie into render's logic much, it feels like templating, for animation. Other shortcomings include no finer grained control over starting/stopping animation. When a render kicks in at an arbitrary moment, you lose information on the animation in the previous render, unless you do the bookkeeping yourself. This can get complicated. Also, no good hooks.

    ### TransitionGroup
    TransitionGroup's does 80% of the job with 20% of the effort. We'll have to rely on it for a while. See last section on unmounting.

    ## Where The Controls Belong
    At the beginning, it wasn't even clear whether the top-down approach worked well for animation. Layout certainly didn't. However, so far, React's top-down approach seems to translate well. Whatever API it is, it seems that it's better for the owner to control the animation of the children. This way, children stay totally oblivious of being animated. For practical purposes, we can wrap the children in a `PredefinedAnimationConvenienceWrapper`.

    ## Describe UI Through Time
    Every time I seem to end up putting animation configurations in lifecycle events. This is no better than jQuery, and it feels like no amount of sugar and helpers can transform these APIs into a truly declarative one. Based on the assumptions above, the better way to do it would be to find way to tell React what the UI looks like not at one single point, but through a period of time. But every time I try to describe the render through this "period of time", I end up automating the render for a while:

    // Milliseconds counter, except instead of increasing at a regular interval, it
    // does a cool slowing-down effect near each 1000 ms.
    var Counter = React.createClass({
    getInitialState: function() {
    return {
    counter: 0
    };
    },

    incrementCounter: function() {
    // Promise, because.
    return this.interpolate({
    method: easeOut,
    params: {
    duration: 1000,
    beginValue: this.state.counter,
    endValue: this.state.counter + 1000
    }
    }, function(animationParams) {
    // Each frame callback. This is essentially a thin raf wrapper (nothing
    // wrong with that!).
    this.setState({counter: animationParams.currentValue});
    }.bind(this));
    },

    componentDidMount: function() {
    function repeat() {
    this.incrementCounter().then(repeat);
    }

    repeat();
    },

    render: function() {
    return (
    <div>
    {this.state.counter}
    </div>
    );
    }
    });

    This was one of the initial APIs. It solves some problems and is even capable enough to emulate some physics. Imo, the problem with these kind of jQuery-ish APIs is that you have to go out of your way to stop the animation, queue them, etc., no matter how short and sugary they might look like. Manually coordinating feels wrong when React potentially offers something better. Other frameworks probably (no research here) just set some flags to prevent the same animation (judged based on, say, equal destination and interpolation method).

    This might be hard. I'd even say maybe as hard as unmounting, because this might change how we think of render. I've found a few ways to approach this.

    ### Timeline
    In Flash, the components have their respective timeline. A ball that loops and spins over 30 frames can be nested inside an owner that spins over 60 frames. This is Flash's main way of composing.

    A component can have multiple timelines: ![Timelines](https://wiki.brown.edu/confluence/download/attachments/4415/Flash_timeline.gif?version=1&modificationDate=1152908704000).
    (Arrows are interpolations. Dots are keyframes.)

    Owner owns child, Layer2 belongs to owner. Child has its own internal timeline. Owner also assigns a timeline to it. _Both owner and child are oblivious of each other's timeline_. Aka, there's no way for a parent to stop its child's from playing its animations. It can only control the timeline it assigned to the child (unless the child expose an API through props).

    You can have code on arbitrary frames and they'll get executed when it's reached. This might be appealing or repulsive:

    // Same counter as above.
    var Counter = React.createClass({
    getInitialState: function() {
    return {
    stepper: 0
    };
    },

    componentDidMount: function() {
    this.play();
    },

    componentWillUpdate: function() {
    switch (this.currentFrame) {
    case 1:
    this.setNextState({stepper: this.state.stepper + 1000});
    break;
    case 1000:
    // Best API or worst API?
    this.gotoAndPlay(1);
    break;
    }
    },

    render: function() {
    var stepper = this.state.stepper;
    return (
    <div>
    {
    // Begin, end, currentTime, duration
    easeOut(stepper, stepper + 1000, this.currentFrame, 1000)
    }
    </div>
    );
    }
    });

    The catch with this is that it changes the logic we put in some lifecycle events such as `willReceiveProps`. The task of "playing animation when the parent rerenders" doesn't make sense anymore (aka, you can't start an animation configuration in `willReceiveProps`), since the parent could be rerendering at every frame. People have pointed out that we can still `setAnimationConfig` and ignore its subsequent, redundant calls, but imo this doesn't feel right.

    ### Push Everything Down to DOMComponent
    The second way is to stay in the current paradigm and only make `ReactDOMComponent`s have a timeline every frame. This isn't just an implementation/optimization detail compared to the previous point. All the animation configurations are squashed to the leaves. The interesting part with this paradigm is that instead of relying on repeatedly rendering through raf, we can plug an existing animation engine, e.g. CSS or something else.

    ### CSS-style
    Render stays render. No extra render executed. We just bring CSS animation to JS and expose better hooks and a more flexible API. I'm not keen on this one: see stress test 4.

    ## Animating Multiple Instances
    Storing the animation configurations in the parent, in a state, quickly becomes tedious. Lots of previous values that can be computer on-the-fly will now be stored inside the state (e.g. children position. Layout also becomes a headache here as we move again from relative and start manually placing everything).

    This can potentially get messy (but maybe I'm wrong). This might either be a conceptual problem or something that a good API can solve.

    ## Physics
    Physics is needed and can be used 90% of the time. However, it'll never be a silver bullet. At first glance, physics seems to be appealing because, among other reasons, it seems declarative and abstracts time away. We don't ever provide an interpolation: we only specify friction, weight, etc. But the reality is that UI breaks the assumptions of a real-world physics system a _lot_ of times:

    - Button turns from green to red.

    - Lightbox enlarges and covers up the screen.

    - Box fades away while shrinking.

    - Assuming the animation system only allowed physics, it's even hard to specify a menu dropdown animation. Sure, you can imagine it as a heavy, horizontal rod placed at the end of the menu that drags it down until the menu stretches too much and bounce back a little; but it actually becomes, imo, difficult to reason about when you're attaching/removing strings everywhere and changing an object's weight and friction, then change it back, just so that it could move across the screen a certain way. This is viable, but I can't imagine how we can incorporate nothing but a physics-only system, in a paradigm that renders snapshots of the UI at a certain point in time, on a UI paradigm which magically move, stack and merge visual entities.

    That being said, physics is the way to go. But if we go full physics, we'll have to provide an additional API for the above cases of animation.

    Physics need to know more than the little information about begin/duration/whatever that we provide. Lots of times, it needs to know the animating state of parent, siblings, etc. If we want this to work reasonably in React's top-down model without passing callbacks everywhere, we'll need the animation manager to be at top level and all-knowing.

    ## Animation Across Hierarchies
    See "Describing UI Through Time" above. If we take the first route then we might have to rethink about how some lifecycle methods work, and expose new ones.

    ## Unmounting
    Huge headache, as this is the only case where data can go potentially out of sync.

    TransitionGroup is one way to do it. The problem with it is that it reimplemented a small, non-customizable reconciler in itself. This is the source of many bugs, and many unfixable ones if we don't expose the reconciler's diffing step and let user control it. A simple way to break it is to pass `[1, 2, 3]` then `setState` to `[2, 4]`. Currently the rendered result will be `[1, 2, 4, 3]`. `setState` again during this period and... you get the point. We have no saying over how the diffing's done.

    The goes deeper as TransitionGroup assumes that things don't get out of sync _too_ long. During this period of transitioning, it reports a different child count than the parent.

    So, the current execution timeline:

    parent setState -> render -> child render -> TransitionGroup mini reconciler

    There has to be a way to include unmounting in a general concept rather than special-casing it. Let's take `[1, 2, 3]` -> `[1, 3]` as example. My new starting point was to consider unmounting as it is: after `props.items` has shrunken, no hack should bring the lost item back. This means that any transitioning out animation that we do will assume that we're working with the old `props.items` (where the item is still present). This also means the reconciler needs to diff this correctly, since if multiple items are coming in and going out, the child that renders the parent's props will have at one point many more than 3 items.

    TransitionGroup's idea itself is not bad; we just have to implement this at React level and make it leverage the actual (customizable) reconciler. The new flow:

    parent setState -> render promise -> child willReceiveProps (+ render preview) -> automated forked render -> render

    The "automated render" part starts at the point _before_ the new, shrunken array has been written as `this.props`: (could be done in `willReceiveProps`). The magical part here is to _preview the next render_ result and apply a diff on top of the current one. We then target these diffed components and command animation configurations on them. API-wise this gets tricky, as I'm still trying to figure out where to actually put this configuration.

    At the end of this, the final, actual child `render` with the new props from parent is called. Note that the parent's `state.items` is already updated at step 2.
    A few assumptions are made here:

    - Things stay as immutable as possible. Screwing around deeply nested objects in props/state will not end well.

    - `key` plays a very critical role here, diffing incorrectly in the past was fine: the render can unmount/re-mount the same thing and the result still looks correct. But animation will expose the flaw very visually if you don't diff the items correctly, e.g. the wrong item might be moving around.

    - To enable preview, `render` should become a function of `props` and `state` and should not rely on `this.props` and `this.state`: https://github.com/facebook/react/issues/1387. As a side note, in an ideal world, `render` will return a plain JS object without actually changing the view. Other rising libraries might even take these objects and render them differently.

    **Note**: The magical (and expensive) part is if we can stack all the diffs through time: `[1, 2]` -> `[1]` -> `[1, 2]` should bring the almost unmounted `2` back visually, even though the instance is destroyed and recreated.

    In this case, `key` wouldn't just be the identity of the current component, but the identity of this component at _any moment_; It's effectively a hash that allows you to identify the component that might unmount halfway and come back before disappearing. if it's not `key`, we need _some_ way to tell the reconciler that the component coming back is the same that was being unmounted (diffing through time!).

    ## API

    var App = React.createClass({
    componentWillUpdate: function(nextProps, nextState) {
    var render1 = this.render(this.props, this.state);
    var render2 = this.render(nextProps, nextState);
    var diff = React.Reconciler.diff(render1, render2);
    var configDiff = diff.map((item) => {
    if (item.transitioning) {
    return;
    }
    if (item.updateType === UPDATE) {
    return someAnimationConfigObject;
    } else if (item.updateType === REMOVE) {
    return someOtherNotSoDifferentConfigObject;
    }
    });

    return configDiff;
    // this.automateForkedRenderWithConfig(configDiff);
    },

    render: function() {
    return (
    <div>
    {this.props.items.map((item) => <div>{item}</div>)}
    </div>
    );
    }
    });

    Animation is almost always a reaction to props/state change. We probably don't even need to cater to the other edge cases.

    It'd be nice to make use of lifecycle return values, although I'm not sure whether passing the animation diff config is its best use (in case it's not, we can use the commented out API). This forks the render into whatever config we've passed (nice thing about returning it is it might be easier to have control over when to actually start/stop the animation, etc.), automate it until it reaches the normal, post-animation `render`.

    We should avoid declaring a duration in the API; rather, for each animation configuration, we call a callback each frame and check whether the return value is `true`. This generalizes well to duration-less animations such as physics. This way, we can leverage whatever third-party animation library rather than reinventing the wheel.

    ## Crazier Stuff
    Providing that the above unmounting idea is realistic, we'll have this concept of combined renders, where the same component, seen only once in a render, can now appear twice. Ignoring the fact that this is a massive diffing fail (can be avoided), we can intentionally cause this and use it for crossfading and swiping between images. For the latter example, at one point during the transitioning out of an image and the transitioning in of the next one, we see two images on the screen. Without animation, our code would look like:

    var App = React.createClass({
    getInitialState: function() {...},

    handleClick: function() {
    this.setState({...});
    },

    render: function(props, state) {
    return (
    <div onClick={this.handleClick}>
    <img key={state.currImageIndex} src={imageURLs[state.currImageIndex]} />
    </div>
    );
    }
    });

    With animation, our render logic doesn't change!

    var App = React.createClass({
    getInitialState: function() {...},

    handleClick: function() {
    var render1 = this.render({currImageIndex: this.state.currImageIndex});
    var render2 = this.render({currImageIndex: this.state.currImageIndex + 1});
    var mergedRender = React.Reconciler.merge(render1, render2);
    this.transitionWithThisRender(mergedRender, moreAnimationConfig, cb);
    },

    render: function(props, state) {
    return (
    <div onClick={this.handleClick}>
    <img key={state.currImageIndex} src={imageURLs[state.currImageIndex]} />
    </div>
    );
    }
    });

    Would be great if animation doesn't get in the way of render, even in this situation. In general, this screws up stuff like `this.props.children` so the whole thing's just food for thought for now.

    ## Other Thoughts
    core.async might be interesting. Maybe this is a better way to coordinate stuff than pure promises for, say, `setState`.

    `key` (identity in general) needs to be more powerful: https://groups.google.com/forum/#!topic/reactjs/ySPp8ksaDAc not just for perf reasons, but also to further help the reconciler to know where we moved items.

    Layout is a headache because it adds a bit of manual management each time. Any current layout system isn't flexible/powerful enough to cooperate with physics like chat heads.