feat(core): Shared Element Transitions (#10022)

This commit is contained in:
Nathan Walker
2023-03-28 11:04:29 -07:00
committed by GitHub
parent a11d577a14
commit 59369fbc19
59 changed files with 2989 additions and 927 deletions

View File

@@ -1,4 +1,4 @@
import { Transition } from '@nativescript/core';
import { PageTransition, Transition } from '@nativescript/core';
export class CustomTransition extends Transition {
constructor(duration: number, curve: any) {
@@ -34,3 +34,5 @@ export class CustomTransition extends Transition {
return animatorSet;
}
}
export class CustomSharedElementPageTransition extends PageTransition {}

View File

@@ -1,6 +1,8 @@
import { Transition } from '@nativescript/core/ui/transition';
import { Transition, PageTransition } from '@nativescript/core';
export class CustomTransition extends Transition {
constructor();
constructor(duration: number, curve: any);
}
export class CustomSharedElementPageTransition extends PageTransition {}

View File

@@ -1,20 +1,22 @@
import * as transition from '@nativescript/core/ui/transition';
import { PageTransition, SharedTransition, SharedTransitionHelper, Transition } from '@nativescript/core';
export class CustomTransition extends transition.Transition {
export class CustomTransition extends Transition {
constructor(duration: number, curve: any) {
super(duration, curve);
}
public animateIOSTransition(containerView: UIView, fromView: UIView, toView: UIView, operation: UINavigationControllerOperation, completion: (finished: boolean) => void): void {
animateIOSTransition(transitionContext: UIViewControllerContextTransitioning, fromViewCtrl: UIViewController, toViewCtrl: UIViewController, operation: UINavigationControllerOperation): void {
const toView = toViewCtrl.view;
const fromView = fromViewCtrl.view;
toView.transform = CGAffineTransformMakeScale(0, 0);
fromView.transform = CGAffineTransformIdentity;
switch (operation) {
case UINavigationControllerOperation.Push:
containerView.insertSubviewAboveSubview(toView, fromView);
transitionContext.containerView.insertSubviewAboveSubview(toView, fromView);
break;
case UINavigationControllerOperation.Pop:
containerView.insertSubviewBelowSubview(toView, fromView);
transitionContext.containerView.insertSubviewBelowSubview(toView, fromView);
break;
}
@@ -27,7 +29,63 @@ export class CustomTransition extends transition.Transition {
toView.transform = CGAffineTransformIdentity;
fromView.transform = CGAffineTransformMakeScale(0, 0);
},
completion
(finished) => {
transitionContext.completeTransition(finished);
}
);
}
}
export class CustomSharedElementPageTransition extends PageTransition {
transitionController: PageTransitionController;
interactiveController: UIPercentDrivenInteractiveTransition;
presented: UIViewController;
presenting: UIViewController;
navigationController: UINavigationController;
operation: number;
iosNavigatedController(navigationController: UINavigationController, operation: number, fromVC: UIViewController, toVC: UIViewController): UIViewControllerAnimatedTransitioning {
this.navigationController = navigationController;
if (!this.transitionController) {
this.presented = toVC;
this.presenting = fromVC;
}
this.transitionController = PageTransitionController.initWithOwner(new WeakRef(this));
// console.log('iosNavigatedController presenting:', this.presenting);
this.operation = operation;
return this.transitionController;
}
}
@NativeClass()
class PageTransitionController extends NSObject implements UIViewControllerAnimatedTransitioning {
static ObjCProtocols = [UIViewControllerAnimatedTransitioning];
owner: WeakRef<PageTransition>;
static initWithOwner(owner: WeakRef<PageTransition>) {
const ctrl = <PageTransitionController>PageTransitionController.new();
ctrl.owner = owner;
return ctrl;
}
transitionDuration(transitionContext: UIViewControllerContextTransitioning): number {
const owner = this.owner.deref();
if (owner) {
return owner.getDuration();
}
return 0.35;
}
animateTransition(transitionContext: UIViewControllerContextTransitioning): void {
const owner = this.owner.deref();
if (owner) {
// console.log('--- PageTransitionController animateTransition');
const state = SharedTransition.getState(owner.id);
if (!state) {
return;
}
SharedTransitionHelper.animate(state, transitionContext, 'page');
}
}
}

View File

@@ -1,10 +1,6 @@
import * as helper from '../ui-helper';
import * as platform from '@nativescript/core/platform';
import { Trace, CoreTypes } from '@nativescript/core';
import { Color } from '@nativescript/core/color';
import { NavigationEntry, NavigationTransition } from '@nativescript/core/ui/frame';
import { Page } from '@nativescript/core/ui/page';
import { CustomTransition } from './custom-transition';
import { Color, Device, CoreTypes, Trace, SharedTransition, NavigationEntry, NavigationTransition, Page, platformNames, GridLayout } from '@nativescript/core';
import { CustomTransition, CustomSharedElementPageTransition } from './custom-transition';
function _testTransition(navigationTransition: NavigationTransition) {
var testId = `Transition[${JSON.stringify(navigationTransition)}]`;
@@ -36,10 +32,10 @@ export function test_Transitions() {
});
var transitions;
if (platform.Device.os === platform.platformNames.ios) {
if (Device.os === platformNames.ios) {
transitions = ['curl'];
} else {
const _sdkVersion = parseInt(platform.Device.sdkVersion);
const _sdkVersion = parseInt(Device.sdkVersion);
transitions = _sdkVersion >= 21 ? ['explode'] : [];
}
@@ -55,3 +51,59 @@ export function test_Transitions() {
// helper.navigateWithEntry({ create: mainPageFactory, clearHistory: true, animated: false });
}
export function test_SharedElementTransitions() {
helper.navigate(() => {
const page = new Page();
page.id = 'SharedelementTransitionsTestPage_MAIN';
page.style.backgroundColor = new Color(255, Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255));
const grid = new GridLayout();
const sharedElement = new GridLayout();
sharedElement.width = { unit: 'dip', value: 200 };
sharedElement.height = { unit: 'dip', value: 200 };
sharedElement.marginTop = 20;
sharedElement.borderRadius = 20;
sharedElement.verticalAlignment = 'top';
sharedElement.iosOverflowSafeArea = false;
sharedElement.backgroundColor = new Color('yellow');
sharedElement.sharedTransitionTag = 'testing';
grid.addChild(sharedElement);
page.content = grid;
return page;
});
const navigationTransition = SharedTransition.custom(new CustomSharedElementPageTransition());
var testId = `SharedElementTransition[${JSON.stringify(navigationTransition)}]`;
if (Trace.isEnabled()) {
Trace.write(`Testing ${testId}`, Trace.categories.Test);
}
var navigationEntry: NavigationEntry = {
create: function (): Page {
let page = new Page();
page.id = testId;
page.style.backgroundColor = new Color(255, Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255));
const grid = new GridLayout();
const sharedElement = new GridLayout();
sharedElement.width = { unit: 'dip', value: 60 };
sharedElement.height = { unit: 'dip', value: 60 };
sharedElement.marginTop = 20;
sharedElement.borderRadius = 30;
sharedElement.verticalAlignment = 'top';
sharedElement.iosOverflowSafeArea = false;
sharedElement.backgroundColor = new Color('purple');
sharedElement.sharedTransitionTag = 'testing';
grid.addChild(sharedElement);
page.content = grid;
return page;
},
animated: true,
transition: navigationTransition,
};
helper.navigateWithEntry(navigationEntry);
}