Let's travel to Space shall we?
Thinking about visual relation in your apps interaction can help deliver delightful user experiences. It all starts in the layout of your screens.
We'll be using the "Vanilla TypeScript" flavor of core to illustrate but this is applicable to all flavors.
When laying out this first page, we know we want the earth to move into it's place on the second page while the Space Man's visor comes straight at us, revealing the settling of the second page.
We can represent this as follows -- omitting css details to focus on layout markup structure alone - see StackBlitz example above with css
<GridLayout rows="3*, 2*">
<image
src="~/assets/Earth.png"
sharedTransitionTag="earth"
width="75%"
stretch="aspectFit"
></image>
<image
src="~/assets/SpaceMan.png"
sharedTransitionTag="spaceman"
stretch="aspectFill"
></image>
<GridLayout rows="160,auto,auto,auto,*" sharedTransitionTag="title">
<label row="1" text="SPACE" />
<label row="2" text="TRAVEL" />
<button row="3" text="TICKETS AVAILABLE" width="55%" tap="{{ open }}" />
</GridLayout>
<GridLayout
row="1"
rows="auto,*"
columns="auto,*"
sharedTransitionTag="infobox"
>
<ContentView width="150" height="100">
<image
src="~/assets/Astronaut.jpg"
width="150"
height="100"
stretch="aspectFill"
></image>
</ContentView>
<StackLayout col="1">
<label text="98% RATING" />
<label text="FIND OUT MORE" />
</StackLayout>
<label row="1" colSpan="2" text="{{descriptionText}}" textWrap="true" />
</GridLayout>
</GridLayout>
Following the GridLayout docs, we represent the general layout with a 2 row grid where 1 row takes up 3 times the available space, 3*
, and the remaining 2 times the available space, 2*
.
In the first row, we position our earth image below the spaceman image (layered like a photoshop file in this case) and our "Space Travel" text with the button layered on top of that.
In the second row, we have a detailed description area.
We know we want some relational visual movement on the Earth and SpaceMan images so we declare them with sharedTransitionTag
values:
<image
src="~/assets/Earth.png"
sharedTransitionTag="earth"
width="75%"
stretch="aspectFit"
></image>
<image
src="~/assets/SpaceMan.png"
sharedTransitionTag="spaceman"
stretch="aspectFill"
></image>
We also want the "Space Travel" title and "Tickets Available" button to move together out to the left when revealing the next page, so we include that altogether in it's own layout container we can declare sharedTransitionTag
on as well:
<GridLayout rows="160,auto,auto,auto,*" sharedTransitionTag="title">
<label row="1" text="SPACE" />
<label row="2" text="TRAVEL" />
<button row="3" text="TICKETS AVAILABLE" width="55%" tap="{{ open }}" />
</GridLayout>
Lastly, we'd like the detailed description area to move down and out of the way so we place sharedTransitionTag
on it too:
<GridLayout
row="1"
rows="auto,*"
columns="auto,*"
sharedTransitionTag="infobox"
></GridLayout>
With the SharedTransition API we can customize how we want our Shared Element Transition to occur.
import { SharedTransition } from '@nativescript/core';
// setup page navigation to move to a new "earth-view" we will layout in a moment
page.frame.navigate({
moduleName: 'earth-view',
// we define our transition here
transition: SharedTransition.custom(new PageTransition(), {
// where the page we're moving away from should end up
pageEnd: {
// on iOS, we can define "independent" tag movement
sharedTransitionTags: {
// fade spaceman out while moving and scaling up
spaceman: {
opacity: 0,
y: 20,
scale: {
x: 6,
y: 6,
},
},
// fade title out while moving off to the left
title: {
opacity: 0,
x: -200,
},
// fade infobox out while moving it down and out
infobox: {
opacity: 0,
y: 800,
},
},
},
// how the page returns back
pageReturn: {
// we use duration to override default spring settings
// in this example, looks better with linear duration animation
duration: 600,
},
}),
});
When laying out the second page, we just consider the relational elements in our design while laying it out however we'd like.
<GridLayout rows="*, *, *, *">
<GridLayout columns="auto,auto">
<image
src="~/assets/back.png"
tap="{{close}}"
stretch="aspectFill"
width="20"
height="20"
/>
<button col="1" text="Back" tap="{{close}}" width="50" />
</GridLayout>
<GridLayout row="1" rowSpan="2" rows="auto,auto,auto">
<label text="EARTH" />
<label
row="1"
text="Earth is the third planet from the Sun and the only object in the universe known to harbor life."
textWrap="true"
width="70%"
/>
<image
row="2"
src="~/assets/Moon.png"
stretch="aspectFit"
width="80"
height="80"
/>
</GridLayout>
<GridLayout rows="*" row="2" rowSpan="2">
<image
src="~/assets/Earth.png"
stretch="aspectFit"
sharedTransitionTag="earth"
/>
</GridLayout>
</GridLayout>
What is perhaps most interesting in this example is that we are only declaring 1 view with sharedTransitionTag="earth"
since it exists on both Page A and Page B. Doing so allows the earth to fluidly transition from it's position on Page A to it's new larger position on Page B.
The rest of the animation is actually covered by the SharedTransition.custom
options provided, which for iOS, defines various sharedTransitionTag
elements on Page A alone to animate according to those options.
So if iOS supports "independent" tagged elements only present on one page and not the other, how to handle Android?
Note: Android "independent" tagged elements will be supported in future core release.
The solution is actually quite simple. You can include inside a <android>
section of your markup the view layout that you want Android to create relation with. So we can simply do that for the section that contains the other sharedTransitionTag
's we want animated, eg:
<android>
<GridLayout rows="3*, 2*" rowSpan="4">
<image
src="~/assets/SpaceMan.png"
stretch="aspectFill"
sharedTransitionTag="spaceman"
scaleX="8"
scaleY="8"
translateY="940"
></image></GridLayout
></android>
On Android, when the page is navigated, it will now find those relational sharedTransitionTag
's to animated to the positions their view declares.
This example illustrates the versatility of Shared Element Transitions in @nativescript/core for iOS and Android allowing you to achieve visually stunning results.
Have some creative ideas for really engaging visuals? Share with the community and tag on Twitter with #nstricks
and we look forward to seeing the beautiful creations you come up with!