Skip to content

Commit 9c4136d

Browse files
committed
feat: added new API which supports pop as well
BREAKING CHANGE: The old API where the shared elements could be passed as props has been dropped. Instead you should define a static variable called `sharedElements` on your Screen component. That variable can be either a function or a static array. See the README for more details
1 parent 1d84d83 commit 9c4136d

File tree

7 files changed

+149
-118
lines changed

7 files changed

+149
-118
lines changed

src/SharedElementRendererData.tsx

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import {
44
SharedElementsConfig,
55
SharedElementAnimatedValue,
66
SharedElementTransitionProps,
7+
NavigationProp,
78
} from './types';
9+
import { normalizeSharedElementsConfig } from './utils';
810

911
export type SharedElementRendererUpdateHandler = () => any;
1012

@@ -13,16 +15,33 @@ export interface ISharedElementRendererData {
1315
endTransition(): void;
1416
willActivateScene(
1517
sceneData: SharedElementSceneData,
16-
sharedElements: SharedElementsConfig,
17-
animValue?: SharedElementAnimatedValue
18+
navigation: NavigationProp
1819
): void;
19-
didActivateScene(sceneData: SharedElementSceneData): void;
20+
didActivateScene(
21+
sceneData: SharedElementSceneData,
22+
navigation: NavigationProp
23+
): void;
24+
}
25+
26+
function getSharedElements(
27+
sceneData: SharedElementSceneData,
28+
navigation: NavigationProp,
29+
otherNavigation: NavigationProp,
30+
show: boolean
31+
): SharedElementsConfig | null {
32+
const { sharedElements } = sceneData.Component;
33+
if (!sharedElements) return null;
34+
// TODO push/pop distinction?
35+
return normalizeSharedElementsConfig(
36+
sharedElements(navigation, otherNavigation, show)
37+
);
2038
}
2139

2240
export default class SharedElementRendererData
2341
implements ISharedElementRendererData {
2442
private sceneData: SharedElementSceneData | null = null;
2543
private prevSceneData: SharedElementSceneData | null = null;
44+
private prevNavigation: NavigationProp | null = null;
2645
private updateSubscribers = new Set<SharedElementRendererUpdateHandler>();
2746
private sceneSubscription: SharedElementEventSubscription | null = null;
2847
private sharedElements: SharedElementsConfig = [];
@@ -38,22 +57,27 @@ export default class SharedElementRendererData
3857

3958
willActivateScene(
4059
sceneData: SharedElementSceneData,
41-
sharedElements: SharedElementsConfig,
42-
animValue?: SharedElementAnimatedValue
60+
navigation: NavigationProp
4361
): void {
4462
/*console.log(
4563
'SharedElementRendererData.willActivateScene: ',
4664
sceneData.name,
4765
', previous: ',
48-
this.prevSceneData ? this.prevSceneData.name : '',
49-
', sharedElements: ',
50-
sharedElements
66+
this.prevSceneData ? this.prevSceneData.name : ''
5167
);*/
52-
this.sceneData = sceneData;
53-
if (!this.prevSceneData) return;
54-
this.sharedElements = sharedElements;
55-
if (animValue) this.animValue = animValue;
56-
if (sharedElements.length) {
68+
if (!this.prevSceneData || !this.prevNavigation) return;
69+
const sharedElements =
70+
getSharedElements(sceneData, navigation, this.prevNavigation, true) ||
71+
getSharedElements(
72+
this.prevSceneData,
73+
this.prevNavigation,
74+
navigation,
75+
false
76+
);
77+
if (sharedElements && sharedElements.length) {
78+
// console.log('sharedElements: ', sharedElements, sceneData);
79+
this.sceneData = sceneData;
80+
this.sharedElements = sharedElements;
5781
this.sceneSubscription = this.sceneData.addUpdateListener(() => {
5882
// TODO optimize
5983
this.emitUpdateEvent();
@@ -62,13 +86,17 @@ export default class SharedElementRendererData
6286
}
6387
}
6488

65-
didActivateScene(sceneData: SharedElementSceneData): void {
66-
// console.log('SharedElementRendererData.didActivateScene: ', sceneData.name);
89+
didActivateScene(
90+
sceneData: SharedElementSceneData,
91+
navigation: NavigationProp
92+
): void {
93+
//console.log('SharedElementRendererData.didActivateScene: ', sceneData.name);
6794
if (this.sceneSubscription) {
6895
this.sceneSubscription.remove();
6996
this.sceneSubscription = null;
7097
}
7198
this.prevSceneData = sceneData;
99+
this.prevNavigation = navigation;
72100
if (this.sceneData) {
73101
this.sceneData = null;
74102
if (this.sharedElements.length) {
@@ -93,20 +121,24 @@ export default class SharedElementRendererData
93121

94122
getTransitions(): SharedElementTransitionProps[] {
95123
const { prevSceneData, sceneData } = this;
96-
return this.sharedElements.map(({ id, sourceId, animation, debug }) => ({
97-
position: this.animValue,
98-
start: {
99-
ancestor:
100-
(prevSceneData ? prevSceneData.getAncestor() : undefined) || null,
101-
node:
102-
(prevSceneData ? prevSceneData.getNode(sourceId) : undefined) || null,
103-
},
104-
end: {
105-
ancestor: (sceneData ? sceneData.getAncestor() : undefined) || null,
106-
node: (sceneData ? sceneData.getNode(id) : undefined) || null,
107-
},
108-
...animation,
109-
debug,
110-
}));
124+
// console.log('getTransitions: ', sceneData, prevSceneData);
125+
return this.sharedElements.map(({ id, sourceId, animation, debug }) => {
126+
return {
127+
position: this.animValue,
128+
start: {
129+
ancestor:
130+
(prevSceneData ? prevSceneData.getAncestor() : undefined) || null,
131+
node:
132+
(prevSceneData ? prevSceneData.getNode(sourceId) : undefined) ||
133+
null,
134+
},
135+
end: {
136+
ancestor: (sceneData ? sceneData.getAncestor() : undefined) || null,
137+
node: (sceneData ? sceneData.getNode(id) : undefined) || null,
138+
},
139+
...animation,
140+
debug,
141+
};
142+
});
111143
}
112144
}

src/SharedElementRendererProxy.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import SharedElementRendererData, {
22
ISharedElementRendererData,
33
} from './SharedElementRendererData';
4-
import { SharedElementAnimatedValue, SharedElementsConfig } from './types';
4+
import { SharedElementAnimatedValue, NavigationProp } from './types';
55
import SharedElementSceneData from './SharedElementSceneData';
66

77
export class SharedElementRendererProxy implements ISharedElementRendererData {
@@ -29,26 +29,28 @@ export class SharedElementRendererProxy implements ISharedElementRendererData {
2929

3030
willActivateScene(
3131
sceneData: SharedElementSceneData,
32-
sharedElements: SharedElementsConfig,
33-
animValue?: SharedElementAnimatedValue
32+
navigation: NavigationProp
3433
) {
3534
if (!this.data) {
3635
console.warn(
3736
'SharedElementRendererProxy.willActivateScene called before Proxy was initialized'
3837
);
3938
return;
4039
}
41-
return this.data.willActivateScene(sceneData, sharedElements, animValue);
40+
return this.data.willActivateScene(sceneData, navigation);
4241
}
4342

44-
didActivateScene(sceneData: SharedElementSceneData) {
43+
didActivateScene(
44+
sceneData: SharedElementSceneData,
45+
navigation: NavigationProp
46+
) {
4547
if (!this.data) {
4648
console.warn(
4749
'SharedElementRendererProxy.didActivateScene called before Proxy was initialized'
4850
);
4951
return;
5052
}
51-
return this.data.didActivateScene(sceneData);
53+
return this.data.didActivateScene(sceneData, navigation);
5254
}
5355

5456
get source(): SharedElementRendererData | null {

src/SharedElementSceneData.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { SharedElementNode, SharedElementEventSubscription } from './types';
1+
import {
2+
SharedElementNode,
3+
SharedElementEventSubscription,
4+
SharedElementSceneComponent,
5+
} from './types';
26

37
export type SharedElementSceneUpdateHandlerEventType =
48
| 'ancestor'
@@ -17,10 +21,16 @@ export default class SharedElementSceneData {
1721
private nodes: {
1822
[key: string]: SharedElementNode;
1923
} = {};
24+
public readonly Component: SharedElementSceneComponent;
2025
public readonly name: string;
2126

22-
constructor(name: string) {
23-
this.name = name;
27+
constructor(Component: SharedElementSceneComponent) {
28+
this.Component = Component;
29+
this.name =
30+
Component.displayName ||
31+
Component.name ||
32+
(Component.constructor ? Component.constructor.name : undefined) ||
33+
'';
2434
}
2535

2636
getAncestor(): SharedElementNode | undefined {

src/createSharedElementScene.tsx

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ import hoistNonReactStatics from 'hoist-non-react-statics';
44
import { nodeFromRef } from 'react-native-shared-element';
55
import SharedElementSceneData from './SharedElementSceneData';
66
import SharedElementSceneContext from './SharedElementSceneContext';
7-
import { SharedElementsConfig, SharedElementEventSubscription } from './types';
7+
import {
8+
SharedElementEventSubscription,
9+
NavigationProp,
10+
SharedElementSceneComponent,
11+
} from './types';
812
import { ISharedElementRendererData } from './SharedElementRendererData';
9-
import { normalizeSharedElementsConfig } from './utils';
1013

1114
const styles = StyleSheet.create({
1215
container: {
@@ -15,7 +18,7 @@ const styles = StyleSheet.create({
1518
});
1619

1720
type PropsType = {
18-
navigation: any;
21+
navigation: NavigationProp;
1922
};
2023

2124
function getActiveRouteState(route: any): any {
@@ -31,19 +34,15 @@ function getActiveRouteState(route: any): any {
3134
}
3235

3336
function createSharedElementScene(
34-
Component: React.ComponentType<any>,
37+
Component: SharedElementSceneComponent,
3538
rendererData: ISharedElementRendererData
3639
): React.ComponentType<any> {
3740
class SharedElementSceneView extends React.PureComponent<PropsType> {
38-
private initial: boolean = true;
3941
private subscriptions: {
4042
[key: string]: SharedElementEventSubscription;
4143
} = {};
4244
private sceneData: SharedElementSceneData = new SharedElementSceneData(
43-
Component.displayName ||
44-
Component.name ||
45-
(Component.constructor ? Component.constructor.name : undefined) ||
46-
''
45+
Component
4746
);
4847

4948
componentDidMount() {
@@ -79,34 +78,17 @@ function createSharedElementScene(
7978
this.sceneData.setAncestor(nodeFromRef(ref));
8079
};
8180

82-
private getSharedElements(): SharedElementsConfig | null {
83-
const { navigation } = this.props;
84-
let sharedElements =
85-
// @ts-ignore
86-
navigation.getParam('sharedElements') || Component.sharedElements;
87-
if (!sharedElements) return null;
88-
if (typeof sharedElements === 'function') {
89-
sharedElements = sharedElements(navigation);
90-
}
91-
return sharedElements
92-
? normalizeSharedElementsConfig(sharedElements)
93-
: null;
94-
}
95-
9681
private onWillFocus = () => {
97-
const sharedElements = this.getSharedElements();
98-
if (sharedElements && this.initial) {
99-
this.initial = false;
100-
rendererData.willActivateScene(this.sceneData, sharedElements);
101-
}
82+
const { navigation } = this.props;
83+
rendererData.willActivateScene(this.sceneData, navigation);
10284
};
10385

10486
private onDidFocus = () => {
10587
const { navigation } = this.props;
10688
const activeRoute = getActiveRouteState(navigation.state);
10789
if (navigation.state.routeName === activeRoute.routeName) {
108-
// console.log('onDidFocus: ', this.sceneData.name, activeRoute, this.props.navigation.state.routeName);
109-
rendererData.didActivateScene(this.sceneData);
90+
// console.log('onDidFocus: ', this.sceneData.name, navigation);
91+
rendererData.didActivateScene(this.sceneData, navigation);
11092
}
11193
};
11294
}

src/createSharedElementStackNavigator.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as React from 'react';
2-
import { Animated } from 'react-native';
32
import hoistNonReactStatics from 'hoist-non-react-statics';
43
import SharedElementRendererView from './SharedElementRendererView';
54
import SharedElementRendererData, {
@@ -38,10 +37,14 @@ function createSharedElementEnabledNavigator(
3837
return createNavigator(wrappedRouteConfigs, {
3938
...navigatorConfig,
4039
onTransitionStart: (transitionProps: any, prevTransitionProps: any) => {
41-
if (transitionProps.index === prevTransitionProps.index) return;
42-
// console.log('onTransitionStart: ', transitionProps, prevTransitionProps);
40+
const { index, position } = transitionProps;
41+
const prevIndex = prevTransitionProps.index;
42+
if (index === prevIndex) return;
4343
rendererData.startTransition(
44-
Animated.subtract(transitionProps.position, transitionProps.index - 1)
44+
position.interpolate({
45+
inputRange: [index - 1, index],
46+
outputRange: index > prevIndex ? [0, 1] : [2, 1],
47+
})
4548
);
4649
if (navigatorConfig.onTransitionStart) {
4750
navigatorConfig.onTransitionStart(transitionProps, prevTransitionProps);

0 commit comments

Comments
 (0)