-
-
Save natew/f0c396442584b071915f to your computer and use it in GitHub Desktop.
<ViewList animator> // passes context: { step: 0.555 } | |
<View /> // extends context with { index: 0 } | |
<View /> // ... | |
<View> // extends context with { index: 2 } | |
// here's the tricky part: you can nest ViewLists, and they should pass their "new" context down to their children | |
<ViewList> // reset context { step: 0 } | |
<View /> // reset context { index: 0 } | |
</ViewList> | |
</View> | |
</ViewList> | |
Where a View has nested animations: | |
<View> // animates inner view | |
<TitleBar> // animates slower than view | |
<Button> | |
<Icon animated> // animates against titlebar to stay in same place | |
</Button> | |
</TitleBar> | |
</View> |
Whats actually passed down from ViewList:
animations: {
viewList: {
step: number
}
}
Then from View extends that
animations: {
viewList: {
step: number,
index: number,
width: number,
height: number
}
}
With observable, I guess it would be:
ViewList
animations: { viewList: Observable }}
View would then need to push extra stuff onto the observable in it's own context tree. I'm actually struggling more with performance so if this would help avoid re-render on ViewList would be a big win.
So the requirements as I understand:
- You need to push animation state updates to components which are interested in them
- You want to avoid re-renders of components which are not interested in animation state updates
- You want to configure animation state transformations based on component tree configuration
What I propose is to configure observable graph in getChildContext
of components.
The top level one would have:
getChildContext() {
return {animation: this.animationObservable}
}
Some children can override observables by transforming current one from own context (shift in time for example):
getChildContext() {
return {animation: this.context.animation.map(x => {...x, step: x.step + 100})}
}
Another children who perform animations would subscribe to observable and re-render themselves when new value is available:
componentWillMount() {
this.context.subscribe((animationState) => {
this.setState({animationState}) // now render is called and we can use this.state.animationState to influence DOM
})
}
.
That basically means that we use context only to configure "pipes" for animation state, how it flows through component hierarchy.
Now to push new animationState we get some component and provide new values to its animation observable:
component.animationObservable.onNext({step: 0, ...})
That way we don't re-render components unless they subscribe to animationState
from context. But at the same time we don't need getChildContext
to be called because animationState-propagation graph already configured and is static.
Some unanswered questions are... what to do when we unmount/remount some components when animation is in progress? The animation graph could be reconfigured in this case (because getChildContext
of some components will be called again) and it can influence animation in some way, for example we can get some jumps in animation and so on. But I think this is a general problem, not specific to proposed setup.
As I understand index
reflects component hierarchy configuration in some way, it should be outside of observable in this case, I think. If step
is the one which is dynamic (time?) it should be an observable.
@andreypopp as for you concerns with unmount, I've run into this at least in one case, but it's actually not been bad. I do have a component called AnimationLoop and you could extend that type of component to keep it's children in state, etc.
Thanks for the thoughts here. Glad I started looking into this. I'm super busy at the moment but you can see the code diff here:
https://github.com/reapp/reapp-ui/compare/observeAnimations
It needs lots of bugfixing and optimization though.
To see an example of this, go to: http://kitchen.reapp.io/
And go into Nested View Lists demo (Or Dotted). Notice they are inside a viewlist already.