mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 10:01:08 +08:00
201 lines
7.5 KiB
TypeScript
201 lines
7.5 KiB
TypeScript
import type { View } from '../core/view';
|
|
import { CORE_ANIMATION_DEFAULTS, getDurationWithDampingFromSpring } from '../../utils/common';
|
|
import { isNumber } from '../../utils/types';
|
|
import { Transition, SharedElementSettings, TransitionInteractiveState } from '.';
|
|
import { SharedTransition, SharedTransitionAnimationType } from './shared-transition';
|
|
import { SharedTransitionHelper } from './shared-transition-helper';
|
|
import { PanGestureEventData, GestureStateTypes } from '../gestures';
|
|
|
|
export class ModalTransition extends Transition {
|
|
transitionController: ModalTransitionController;
|
|
interactiveController: UIPercentDrivenInteractiveTransition;
|
|
interactiveGestureRecognizer: UIScreenEdgePanGestureRecognizer;
|
|
presented: UIViewController;
|
|
presenting: UIViewController;
|
|
sharedElements: {
|
|
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;
|
|
|
|
iosPresentedController(presented: UIViewController, presenting: UIViewController, source: UIViewController): UIViewControllerAnimatedTransitioning {
|
|
this.transitionController = ModalTransitionController.initWithOwner(new WeakRef(this));
|
|
this.presented = presented;
|
|
// console.log('presenting:', presenting)
|
|
return this.transitionController;
|
|
}
|
|
|
|
iosDismissedController(dismissed: UIViewController): UIViewControllerAnimatedTransitioning {
|
|
this.transitionController = ModalTransitionController.initWithOwner(new WeakRef(this));
|
|
this.presented = dismissed;
|
|
return this.transitionController;
|
|
}
|
|
|
|
iosInteractionDismiss(animator: UIViewControllerAnimatedTransitioning): UIViewControllerInteractiveTransitioning {
|
|
// console.log('-- iosInteractionDismiss --');
|
|
this.interactiveController = PercentInteractiveController.initWithOwner(new WeakRef(this));
|
|
return this.interactiveController;
|
|
}
|
|
|
|
iosInteractionPresented(animator: UIViewControllerAnimatedTransitioning): UIViewControllerInteractiveTransitioning {
|
|
// console.log('-- iosInteractionPresented --');
|
|
return null;
|
|
}
|
|
|
|
setupInteractiveGesture(startCallback: () => void, view: View) {
|
|
this._interactiveStartCallback = startCallback;
|
|
this._interactiveDismissGesture = this._interactiveDismissGestureHandler.bind(this);
|
|
view.on('pan', this._interactiveDismissGesture);
|
|
// this.interactiveGestureRecognizer = UIScreenEdgePanGestureRecognizer.alloc().initWithTargetAction()
|
|
// let edgeSwipeGestureRecognizer = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
|
|
// edgeSwipeGestureRecognizer.edges = .left
|
|
// view.addGestureRecognizer(edgeSwipeGestureRecognizer)
|
|
}
|
|
|
|
private _interactiveDismissGestureHandler(args: PanGestureEventData) {
|
|
if (args?.ios?.view) {
|
|
const state = SharedTransition.getState(this.id);
|
|
const percent = state.interactive?.dismiss?.percentFormula ? state.interactive.dismiss.percentFormula(args) : args.deltaY / (args.ios.view.bounds.size.height / 2);
|
|
if (SharedTransition.DEBUG) {
|
|
console.log('Interactive dismissal percentage:', percent);
|
|
}
|
|
switch (args.state) {
|
|
case GestureStateTypes.began:
|
|
SharedTransition.updateState(this.id, {
|
|
interactiveBegan: true,
|
|
interactiveCancelled: false,
|
|
});
|
|
if (this._interactiveStartCallback) {
|
|
this._interactiveStartCallback();
|
|
}
|
|
break;
|
|
case GestureStateTypes.changed:
|
|
if (percent < 1) {
|
|
if (this.interactiveController) {
|
|
this.interactiveController.updateInteractiveTransition(percent);
|
|
}
|
|
}
|
|
break;
|
|
case GestureStateTypes.cancelled:
|
|
case GestureStateTypes.ended:
|
|
if (this.interactiveController) {
|
|
const finishThreshold = isNumber(state.interactive?.dismiss?.finishThreshold) ? state.interactive.dismiss.finishThreshold : 0.5;
|
|
if (percent > finishThreshold) {
|
|
this.interactiveController.finishInteractiveTransition();
|
|
} else {
|
|
SharedTransition.updateState(this.id, {
|
|
interactiveCancelled: true,
|
|
});
|
|
this.interactiveController.cancelInteractiveTransition();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@NativeClass()
|
|
class PercentInteractiveController extends UIPercentDrivenInteractiveTransition implements UIViewControllerInteractiveTransitioning {
|
|
static ObjCProtocols = [UIViewControllerInteractiveTransitioning];
|
|
owner: WeakRef<ModalTransition>;
|
|
interactiveState: TransitionInteractiveState;
|
|
|
|
static initWithOwner(owner: WeakRef<ModalTransition>) {
|
|
const ctrl = <PercentInteractiveController>PercentInteractiveController.new();
|
|
ctrl.owner = owner;
|
|
return ctrl;
|
|
}
|
|
|
|
startInteractiveTransition(transitionContext: UIViewControllerContextTransitioning) {
|
|
// console.log('startInteractiveTransition');
|
|
if (!this.interactiveState) {
|
|
this.interactiveState = {
|
|
transitionContext,
|
|
};
|
|
const owner = this.owner?.deref();
|
|
if (owner) {
|
|
const state = SharedTransition.getState(owner.id);
|
|
SharedTransitionHelper.interactiveStart(state, this.interactiveState, 'modal');
|
|
}
|
|
}
|
|
}
|
|
|
|
updateInteractiveTransition(percentComplete: number) {
|
|
const owner = this.owner?.deref();
|
|
if (owner) {
|
|
const state = SharedTransition.getState(owner.id);
|
|
SharedTransitionHelper.interactiveUpdate(state, this.interactiveState, 'modal', percentComplete);
|
|
}
|
|
}
|
|
|
|
cancelInteractiveTransition() {
|
|
// console.log('cancelInteractiveTransition');
|
|
const owner = this.owner?.deref();
|
|
if (owner) {
|
|
const state = SharedTransition.getState(owner.id);
|
|
SharedTransitionHelper.interactiveCancel(state, this.interactiveState, 'modal');
|
|
}
|
|
}
|
|
|
|
finishInteractiveTransition() {
|
|
// console.log('finishInteractiveTransition');
|
|
const owner = this.owner?.deref();
|
|
if (owner) {
|
|
const state = SharedTransition.getState(owner.id);
|
|
SharedTransitionHelper.interactiveFinish(state, this.interactiveState, 'modal');
|
|
}
|
|
}
|
|
}
|
|
|
|
@NativeClass()
|
|
class ModalTransitionController extends NSObject implements UIViewControllerAnimatedTransitioning {
|
|
static ObjCProtocols = [UIViewControllerAnimatedTransitioning];
|
|
owner: WeakRef<ModalTransition>;
|
|
|
|
static initWithOwner(owner: WeakRef<ModalTransition>) {
|
|
const ctrl = <ModalTransitionController>ModalTransitionController.new();
|
|
ctrl.owner = owner;
|
|
return ctrl;
|
|
}
|
|
|
|
transitionDuration(transitionContext: UIViewControllerContextTransitioning): number {
|
|
const owner = this.owner.deref();
|
|
if (owner) {
|
|
const state = SharedTransition.getState(owner.id);
|
|
switch (state?.activeType) {
|
|
case SharedTransitionAnimationType.present:
|
|
if (isNumber(state?.pageEnd?.duration)) {
|
|
return state.pageEnd?.duration / 1000;
|
|
} else {
|
|
return getDurationWithDampingFromSpring(state.pageEnd?.spring).duration;
|
|
}
|
|
|
|
case SharedTransitionAnimationType.dismiss:
|
|
if (isNumber(state?.pageReturn?.duration)) {
|
|
return state.pageReturn?.duration / 1000;
|
|
} else {
|
|
return getDurationWithDampingFromSpring(state.pageReturn?.spring).duration;
|
|
}
|
|
}
|
|
}
|
|
return CORE_ANIMATION_DEFAULTS.duration;
|
|
}
|
|
|
|
animateTransition(transitionContext: UIViewControllerContextTransitioning): void {
|
|
// console.log('ModalTransitionController animateTransition');
|
|
const owner = this.owner.deref();
|
|
if (owner) {
|
|
// console.log('owner.id:', owner.id);
|
|
const state = SharedTransition.getState(owner.id);
|
|
if (!state) {
|
|
return;
|
|
}
|
|
SharedTransitionHelper.animate(state, transitionContext, 'modal');
|
|
}
|
|
}
|
|
}
|