@@ -6,9 +6,7 @@ import Collector, {
66 SupplyRefHandler ,
77 CollectorChildrenAsFunction ,
88 CollectorData ,
9- CollectorChildrenProps ,
109 CollectorActions ,
11- InlineStyles ,
1210 TargetPropsFunc ,
1311 AnimationData ,
1412 AnimationCallback ,
@@ -18,69 +16,8 @@ import defer from '../lib/defer';
1816import noop from '../lib/noop' ;
1917import { precondition , warn } from '../lib/log' ;
2018import * as store from '../lib/animatorStore' ;
21- import { InjectedProps , withVisibilityManagerContext } from '../VisibilityManager' ;
22-
23- export type AnimationFunc = ( ) => Promise < void > ;
24-
25- export interface MappedAnimation {
26- animate : AnimationFunc ;
27- beforeAnimate : AnimationFunc ;
28- afterAnimate : AnimationFunc ;
29- cleanup : ( ) => void ;
30- }
31-
32- export type AnimationBlock = MappedAnimation [ ] ;
33-
34- export interface ChildProps {
35- style ?: InlineStyles ;
36- className ?: string ;
37- }
38-
39- export interface AnimatorState {
40- childProps : ChildProps ;
41- animationsMarkup : React . ReactPortal [ ] ;
42- }
43-
44- export interface AnimatorProps extends CollectorChildrenProps , InjectedProps {
45- /**
46- * Name of the animator, this should match the target animator.
47- */
48- name : string ;
49-
50- /**
51- * Will trigger animations over itself when this prop changes.
52- *
53- * You can't use the with the "in" prop.
54- */
55- triggerSelfKey ?: string ;
56-
57- /**
58- * Use if your element is expected to persist through an animation.
59- * When you transition to the next state set your "in" to false and vice versa.
60- * This lets the Animator components know when to execute the animations.
61- *
62- * You can't use this with the "triggerSelfKey".
63- */
64- in ?: boolean ;
65-
66- /**
67- * Callback called when all animations have finished and been cleaned up. Fired from the triggering Animator
68- * component.
69- */
70- onFinish : ( ) => void ;
71-
72- /**
73- * Time this component will wait until it throws away the animation.
74- * Defaults to 50ms, might want to bump it up if loading something that was code split.
75- */
76- timeToWaitForNextAnimator : number ;
77-
78- /**
79- * HTMLElement container used when creating elements for animations,
80- * generally only supporting animations will need this.
81- */
82- container : HTMLElement | ( ( ) => HTMLElement ) ;
83- }
19+ import { withVisibilityManagerContext } from '../VisibilityManager' ;
20+ import { AnimatorProps , AnimatorState , AnimationBlock } from './types' ;
8421
8522export default class Animator extends React . PureComponent < AnimatorProps , AnimatorState > {
8623 static displayName = 'Animator' ;
@@ -89,6 +26,7 @@ export default class Animator extends React.PureComponent<AnimatorProps, Animato
8926 onFinish : noop ,
9027 timeToWaitForNextAnimator : 50 ,
9128 container : document . body ,
29+ name : '' ,
9230 } ;
9331
9432 state : AnimatorState = {
@@ -111,7 +49,13 @@ export default class Animator extends React.PureComponent<AnimatorProps, Animato
11149 abortAnimations : ( ) => void = ( ) => undefined ;
11250
11351 componentDidMount ( ) {
114- const { in : componentIn , name } = this . props ;
52+ const { in : componentIn , name, triggerSelfKey } = this . props ;
53+
54+ if ( process . env . NODE_ENV === 'development' && ( ! triggerSelfKey && ! name ) ) {
55+ warn (
56+ '"name" prop needs to be defined. Without it you may have problems matching up animator targets. You will not get this error when using "triggerSelfKey" prop.'
57+ ) ;
58+ }
11559
11660 if ( componentIn === undefined && store . has ( name ) ) {
11761 // A child has already been stored, so this is probably the matching pair.
@@ -128,22 +72,23 @@ export default class Animator extends React.PureComponent<AnimatorProps, Animato
12872
12973 getSnapshotBeforeUpdate ( prevProps : AnimatorProps ) {
13074 if ( prevProps . in === true && this . props . in === false ) {
131- this . storeDOMData ( ) ;
75+ this . snapshotDOMData ( ) ;
13276 this . delayedClearStore ( ) ;
13377 this . abortAnimations ( ) ;
13478 }
13579
13680 if ( prevProps . triggerSelfKey !== this . props . triggerSelfKey ) {
137- this . storeDOMData ( ) ;
138- this . delayedClearStore ( ) ;
81+ return this . snapshotDOMData ( 'return' ) ;
13982 }
14083
141- // we can return snapshot here to circumvent the entire storing of dom data.
142- // would remove the need for setting a name!
14384 return null ;
14485 }
14586
146- componentDidUpdate ( prevProps : AnimatorProps , _ : AnimatorState ) {
87+ componentDidUpdate (
88+ prevProps : AnimatorProps ,
89+ _ : AnimatorState ,
90+ DOMSnapshot : store . AnimatorData | null
91+ ) {
14792 const inPropSame = this . props . in === prevProps . in ;
14893 const triggerSelfKeyPropSame = this . props . triggerSelfKey === prevProps . triggerSelfKey ;
14994
@@ -192,7 +137,7 @@ export default class Animator extends React.PureComponent<AnimatorProps, Animato
192137 // Make sure to keep react state the same for any inflight animations to be captured correctly.
193138 requestAnimationFrame ( ( ) => {
194139 this . abortAnimations ( ) ;
195- this . executeAnimations ( ) ;
140+ this . executeAnimations ( DOMSnapshot ) ;
196141 } ) ;
197142 }
198143 }
@@ -204,7 +149,7 @@ export default class Animator extends React.PureComponent<AnimatorProps, Animato
204149 return ;
205150 }
206151
207- this . storeDOMData ( ) ;
152+ this . snapshotDOMData ( ) ;
208153 this . delayedClearStore ( ) ;
209154 this . abortAnimations ( ) ;
210155 this . unmounting = true ;
@@ -226,64 +171,69 @@ export default class Animator extends React.PureComponent<AnimatorProps, Animato
226171 setTimeout ( ( ) => store . remove ( name ) , timeToWaitForNextAnimator ) ;
227172 }
228173
229- storeDOMData ( ) {
230- if ( this . unmounting ) {
231- return ;
232- }
233-
174+ snapshotDOMData ( action : 'store' | 'return' = 'store' ) : store . AnimatorData | null {
234175 // If there is only a Animator target and no child animations
235176 // data will be undefined, which means there are no animations to store.
236- if ( this . data ) {
237- if ( process . env . NODE_ENV === 'development' ) {
238- precondition (
239- this . element ,
240- `The ref was not set when trying to store data, check that a child element has a ref passed. This needs to be set so we can take a snapshot of the origin DOM element.
177+ if ( this . unmounting || ! this . data ) {
178+ return null ;
179+ }
180+
181+ if ( process . env . NODE_ENV === 'development' ) {
182+ precondition (
183+ this . element ,
184+ `The ref was not set when trying to store data, check that a child element has a ref passed. This needs to be set so we can take a snapshot of the origin DOM element.
241185
242186<${ Animator . displayName } name="${ this . props . name } ">
243187 {props => <div ref={props.ref} />}
244188</${ Animator . displayName } >
245189`
246- ) ;
247- }
190+ ) ;
191+ }
248192
249- const elementBoundingBox = getElementBoundingBox ( this . element as HTMLElement ) ;
250- const focalTargetElementBoundingBox = this . focalTargetElement
251- ? getElementBoundingBox ( this . focalTargetElement )
252- : undefined ;
193+ const elementBoundingBox = getElementBoundingBox ( this . element as HTMLElement ) ;
194+ const focalTargetElementBoundingBox = this . focalTargetElement
195+ ? getElementBoundingBox ( this . focalTargetElement )
196+ : undefined ;
253197
254- if ( process . env . NODE_ENV === 'development' && elementBoundingBox . size . height === 0 ) {
255- warn ( `Your target child had a height of zero when capturing it's DOM data. This may affect the animation.
198+ if ( process . env . NODE_ENV === 'development' && elementBoundingBox . size . height === 0 ) {
199+ warn ( `Your target child had a height of zero when capturing it's DOM data. This may affect the animation.
256200If it's an image, try and have the image loaded before mounting, or set a static height.` ) ;
257- }
258-
259- const { name } = this . props ;
201+ }
260202
261- // NOTE: Currently in react 16.3 if the parent being unmounted is a Fragment
262- // there is a chance for sibling elements to be removed from the DOM first
263- // resulting in inaccurate calculations of location. Watch out!
264- const data : store . AnimatorData = {
265- elementData : {
266- element : this . element as HTMLElement ,
267- elementBoundingBox,
268- focalTargetElement : this . focalTargetElement ,
269- focalTargetElementBoundingBox,
270- render : this . renderChildren ,
271- } ,
272- collectorData : this . data ,
273- } ;
203+ const { name } = this . props ;
204+
205+ // NOTE: Currently in react 16.3 if the parent being unmounted is a Fragment
206+ // there is a chance for sibling elements to be removed from the DOM first
207+ // resulting in inaccurate calculations of location. Watch out!
208+ const data : store . AnimatorData = {
209+ elementData : {
210+ element : this . element as HTMLElement ,
211+ elementBoundingBox,
212+ focalTargetElement : this . focalTargetElement ,
213+ focalTargetElementBoundingBox,
214+ render : this . renderChildren ,
215+ } ,
216+ collectorData : this . data ,
217+ } ;
218+
219+ if ( action === 'return' ) {
220+ return data ;
221+ }
274222
223+ if ( action === 'store' ) {
275224 store . set ( name , data ) ;
276225 }
226+
227+ return null ;
277228 }
278229
279- executeAnimations = ( ) => {
230+ executeAnimations = ( DOMSnapshot : store . AnimatorData | null = store . get ( this . props . name ) ) => {
280231 const { name, container : getContainer , context } = this . props ;
281232 const container = typeof getContainer === 'function' ? getContainer ( ) : getContainer ;
282- const fromTarget = store . get ( name ) ;
283233 let aborted = false ;
284234
285- if ( fromTarget ) {
286- const { collectorData, elementData } = fromTarget ;
235+ if ( DOMSnapshot ) {
236+ const { collectorData, elementData } = DOMSnapshot ;
287237 this . animating = true ;
288238
289239 // Calculate DOM data for the executing element to then be passed to the animation/s.
0 commit comments