UNPKG

react-navigation-shared-element

Version:
290 lines (287 loc) 11.1 kB
import { normalizeSharedElementsConfig } from "./utils"; function getSharedElements(sceneData, otherSceneData, showing) { const { sharedElements } = sceneData.Component; if (!sharedElements) return null; return normalizeSharedElementsConfig(sharedElements(sceneData.navigation, otherSceneData.navigation, showing)); } const NO_SHARED_ELEMENTS = []; /** * TODO * - [ ] Not all lifecycle events not emitted by stack when using gestures (close modal) */ export default class SharedElementRendererData { constructor() { this.scenes = []; this.updateSubscribers = new Set(); this.sharedElements = null; this.isShowing = true; this.route = null; this.prevRoute = null; this.scene = null; this.prevScene = null; this.isTransitionStarted = false; this.isTransitionClosing = false; this.transitionNavigatorId = ""; this.transitionNestingDepth = -1; this.debugRefCount = 0; } startTransition(closing, navigatorId, nestingDepth) { if (this.debug) console.debug(`[${navigatorId}]startTransition, closing: ${closing}, nestingDepth: ${nestingDepth}`); if (!this.isTransitionStarted || this.route) { this.prevRoute = this.route; this.route = null; this.routeAnimValue = null; // When a transition wasn't completely fully, but a new transition // has already started, then the `willBlur` event is not called. // For this particular case, we capture the animation-value of the // last (previous) scene that is now being hidden. if (this.isTransitionStarted) { const scene = this.getScene(this.prevRoute); if (scene) { this.routeAnimValue = scene.getAnimValue(true); } } this.isTransitionStarted = true; this.isTransitionClosing = closing; this.transitionNavigatorId = navigatorId; this.transitionNestingDepth = nestingDepth; } else { // When navigators are nested, `startTransition` may be called multiple // times. In such as case, we want to use the most shallow navigator, // as that is the one doing the transition. if (nestingDepth < this.transitionNestingDepth) { this.transitionNavigatorId = navigatorId; this.transitionNestingDepth = nestingDepth; } } } endTransition(closing, navigatorId, nestingDepth) { if (this.debug) console.debug(`[${navigatorId}]endTransition, closing: ${closing}, nestingDepth: ${nestingDepth}`); if (!this.isTransitionStarted || this.transitionNavigatorId !== navigatorId) { return; } this.isTransitionStarted = false; if (this.prevRoute != null) { this.prevRoute = null; this.routeAnimValue = null; this.updateSceneListeners(); this.updateSharedElements(); } } updateSceneState(sceneData, route, sceneEvent) { switch (sceneEvent) { case "willFocus": return this.willFocusScene(sceneData, route); case "didFocus": return this.didFocusScene(sceneData, route); //case "willBlur": //return this.willBlurScene(sceneData, route); } } addDebugRef() { return ++this.debugRefCount; } releaseDebugRef() { return --this.debugRefCount; } get debug() { return this.debugRefCount > 0; } willFocusScene(sceneData, route) { if (this.debug) console.debug(`[${sceneData.navigatorId}]willFocus, scene: "${sceneData.name}", depth: ${sceneData.nestingDepth}, closing: ${this.isTransitionClosing}`); this.registerScene(sceneData, route); // Wait for a transition start, before starting any animations if (!this.isTransitionStarted) return; // Use the animation value from the navigator that // started the transition if (this.prevRoute) { const scene = this.isTransitionClosing ? this.getScene(this.prevRoute) : sceneData; if (scene?.navigatorId === this.transitionNavigatorId) { this.routeAnimValue = scene?.getAnimValue(this.isTransitionClosing); } } // In case of nested navigators, multiple scenes will become // activated. Make sure to use the scene that is nested most deeply, // as this will be the one visible to the user if (!this.route) { this.route = route; } else { const routeScene = this.getScene(this.route); if (routeScene && routeScene.nestingDepth <= sceneData.nestingDepth) { this.route = route; } } // Update transition if (this.prevRoute && this.route && this.routeAnimValue) { this.updateSceneListeners(); this.updateSharedElements(); } } didFocusScene(sceneData, route) { if (this.debug) console.debug(`[${sceneData.navigatorId}]didFocus, scene: "${sceneData.name}", depth: ${sceneData.nestingDepth}`); if (!this.route || this.prevRoute) { this.route = route; } else { const routeScene = this.getScene(this.route); if (routeScene && routeScene.nestingDepth <= sceneData.nestingDepth) { this.route = route; } } this.registerScene(sceneData, route); } /*willBlurScene( sceneData: SharedElementSceneData, // @ts-ignore route: Route // eslint-disable-line @typescript-eslint/no-unused-vars ): void { if (this.debug) console.debug( `[${sceneData.navigatorId}]willBlur, scene: "${sceneData.name}", depth: ${sceneData.nestingDepth}` ); // Wait for a transition start, before starting any animations if (!this.isTransitionStarted) return; // Use the animation value from the navigator that // started the transition if ( this.isTransitionClosing && sceneData.navigatorId === this.transitionNavigatorId && !this.routeAnimValue ) { this.routeAnimValue = sceneData.getAnimValue(this.isTransitionClosing); } // Update transition if (this.prevRoute && this.route && this.routeAnimValue) { this.updateSceneListeners(); this.updateSharedElements(); } }*/ registerScene(sceneData, route) { this.scenes.push({ scene: sceneData, route, subscription: null }); if (this.scenes.length > 10) { const { subscription } = this.scenes[0]; this.scenes.splice(0, 1); if (subscription) subscription.remove(); } this.updateSceneListeners(); } updateSceneListeners() { this.scenes.forEach(sceneRoute => { const { scene, route, subscription } = sceneRoute; const isActive = (this.route && this.route.key === route.key) || (this.prevRoute && this.prevRoute.key === route.key); if (isActive && !subscription) { sceneRoute.subscription = scene.addUpdateListener(() => { // TODO optimize this.emitUpdateEvent(); }); } else if (!isActive && subscription) { sceneRoute.subscription = null; subscription.remove(); } }); } getScene(route) { const sceneRoute = route ? this.scenes.find(sc => sc.route.key === route.key) : undefined; return sceneRoute ? sceneRoute.scene : null; } updateSharedElements() { const { route, prevRoute, routeAnimValue } = this; const scene = this.getScene(route); const prevScene = this.getScene(prevRoute); const sceneAnimValue = routeAnimValue; // Update current scene & previous scene if (scene === this.scene && prevScene === this.prevScene && sceneAnimValue === this.sceneAnimValue) return; this.scene = scene; this.prevScene = prevScene; this.sceneAnimValue = sceneAnimValue; // Update shared elements let sharedElements = null; let isShowing = true; if (sceneAnimValue && scene && prevScene) { sharedElements = getSharedElements(scene, prevScene, true); if (!sharedElements) { isShowing = false; sharedElements = getSharedElements(prevScene, scene, false); } } if (this.sharedElements !== sharedElements) { if (this.debug) { if (sharedElements) { console.debug(`Transition start: "${prevScene?.name}" -> "${scene?.name}", elements: ${JSON.stringify(sharedElements, undefined, 2)}`); } else { console.debug(`Transition end: "${scene?.name}"`); } } this.sharedElements = sharedElements; this.isShowing = isShowing; /*console.log( 'updateSharedElements: ', sharedElements, ' ,isShowing: ', isShowing, ', animValue: ', animValue );*/ this.emitUpdateEvent(); } } addUpdateListener(handler) { this.updateSubscribers.add(handler); return { remove: () => this.updateSubscribers.delete(handler) }; } emitUpdateEvent() { this.updateSubscribers.forEach(handler => handler()); } getTransitions() { const { sharedElements, prevScene, scene, isShowing, sceneAnimValue, route } = this; if (!sharedElements || !scene || !prevScene || !route) return NO_SHARED_ELEMENTS; return sharedElements.map(({ id, otherId, ...other }) => { const startId = isShowing ? otherId || id : id; const endId = isShowing ? id : otherId || id; return { key: route.key, position: sceneAnimValue, start: { ancestor: (prevScene ? prevScene.getAncestor() : undefined) || null, node: (prevScene ? prevScene.getNode(startId) : undefined) || null }, end: { ancestor: (scene ? scene.getAncestor() : undefined) || null, node: (scene ? scene.getNode(endId) : undefined) || null }, ...other }; }); } get nestingDepth() { return 0; } } //# sourceMappingURL=SharedElementRendererData.js.map