feat: support for independent transition tags and cleanup wip

This commit is contained in:
Nathan Walker
2023-03-20 23:44:07 -07:00
parent c2324a6293
commit 8b79a40ca9
4 changed files with 238 additions and 93 deletions

View File

@@ -38,11 +38,11 @@ export class TransitionsModel extends Observable {
finishThreshold: 0.5,
},
},
toPageStart: {
pageStart: {
y: 200,
// duration: 400,
},
fromPageEnd: {
pageReturn: {
y: 100,
// duration: 500,
},

View File

@@ -3,8 +3,9 @@ import { Screen } from '../../platform';
import { iOSNativeHelper } from '../../utils/native-helper';
import { isNumber } from '../../utils/types';
import { Transition } from '.';
import { SharedTransition, SharedTransitionAnimationType, DEFAULT_DURATION, DEFAULT_SPRING } from './shared-transition';
import { SharedTransition, SharedTransitionAnimationType, DEFAULT_DURATION, DEFAULT_SPRING, getRectFromProps } from './shared-transition';
import { PanGestureEventData, GestureStateTypes } from '../gestures';
type SharedElementSettings = { view: View; startFrame: CGRect; endFrame?: CGRect; startOpacity?: number; endOpacity?: number; scale?: { x?: number; y?: number }; startTransform?: CGAffineTransform; snapshot?: UIImageView };
export class ModalTransition extends Transition {
transitionController: ModalTransitionController;
@@ -13,8 +14,10 @@ export class ModalTransition extends Transition {
presented: UIViewController;
presenting: UIViewController;
sharedElements: {
presented?: Array<{ view: View; startFrame?: CGRect; endFrame?: CGRect; snapshot?: UIImageView; startOpacity?: number; endOpacity?: number }>;
presenting?: Array<{ view: View; startFrame: CGRect; endFrame?: CGRect; snapshot?: UIImageView; startOpacity?: number; endOpacity?: number }>;
presented?: Array<SharedElementSettings>;
presenting?: Array<SharedElementSettings>;
// independent sharedTransitionTags which are part of the shared transition but only on one page
independent?: Array<SharedElementSettings & { isPresented?: boolean }>;
};
private _interactiveStartCallback: () => void;
private _interactiveDismissGesture: (args: any /*PanGestureEventData*/) => void;
@@ -132,7 +135,7 @@ class PercentInteractiveController extends UIPercentDrivenInteractiveTransition
this.transitionContext.containerView.addSubview(p.snapshot);
}
const state = SharedTransition.getState(owner.id);
const toProps = state.toPageStart;
const toProps = state.pageStart;
const startY = isNumber(toProps?.y) ? toProps?.y : Screen.mainScreen.heightDIPs;
this.backgroundAnimation = UIViewPropertyAnimator.alloc().initWithDurationDampingRatioAnimations(1, 1, () => {
for (const p of owner.sharedElements.presenting) {
@@ -160,7 +163,7 @@ class PercentInteractiveController extends UIPercentDrivenInteractiveTransition
}
if (this.backgroundAnimation) {
this.backgroundAnimation.reversed = true;
const duration = isNumber(state.toPageStart?.duration) ? state.toPageStart?.duration / 1000 : DEFAULT_DURATION;
const duration = isNumber(state.pageStart?.duration) ? state.pageStart?.duration / 1000 : DEFAULT_DURATION;
this.backgroundAnimation.continueAnimationWithTimingParametersDurationFactor(null, duration);
setTimeout(() => {
for (const p of owner.sharedElements.presented) {
@@ -192,7 +195,7 @@ class PercentInteractiveController extends UIPercentDrivenInteractiveTransition
return;
}
const duration = isNumber(state.fromPageEnd?.duration) ? state.fromPageEnd?.duration / 1000 : DEFAULT_DURATION;
const duration = isNumber(state.pageReturn?.duration) ? state.pageReturn?.duration / 1000 : DEFAULT_DURATION;
this.backgroundAnimation.continueAnimationWithTimingParametersDurationFactor(null, duration);
setTimeout(() => {
for (const presenting of owner.sharedElements.presenting) {
@@ -239,7 +242,7 @@ class ModalTransitionController extends NSObject implements UIViewControllerAnim
transitionContext.containerView.addSubview(owner.presented.view);
owner.presented.view.layoutIfNeeded();
const { sharedElements, presented } = SharedTransition.getSharedElements(state.page, state.toPage);
const { sharedElements, presented, presenting } = SharedTransition.getSharedElements(state.page, state.toPage);
if (SharedTransition.DEBUG) {
console.log(' ModalTransition: Present');
@@ -251,11 +254,26 @@ class ModalTransitionController extends NSObject implements UIViewControllerAnim
console.log(`2. Take snapshots of shared elements and position them based on presenting view:`);
}
const pageStart = state.pageStart;
owner.presented.view.alpha = isNumber(pageStart?.opacity) ? pageStart?.opacity : 0;
const startFrame = getRectFromProps(pageStart, {
x: 0,
y: Screen.mainScreen.heightDIPs,
width: Screen.mainScreen.widthDIPs,
height: Screen.mainScreen.heightDIPs,
});
const pageEnd = state.pageEnd;
const pageEndIndependentTags = Object.keys(pageEnd?.sharedTransitionTags || {});
console.log('pageEndIndependentTags:', pageEndIndependentTags);
for (const presentingView of sharedElements) {
if (!owner.sharedElements) {
owner.sharedElements = {
presented: [],
presenting: [],
independent: [],
};
}
const presentingSharedElement = presentingView.ios;
@@ -268,37 +286,37 @@ class ModalTransitionController extends NSObject implements UIViewControllerAnim
const presentedView = presented.find((v) => v.sharedTransitionTag === presentingView.sharedTransitionTag);
const presentedSharedElement = presentedView.ios;
const sharedElementSnapshot = UIImageView.alloc().init(); // WithImage(snapshotView(presentedSharedElement));
const snapshot = UIImageView.alloc().init();
// treat images differently...
if (presentedSharedElement instanceof UIImageView) {
// in case the image is loaded async, we need to update the snapshot when it changes
// todo: remove listener on transition end
presentedView.on('imageSourceChange', () => {
sharedElementSnapshot.image = iOSNativeHelper.snapshotView(presentedSharedElement, Screen.mainScreen.scale);
sharedElementSnapshot.tintColor = presentedSharedElement.tintColor;
snapshot.image = iOSNativeHelper.snapshotView(presentedSharedElement, Screen.mainScreen.scale);
snapshot.tintColor = presentedSharedElement.tintColor;
});
sharedElementSnapshot.tintColor = presentedSharedElement.tintColor;
sharedElementSnapshot.contentMode = presentedSharedElement.contentMode;
snapshot.tintColor = presentedSharedElement.tintColor;
snapshot.contentMode = presentedSharedElement.contentMode;
}
iOSNativeHelper.copyLayerProperties(sharedElementSnapshot, presentingSharedElement);
sharedElementSnapshot.clipsToBounds = true;
// console.log('---> snapshot: ', sharedElementSnapshot);
iOSNativeHelper.copyLayerProperties(snapshot, presentingSharedElement);
snapshot.clipsToBounds = true;
// console.log('---> snapshot: ', snapshot);
const startFrame = presentingSharedElement.convertRectToView(presentingSharedElement.bounds, transitionContext.containerView);
const endFrame = presentedSharedElement.convertRectToView(presentedSharedElement.bounds, transitionContext.containerView);
sharedElementSnapshot.frame = startFrame;
snapshot.frame = startFrame;
if (SharedTransition.DEBUG) {
console.log('---> ', presentingView.sharedTransitionTag, ' frame:', iOSNativeHelper.printCGRect(sharedElementSnapshot.frame));
console.log('---> ', presentingView.sharedTransitionTag, ' frame:', iOSNativeHelper.printCGRect(snapshot.frame));
}
owner.sharedElements.presenting.push({
view: presentingView,
startFrame,
endFrame,
snapshot: sharedElementSnapshot,
snapshot,
startOpacity: presentingView.opacity,
endOpacity: presentedView.opacity,
});
@@ -311,14 +329,90 @@ class ModalTransitionController extends NSObject implements UIViewControllerAnim
});
// set initial opacity to match the source view opacity
sharedElementSnapshot.alpha = presentingView.opacity;
snapshot.alpha = presentingView.opacity;
// hide both while animating within the transition context
presentingView.opacity = 0;
presentedView.opacity = 0;
// add snapshot to animate
transitionContext.containerView.addSubview(sharedElementSnapshot);
transitionContext.containerView.addSubview(snapshot);
}
for (const tag of pageEndIndependentTags) {
// only consider start when there's a matching end
const pageStartIndependentProps = pageStart?.sharedTransitionTags[tag];
console.log('start:', tag, pageStartIndependentProps);
const pageEndIndependentProps = pageEnd?.sharedTransitionTags[tag];
let independentView = presenting.find((v) => v.sharedTransitionTag === tag);
let isPresented = false;
if (!independentView) {
independentView = presented.find((v) => v.sharedTransitionTag === tag);
if (!independentView) {
break;
}
isPresented = true;
}
const independentSharedElement: UIView = independentView.ios;
let snapshot: UIImageView;
if (isPresented) {
snapshot = UIImageView.alloc().init();
} else {
snapshot = UIImageView.alloc().initWithImage(iOSNativeHelper.snapshotView(independentSharedElement, Screen.mainScreen.scale));
}
if (independentSharedElement instanceof UIImageView) {
// in case the image is loaded async, we need to update the snapshot when it changes
// todo: remove listener on transition end
if (isPresented) {
independentView.on('imageSourceChange', () => {
snapshot.image = iOSNativeHelper.snapshotView(independentSharedElement, Screen.mainScreen.scale);
snapshot.tintColor = independentSharedElement.tintColor;
});
}
snapshot.tintColor = independentSharedElement.tintColor;
snapshot.contentMode = independentSharedElement.contentMode;
}
snapshot.clipsToBounds = true;
const startFrame = independentSharedElement.convertRectToView(independentSharedElement.bounds, transitionContext.containerView);
const startFrameRect = getRectFromProps(pageStartIndependentProps);
// adjust for any specified start positions
const startFrameAdjusted = CGRectMake(startFrame.origin.x + startFrameRect.x, startFrame.origin.y + startFrameRect.y, startFrame.size.width, startFrame.size.height);
console.log('startFrameAdjusted:', tag, iOSNativeHelper.printCGRect(startFrameAdjusted));
// if (pageStartIndependentProps?.scale) {
// snapshot.transform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(startFrameAdjusted.origin.x, startFrameAdjusted.origin.y), CGAffineTransformMakeScale(pageStartIndependentProps.scale.x, pageStartIndependentProps.scale.y))
// } else {
snapshot.frame = startFrameAdjusted;
// }
if (SharedTransition.DEBUG) {
console.log('---> ', independentView.sharedTransitionTag, ' frame:', iOSNativeHelper.printCGRect(snapshot.frame));
}
const endFrameRect = getRectFromProps(pageEndIndependentProps);
const endFrame = CGRectMake(startFrame.origin.x + endFrameRect.x, startFrame.origin.y + endFrameRect.y, startFrame.size.width, startFrame.size.height);
console.log('endFrame:', tag, iOSNativeHelper.printCGRect(endFrame));
owner.sharedElements.independent.push({
view: independentView,
isPresented,
startFrame,
snapshot,
endFrame,
startTransform: independentSharedElement.transform,
scale: pageEndIndependentProps.scale,
startOpacity: independentView.opacity,
endOpacity: isNumber(pageEndIndependentProps.opacity) ? pageEndIndependentProps.opacity : 0,
});
independentView.opacity = 0;
// add snapshot to animate
transitionContext.containerView.addSubview(snapshot);
}
// Important: always set after above shared element positions have had their start positions set
owner.presented.view.frame = CGRectMake(startFrame.x, startFrame.y, startFrame.width, startFrame.height);
const cleanupPresent = () => {
for (const presented of owner.sharedElements.presented) {
presented.view.opacity = presented.startOpacity;
@@ -326,21 +420,35 @@ class ModalTransitionController extends NSObject implements UIViewControllerAnim
for (const presenting of owner.sharedElements.presenting) {
presenting.snapshot.removeFromSuperview();
}
for (const independent of owner.sharedElements.independent) {
independent.snapshot.removeFromSuperview();
if (independent.isPresented) {
independent.view.opacity = independent.startOpacity;
}
}
SharedTransition.updateState(owner.id, {
activeType: SharedTransitionAnimationType.dismiss,
});
transitionContext.completeTransition(true);
};
const updateFramePresent = () => {
const animateProperties = () => {
if (SharedTransition.DEBUG) {
console.log('3. Animating shared elements:');
}
owner.presented.view.alpha = isNumber(pageEnd?.opacity) ? pageEnd?.opacity : 1;
const endFrame = getRectFromProps(pageEnd);
owner.presented.view.frame = CGRectMake(endFrame.x, endFrame.y, endFrame.width, endFrame.height);
// animate page properties to the following:
// https://stackoverflow.com/a/27997678/1418981
// In order to have proper layout. Seems mostly needed when presenting.
// For instance during presentation, destination view doesn't account navigation bar height.
// Not sure if best to leave all the time?
// owner.presented.view.setNeedsLayout();
// owner.presented.view.layoutIfNeeded();
if (SharedTransition.DEBUG) {
console.log('3. Animating shared elements:');
}
for (const presented of owner.sharedElements.presented) {
const presentingMatch = owner.sharedElements.presenting.find((v) => v.view.sharedTransitionTag === presented.view.sharedTransitionTag);
// Workaround wrong origin due ongoing layout process.
@@ -359,51 +467,46 @@ class ModalTransitionController extends NSObject implements UIViewControllerAnim
console.log(`---> ${presentingMatch.view.sharedTransitionTag} animate to: `, iOSNativeHelper.printCGRect(correctedEndFrame));
}
}
for (const independent of owner.sharedElements.independent) {
let endFrame: CGRect = independent.endFrame;
// if (independent.isPresented) {
// const updatedEndFrame = independent.view.ios.convertRectToView(independent.view.ios.bounds, transitionContext.containerView);
// endFrame = CGRectMake(updatedEndFrame.origin.x, updatedEndFrame.origin.y, independent.endFrame.size.width, independent.endFrame.size.height);
// }
if (independent.scale) {
independent.snapshot.transform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(endFrame.origin.x, endFrame.origin.y), CGAffineTransformMakeScale(independent.scale.x, independent.scale.y));
} else {
independent.snapshot.frame = endFrame;
}
independent.snapshot.alpha = independent.endOpacity;
if (SharedTransition.DEBUG) {
console.log(`---> ${independent.view.sharedTransitionTag} animate to: `, iOSNativeHelper.printCGRect(independent.endFrame));
}
}
};
// starting page properties
const toProps = state.toPageStart;
owner.presented.view.alpha = isNumber(toProps?.opacity) ? toProps?.opacity : 0;
const startX = isNumber(toProps?.x) ? toProps?.x : 0;
const startY = isNumber(toProps?.y) ? toProps?.y : Screen.mainScreen.heightDIPs;
const startWidth = isNumber(toProps?.width) ? toProps?.width : Screen.mainScreen.widthDIPs;
const startHeight = isNumber(toProps?.height) ? toProps?.height : Screen.mainScreen.heightDIPs;
owner.presented.view.frame = CGRectMake(startX, startY, startWidth, startHeight);
const animateProperties = () => {
const props = state.toPageEnd;
// animate page properties to the following:
owner.presented.view.alpha = isNumber(props?.opacity) ? props?.opacity : 1;
const endX = isNumber(props?.x) ? props?.x : 0;
const endY = isNumber(props?.y) ? props?.y : 0;
const endWidth = isNumber(props?.width) ? props?.width : Screen.mainScreen.widthDIPs;
const endHeight = isNumber(props?.height) ? props?.height : Screen.mainScreen.heightDIPs;
owner.presented.view.frame = CGRectMake(endX, endY, endWidth, endHeight);
};
if (isNumber(toProps?.duration)) {
if (isNumber(pageEnd?.duration)) {
// override spring and use only linear animation
UIView.animateWithDurationAnimationsCompletion(
toProps?.duration / 1000,
pageEnd?.duration / 1000,
() => {
animateProperties();
updateFramePresent();
},
() => {
cleanupPresent();
}
);
} else {
const spring = toProps?.spring;
const spring = pageEnd?.spring;
iOSNativeHelper.animateWithSpring({
tension: isNumber(spring?.tension) ? spring?.tension : DEFAULT_SPRING.tension,
friction: isNumber(spring?.friction) ? spring?.tension : DEFAULT_SPRING.friction,
friction: isNumber(spring?.friction) ? spring?.friction : DEFAULT_SPRING.friction,
mass: isNumber(spring?.mass) ? spring?.mass : DEFAULT_SPRING.mass,
velocity: isNumber(spring?.velocity) ? spring?.velocity : DEFAULT_SPRING.velocity,
delay: isNumber(spring?.delay) ? spring?.delay : DEFAULT_SPRING.delay,
animations: () => {
animateProperties();
updateFramePresent();
},
completion: () => {
cleanupPresent();
@@ -435,18 +538,40 @@ class ModalTransitionController extends NSObject implements UIViewControllerAnim
p.snapshot.alpha = p.endOpacity;
transitionContext.containerView.addSubview(p.snapshot);
}
for (const independent of owner.sharedElements.independent) {
independent.snapshot.alpha = independent.endOpacity;
transitionContext.containerView.addSubview(independent.snapshot);
}
const pageReturn = state.pageReturn;
const cleanupDismiss = () => {
for (const presenting of owner.sharedElements.presenting) {
presenting.view.opacity = presenting.startOpacity;
}
for (const independent of owner.sharedElements.independent) {
independent.view.opacity = independent.startOpacity;
}
SharedTransition.finishState(owner.id);
owner.sharedElements = null;
transitionContext.completeTransition(true);
};
const updateFrameDismiss = () => {
const animateProperties = () => {
if (SharedTransition.DEBUG) {
console.log('3. Dismissing shared elements:');
}
owner.presented.view.alpha = isNumber(pageReturn?.opacity) ? pageReturn?.opacity : 0;
const endFrame = getRectFromProps(pageReturn, {
x: 0,
y: Screen.mainScreen.heightDIPs,
width: Screen.mainScreen.widthDIPs,
height: Screen.mainScreen.heightDIPs,
});
owner.presented.view.frame = CGRectMake(endFrame.x, endFrame.y, endFrame.width, endFrame.height);
for (const presenting of owner.sharedElements.presenting) {
iOSNativeHelper.copyLayerProperties(presenting.snapshot, presenting.view.ios);
presenting.snapshot.frame = presenting.startFrame;
@@ -456,41 +581,39 @@ class ModalTransitionController extends NSObject implements UIViewControllerAnim
console.log(`---> ${presenting.view.sharedTransitionTag} animate to: `, iOSNativeHelper.printCGRect(presenting.startFrame));
}
}
for (const independent of owner.sharedElements.independent) {
independent.snapshot.alpha = independent.startOpacity;
if (independent.scale) {
independent.snapshot.transform = independent.startTransform;
} else {
independent.snapshot.frame = independent.startFrame;
}
if (SharedTransition.DEBUG) {
console.log(`---> ${independent.view.sharedTransitionTag} animate to: `, iOSNativeHelper.printCGRect(independent.startFrame));
}
}
};
const props = state.fromPageEnd;
const animateProperties = () => {
owner.presented.view.alpha = isNumber(props?.opacity) ? props?.opacity : 0;
const endX = isNumber(props?.x) ? props?.x : 0;
const endY = isNumber(props?.y) ? props?.y : Screen.mainScreen.heightDIPs;
const endWidth = isNumber(props?.width) ? props?.width : Screen.mainScreen.widthDIPs;
const endHeight = isNumber(props?.height) ? props?.height : Screen.mainScreen.heightDIPs;
owner.presented.view.frame = CGRectMake(endX, endY, endWidth, endHeight);
};
if (isNumber(props?.duration)) {
if (isNumber(pageReturn?.duration)) {
// override spring and use only linear animation
UIView.animateWithDurationAnimationsCompletion(
props?.duration / 1000,
pageReturn?.duration / 1000,
() => {
animateProperties();
updateFrameDismiss();
},
() => {
cleanupDismiss();
}
);
} else {
const spring = props?.spring;
const spring = pageReturn?.spring;
iOSNativeHelper.animateWithSpring({
tension: isNumber(spring?.tension) ? spring?.tension : DEFAULT_SPRING.tension,
friction: isNumber(spring?.friction) ? spring?.tension : DEFAULT_SPRING.friction,
friction: isNumber(spring?.friction) ? spring?.friction : DEFAULT_SPRING.friction,
animations: () => {
animateProperties();
updateFrameDismiss();
},
completion: () => {
cleanupDismiss();

View File

@@ -148,7 +148,7 @@ class PercentInteractiveController extends UIPercentDrivenInteractiveTransition
this.transitionContext.containerView.addSubview(p.snapshot);
}
const state = SharedTransition.getState(owner.id);
const props = state.fromPageEnd;
const props = state.pageReturn;
this.backgroundAnimation = UIViewPropertyAnimator.alloc().initWithDurationDampingRatioAnimations(1, 1, () => {
for (const p of owner.sharedElements.presenting) {
p.snapshot.frame = p.startFrame;
@@ -179,7 +179,7 @@ class PercentInteractiveController extends UIPercentDrivenInteractiveTransition
}
if (this.backgroundAnimation) {
this.backgroundAnimation.reversed = true;
const duration = isNumber(state.toPageStart?.duration) ? state.toPageStart?.duration / 1000 : 0.35;
const duration = isNumber(state.pageStart?.duration) ? state.pageStart?.duration / 1000 : 0.35;
this.backgroundAnimation.continueAnimationWithTimingParametersDurationFactor(null, duration);
setTimeout(() => {
for (const p of owner.sharedElements.presented) {
@@ -211,7 +211,7 @@ class PercentInteractiveController extends UIPercentDrivenInteractiveTransition
return;
}
const duration = isNumber(state.fromPageEnd?.duration) ? state.fromPageEnd?.duration / 1000 : 0.35;
const duration = isNumber(state.pageReturn?.duration) ? state.pageReturn?.duration / 1000 : 0.35;
this.backgroundAnimation.continueAnimationWithTimingParametersDurationFactor(null, duration);
setTimeout(() => {
for (const presenting of owner.sharedElements.presenting) {
@@ -396,7 +396,7 @@ class PageTransitionController extends NSObject implements UIViewControllerAnima
};
// starting page properties
const toProps = state.toPageStart;
const toProps = state.pageStart;
owner.presented.view.alpha = isNumber(toProps?.opacity) ? toProps?.opacity : 0;
const startX = isNumber(toProps?.x) ? toProps?.x : Screen.mainScreen.widthDIPs;
@@ -406,7 +406,7 @@ class PageTransitionController extends NSObject implements UIViewControllerAnima
owner.presented.view.frame = CGRectMake(startX, startY, startWidth, startHeight);
const animateProperties = () => {
const props = state.toPageEnd;
const props = state.pageEnd;
// animate page properties to the following:
owner.presented.view.alpha = isNumber(props?.opacity) ? props?.opacity : 1;
@@ -432,7 +432,7 @@ class PageTransitionController extends NSObject implements UIViewControllerAnima
const spring = toProps?.spring;
iOSNativeHelper.animateWithSpring({
tension: isNumber(spring?.tension) ? spring?.tension : DEFAULT_SPRING.tension,
friction: isNumber(spring?.friction) ? spring?.tension : DEFAULT_SPRING.friction,
friction: isNumber(spring?.friction) ? spring?.friction : DEFAULT_SPRING.friction,
animations: () => {
animateProperties();
@@ -492,7 +492,7 @@ class PageTransitionController extends NSObject implements UIViewControllerAnima
}
};
const props = state.fromPageEnd;
const props = state.pageReturn;
const animateProperties = () => {
owner.presented.view.alpha = isNumber(props?.opacity) ? props?.opacity : 0;
@@ -520,7 +520,7 @@ class PageTransitionController extends NSObject implements UIViewControllerAnima
const spring = props?.spring;
iOSNativeHelper.animateWithSpring({
tension: isNumber(spring?.tension) ? spring?.tension : DEFAULT_SPRING.tension,
friction: isNumber(spring?.friction) ? spring?.tension : DEFAULT_SPRING.friction,
friction: isNumber(spring?.friction) ? spring?.friction : DEFAULT_SPRING.friction,
animations: () => {
animateProperties();

View File

@@ -1,4 +1,6 @@
import type { Transition } from '.';
import { Screen } from '../../platform';
import { isNumber } from '../../utils/types';
import { querySelectorAll, ViewBase } from '../core/view-base';
import type { View } from '../core/view';
import type { PanGestureEventData } from '../gestures';
@@ -7,6 +9,9 @@ export const DEFAULT_DURATION = 0.35;
export const DEFAULT_SPRING = {
tension: 140,
friction: 10,
mass: 1,
velocity: 0,
delay: 0,
};
// always increment when adding new transitions to be able to track their state
export enum SharedTransitionAnimationType {
@@ -50,15 +55,15 @@ export interface SharedTransitionConfig {
/**
* View settings to start your transition.
*/
toPageStart?: SharedTransitionPageProperties;
pageStart?: SharedTransitionPageProperties;
/**
* View settings to end your transition.
*/
toPageEnd?: SharedTransitionPageProperties;
pageEnd?: SharedTransitionPageProperties;
/**
* View settings to end your transition for the 'from' (aka outgoing or dismissed) page.
*/
fromPageEnd?: SharedTransitionPageProperties;
pageReturn?: SharedTransitionPageProperties;
}
export interface SharedTransitionState extends SharedTransitionConfig {
activeType?: SharedTransitionAnimationType;
@@ -67,17 +72,15 @@ export interface SharedTransitionState extends SharedTransitionConfig {
interactiveBegan?: boolean;
interactiveCancelled?: boolean;
}
type SharedTransitionPageProperties = {
x?: number;
y?: number;
width?: number;
height?: number;
opacity?: number;
export type SharedRect = { x?: number; y?: number; width?: number; height?: number };
export type SharedProperties = SharedRect & { opacity?: number; scale?: { x?: number; y?: number } };
type SharedTransitionPageProperties = SharedProperties & {
/**
* Linear duration in milliseconds
* Note: When this is defined, it will override spring options and use only linear animation.
*/
duration?: number;
sharedTransitionTags?: { [key: string]: SharedProperties };
/**
* Spring animation settings.
* Defaults to 140 tension with 10 friction.
@@ -173,7 +176,10 @@ export class SharedTransition {
// 2. Presenting view: gather all sharedTransitionTag views
const presentingSharedElements = <Array<View>>querySelectorAll(fromPage, 'sharedTransitionTag');
// console.log('presenting sharedTransitionTag total:', presentingSharedElements.length);
console.log(
'presenting sharedTransitionTags:',
presentingSharedElements.map((v) => v.sharedTransitionTag)
);
// 3. only handle sharedTransitionTag on presenting which match presented
const presentedTags = presentedSharedElements.map((v) => v.sharedTransitionTag);
@@ -184,3 +190,19 @@ export class SharedTransition {
};
}
}
export function getRectFromProps(props: SharedTransitionPageProperties, defaults?: SharedRect): SharedRect {
defaults = {
x: 0,
y: 0,
width: Screen.mainScreen.widthDIPs,
height: Screen.mainScreen.heightDIPs,
...(defaults || {}),
};
return {
x: isNumber(props?.x) ? props?.x : defaults.x,
y: isNumber(props?.y) ? props?.y : defaults.y,
width: isNumber(props?.width) ? props?.width : defaults.width,
height: isNumber(props?.height) ? props?.height : defaults.height,
};
}