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

@@ -20,6 +20,7 @@
<Button text="scroll-view" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
<Button text="switch" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
<Button text="touch-animations" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
<Button text="transitions" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
<Button text="vector-image" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
<Button text="visibility-vs-hidden" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
<Button text="fs-helper" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />

View File

@@ -0,0 +1,32 @@
import { Observable, EventData, Page, ShowModalOptions, SharedTransition, ModalTransition, PageTransition, FadeTransition, SlideTransition } from '@nativescript/core';
let page: Page;
export function navigatingTo(args: EventData) {
page = <Page>args.object;
page.bindingContext = new TransitionsModel();
}
export class TransitionsModel extends Observable {
open(args: EventData) {
const type = (<any>args.object).type;
let moduleName: string;
switch (type) {
case '1':
moduleName = `pages/transitions/transition-example`;
break;
case '2':
moduleName = `pages/transitions/transition-page-modal-example`;
break;
}
page.frame.navigate({
moduleName,
transition: SharedTransition.custom(new PageTransition(), {
interactive: {
dismiss: {
finishThreshold: 0.5,
},
},
}),
});
}
}

View File

@@ -0,0 +1,13 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
<Page.actionBar>
<ActionBar title="Transitions" icon="" class="action-bar">
</ActionBar>
</Page.actionBar>
<GridLayout rows="*,auto,*" class="p-20">
<StackLayout row="1">
<Button text="Open Testing Examples" class="btn btn-primary btn-view-demo" tap="{{open}}" type="1" />
<Button text="Open Page and Modal Example" class="btn btn-primary btn-view-demo" tap="{{open}}" type="2" />
</StackLayout>
</GridLayout>
</Page>

View File

@@ -0,0 +1,37 @@
import { Observable, EventData, Page, NavigatedData } from '@nativescript/core';
let page: Page;
export function navigatingTo(args: NavigatedData) {
bindPage(args, args.context);
}
let closeCallback;
export function onShownModally(args) {
bindPage(args, args.context);
closeCallback = args.closeCallback;
}
function bindPage(args, context: any) {
page = <Page>args.object;
page.bindingContext = new TransitionsModel(context);
}
export class TransitionsModel extends Observable {
example1 = true;
example2: boolean;
example3: boolean;
dynamicTag: string;
constructor(options: { isModal?: boolean; example2?: boolean; example3?: boolean; dynamicTag?: string }) {
super();
this.example1 = options.example2 || options.example3 ? false : true;
this.example2 = options.example2;
this.example3 = options.example3;
this.dynamicTag = options.dynamicTag;
}
close() {
if (closeCallback) {
closeCallback();
}
}
}

View File

@@ -0,0 +1,22 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" shownModally="onShownModally" class="page">
<Page.actionBar>
<ActionBar title="Transitions Example Detail" icon="" class="action-bar">
</ActionBar>
</Page.actionBar>
<GridLayout>
<Button text="Close" tap="{{close}}" visibility="{{ isModal ? 'visible' : 'collapsed' }}" horizontalAlignment="left" verticalAlignment="top" marginTop="20" marginLeft="20" />
<ContentView width="80%" height="300" borderRadius="20" backgroundColor="green" sharedTransitionTag="fab" horizontalAlignment="center" verticalAlignment="top" marginTop="100" visibility="{{ example1 ? 'visible' : 'collapsed' }}" />
<GridLayout visibility="{{ example2 ? 'visible' : 'collapsed' }}">
<ContentView width="200" height="200" borderRadius="100" backgroundColor="purple" sharedTransitionTag="shape1" verticalAlignment="top" horizontalAlignment="right" marginRight="20" marginTop="20" iosIgnoreSafeArea="true" />
<ContentView width="80" height="80" borderRadius="40" backgroundColor="orange" sharedTransitionTag="shape2" verticalAlignment="top" horizontalAlignment="left" marginLeft="20" marginTop="20" iosIgnoreSafeArea="true" />
<ContentView width="20" height="20" borderRadius="10" backgroundColor="brown" sharedTransitionTag="shape3" verticalAlignment="bottom" horizontalAlignment="right" marginRight="20" iosIgnoreSafeArea="true" />
<ContentView width="150" height="150" borderRadius="75" backgroundColor="yellow" sharedTransitionTag="shape4" verticalAlignment="bottom" horizontalAlignment="left" marginLeft="20" iosIgnoreSafeArea="true" />
</GridLayout>
<GridLayout visibility="{{ example3 ? 'visible' : 'collapsed' }}">
<ContentView width="80%" height="200" borderRadius="20" backgroundColor="purple" sharedTransitionTag="{{dynamicTag}}" verticalAlignment="top" horizontalAlignment="center" marginRight="20" marginTop="20" iosIgnoreSafeArea="true" />
</GridLayout>
</GridLayout>
</Page>

View File

@@ -0,0 +1,111 @@
import { Observable, EventData, Page, ShowModalOptions, SharedTransition, ModalTransition, PageTransition, FadeTransition, SlideTransition, PropertyChangeData } from '@nativescript/core';
let page: Page;
// SharedTransition.DEBUG = true;
export function navigatingTo(args: EventData) {
page = <Page>args.object;
page.bindingContext = new TransitionsModel();
}
let updatedSegmentValue: number;
if (typeof updatedSegmentValue === 'undefined') {
updatedSegmentValue = 0;
}
export class TransitionsModel extends Observable {
segmentSelectedIndex = updatedSegmentValue;
items = [
{
title: 'Homer',
dynamicTag: 'dynamic1',
},
{
title: 'Marge',
dynamicTag: 'dynamic2',
},
{
title: 'Bart',
dynamicTag: 'dynamic3',
},
{
title: 'Lisa',
dynamicTag: 'dynamic4',
},
{
title: 'Maggie',
dynamicTag: 'dynamic5',
},
];
constructor() {
super();
this.on(Observable.propertyChangeEvent, (data: PropertyChangeData) => {
if (data.propertyName === 'segmentSelectedIndex') {
console.log('change segmentSelectedIndex--');
console.log(data.value);
updatedSegmentValue = data.value;
this.segmentSelectedIndex = data.value;
}
});
}
open(args) {
const moduleName = `pages/transitions/transition-example-detail`;
const context: any = {
example2: !!args.object.example2,
example3: !!args.object.example3,
dynamicTag: args.object.dynamicTag,
};
page.frame.navigate({
moduleName,
context,
transition: SharedTransition.custom(new PageTransition(), {
interactive: {
dismiss: {
finishThreshold: 0.5,
},
},
// pageEnd: {
// duration: 3000
// },
// pageReturn: {
// duration: 1000
// }
}),
});
// Try modals as well:
// context.isModal = true;
// page.showModal(moduleName, {
// context,
// transition: SharedTransition.custom(new ModalTransition(), {
// interactive: {
// dismiss: {
// finishThreshold: 0.5,
// },
// },
// pageStart: {
// y: 200,
// // duration: 400,
// },
// pageReturn: {
// y: 100,
// // duration: 500,
// },
// }),
// closeCallback(args) {
// // console.log('close modal callback', args);
// },
// } as ShowModalOptions);
}
onItemTap(args) {
const item = this.items[args.index];
console.log(item);
this.open({
object: {
example3: true,
dynamicTag: item.dynamicTag,
},
});
}
}

View File

@@ -0,0 +1,40 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
<Page.actionBar>
<ActionBar title="Transitions Example 1" icon="" class="action-bar">
</ActionBar>
</Page.actionBar>
<GridLayout rows="auto,*">
<SegmentedBar sharedTransitionTag="segmentbar" horizontalAlignment="center" selectedIndex="{{ segmentSelectedIndex }}" marginTop="20">
<SegmentedBarItem title="Example A" />
<SegmentedBarItem title="Example B" />
<SegmentedBarItem title="Example C" />
</SegmentedBar>
<!-- Example A: Fab -->
<ContentView visibility="{{ segmentSelectedIndex === 0 ? 'visible' : 'collapsed' }}" row="1" width="75" height="75" borderRadius="38" backgroundColor="#65adf1" sharedTransitionTag="fab" horizontalAlignment="right" verticalAlignment="bottom" marginBottom="20" marginRight="30" tap="{{open}}" sharedTransitionIgnore="{{segmentSelectedIndex!==0}}" />
<!-- Example B: Multiple Shapes in Layout Containers -->
<GridLayout row="1" visibility="{{ segmentSelectedIndex === 1 ? 'visible' : 'collapsed' }}" tap="{{open}}" marginTop="20" example2="true">
<GridLayout rows="" columns="*,auto,*,auto,*,auto,*,auto,*" verticalAlignment="bottom" marginBottom="20">
<ContentView col="1" width="40" height="40" borderRadius="20" backgroundColor="#65adf1" sharedTransitionTag="shape1" sharedTransitionIgnore="{{segmentSelectedIndex!==1}}" />
<ContentView col="3" width="40" height="40" borderRadius="20" backgroundColor="#65adf1" sharedTransitionTag="shape2" sharedTransitionIgnore="{{segmentSelectedIndex!==1}}" />
<ContentView col="5" width="40" height="40" borderRadius="20" backgroundColor="#65adf1" sharedTransitionTag="shape3" sharedTransitionIgnore="{{segmentSelectedIndex!==1}}" />
<ContentView col="7" width="40" height="40" borderRadius="20" backgroundColor="#65adf1" sharedTransitionTag="shape4" sharedTransitionIgnore="{{segmentSelectedIndex!==1}}" />
</GridLayout>
</GridLayout>
<!-- Example C: Dynamic sharedTransitionTags passed around -->
<GridLayout row="2" visibility="{{ segmentSelectedIndex === 2 ? 'visible' : 'collapsed' }}" marginTop="20" example3="true">
<ListView items="{{ items }}" itemTap="{{onItemTap}}" separatorColor="transparent">
<ListView.itemTemplate>
<GridLayout columns="auto,*" padding="8">
<ContentView marginLeft="10" width="40" height="40" borderRadius="20" backgroundColor="#65adf1" sharedTransitionTag="{{dynamicTag}}" />
<Label col="1" marginLeft="10" text="{{ title }}" />
</GridLayout>
</ListView.itemTemplate>
</ListView>
</GridLayout>
</GridLayout>
</Page>

View File

@@ -0,0 +1,56 @@
import { Observable, EventData, Page, ShowModalOptions, SharedTransition, ModalTransition, PageTransition, FadeTransition, SlideTransition } from '@nativescript/core';
let page: Page;
export function navigatingTo(args: EventData) {
page = <Page>args.object;
page.bindingContext = new TransitionsModel();
}
// Could create a complete custom example which extends some of the built in options
// class SampleCustomModalTransition extends ModalTransition implements TransitionType {
// }
// SharedTransition.DEBUG = true;
export class TransitionsModel extends Observable {
open() {
page.frame.navigate({
moduleName: `pages/transitions/transitions-detail`,
transition: SharedTransition.custom(new PageTransition(), {
interactive: {
dismiss: {
finishThreshold: 0.5,
},
},
// toPageStart: {
// duration: 1000,
// },
// fromPageEnd: {
// duration: 500,
// },
}),
});
}
openModal() {
page.showModal('pages/transitions/transitions-modal', {
transition: SharedTransition.custom(new ModalTransition(), {
interactive: {
dismiss: {
finishThreshold: 0.5,
},
},
pageStart: {
y: 200,
// duration: 400,
},
pageReturn: {
y: 100,
// duration: 500,
},
}),
closeCallback(args) {
// console.log('close modal callback', args);
},
} as ShowModalOptions);
}
}

View File

@@ -0,0 +1,23 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
<Page.actionBar>
<ActionBar title="Transitions" icon="" class="action-bar">
</ActionBar>
</Page.actionBar>
<GridLayout rows="*,auto,auto,*">
<GridLayout row="1" rows="auto,auto" tap="{{ open }}">
<Image sharedTransitionTag="image" src="https://cdn.pixabay.com/photo/2012/08/27/14/19/mountains-55067__340.png" width="100" />
<Label row="1" text="Open Page" class="text-center" color="black" />
</GridLayout>
<GridLayout row="2" rows="auto,auto,auto,auto" tap="{{ openModal }}" marginTop="50">
<Image sharedTransitionTag="image-modal" src="https://cdn.pixabay.com/photo/2012/08/27/14/19/mountains-55067__340.png" width="100" />
<Label row="1" text="NativeScript Rocks!" sharedTransitionTag="open-modal-label" class="text-center" color="black" />
<ContentView row="2" sharedTransitionTag="open-modal-box1" borderWidth="5" borderColor="yellow" marginTop="20" width="50" height="50" borderRadius="999" backgroundColor="purple" />
<ContentView row="3" sharedTransitionTag="open-modal-box2" marginTop="20" width="20" height="20" borderRadius="999" backgroundColor="orange" />
<ContentView row="3" sharedTransitionTag="open-modal-box3" marginTop="20" width="20" height="20" borderRadius="999" backgroundColor="red" horizontalAlignment="left" marginLeft="100" />
<ContentView row="4" sharedTransitionTag="open-modal-box4" marginTop="20" width="20" height="20" borderRadius="999" backgroundColor="pink" horizontalAlignment="right" marginRight="100" />
</GridLayout>
</GridLayout>
</Page>

View File

@@ -0,0 +1,10 @@
import { Observable, EventData, Page } from '@nativescript/core';
let page: Page;
export function navigatingTo(args: EventData) {
page = <Page>args.object;
page.bindingContext = new TransitionsModel();
}
export class TransitionsModel extends Observable {}

View File

@@ -0,0 +1,19 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
<Page.actionBar>
<ActionBar title="Transition Detail" icon="" class="action-bar">
</ActionBar>
</Page.actionBar>
<GridLayout rows="auto,auto,*" columns="*" verticalAlignment="top">
<Image row="1" sharedTransitionTag="image" src="https://cdn.pixabay.com/photo/2012/08/27/14/19/mountains-55067__340.png" height="230" verticalAlignment="top" marginTop="10" />
<GridLayout row="2" rows="auto,auto,auto">
<Label text="Opened Navigated Page" verticalAlignment="top" marginTop="20" class="text-center" fontSize="28" color="black" />
<ContentView row="2" sharedTransitionTag="open-modal-box1" marginTop="20" width="200" height="200" borderRadius="100" backgroundColor="purple" />
<ContentView row="1" sharedTransitionTag="open-modal-box2" marginTop="20" width="75" height="75" borderRadius="37" backgroundColor="orange" />
<ContentView row="2" sharedTransitionTag="open-modal-box3" marginTop="20" width="30" height="30" borderRadius="15" backgroundColor="red" horizontalAlignment="right" marginRight="50" />
<ContentView row="1" sharedTransitionTag="open-modal-box4" marginTop="20" width="30" height="30" borderRadius="15" backgroundColor="pink" horizontalAlignment="left" marginLeft="50" />
</GridLayout>
</GridLayout>
</Page>

View File

@@ -0,0 +1,22 @@
import { Observable, ShownModallyData, LoadEventData, Page, ShowModalOptions } from '@nativescript/core';
let page: Page;
let closeCallback: Function;
export function onShownModally(args: ShownModallyData) {
closeCallback = args.closeCallback;
if (args.context) {
args.context.shownModally = true;
}
}
export function onLoaded(args: LoadEventData) {
page = args.object as Page;
page.bindingContext = new TransitionModalPage();
}
export class TransitionModalPage extends Observable {
close() {
closeCallback();
}
}

View File

@@ -0,0 +1,16 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="onLoaded" shownModally="onShownModally">
<GridLayout rows="auto,auto,*" columns="*" verticalAlignment="top">
<Button text="Close" tap="{{close}}" horizontalAlignment="right" marginRight="10" />
<Image row="1" sharedTransitionTag="image-modal" src="https://cdn.pixabay.com/photo/2012/08/27/14/19/mountains-55067__340.png" height="230" verticalAlignment="top" marginTop="10" />
<GridLayout row="2" rows="auto,auto,auto">
<Label text="Opened Modal" verticalAlignment="top" marginTop="20" class="text-center" fontSize="28" color="black" />
<ContentView row="1" sharedTransitionTag="open-modal-box2" marginTop="20" width="75" height="75" borderRadius="37" backgroundColor="orange" />
<ContentView row="2" sharedTransitionTag="open-modal-box1" marginTop="20" width="200" height="200" borderRadius="100" backgroundColor="purple" />
<ContentView row="2" sharedTransitionTag="open-modal-box3" marginTop="20" width="30" height="30" borderRadius="15" backgroundColor="red" horizontalAlignment="right" marginRight="50" />
<ContentView row="1" sharedTransitionTag="open-modal-box4" marginTop="20" width="30" height="30" borderRadius="15" backgroundColor="pink" horizontalAlignment="left" marginLeft="50" />
</GridLayout>
</GridLayout>
</Page>