Rotating pages

This example, based on the awesome Ecommerce-UX design by Levani Ambokadze, shows the use of SwipeGesture and SwipeNavigate, as well as how you can achieve 3D transformations using Rotate and Viewport.

The icons are from Google’s Material Icons pack.

Swipe navigation

Swiping left and right on both detail cards and the preview photos is done using a PageControl, which handles linear navigation between panels using swipes. The following is a simplification of the two PageControls used in the app:

<Panel>
    <PageControl>
    	<Each Count="3">
			<Panel Margin="10,10,10,10">
				<Panel Color="#F00">
					<Translation Y="0.46" RelativeTo="ParentSize" ux:Name="pageTranslation" />
				</Panel>
			   	<Panel Alignment="Top" Height="60%"> <!-- Preview images -->
					<PageControl>
						<Panel Color="#0FA" />
						<Panel Color="#0AF" />
						<Panel Color="#F0A" />
					</PageControl>
				</Panel>
		   	</Panel>
	    </Each>
    </PageControl>
</Panel>

Note that the PageControl for navigating the preview images is a child of the PageControl which navigates the pages. In cases like this, the child PageControl will always get input, rather than the parent. Additionally, this snippet uses default transition animations instead of custom ones. In our example app, we set Transition="None" on the PageControls to enable custom animations, and specified our own EnteringAnimation and ExitingAnimation. These will be discussed in further detail later in the article.

Swiping up to reveal more details

This effect uses a SwipingAnimation hooked up to a SwipeGesture in order to translate the detail card upwards as the user swipes up. While a PageControl uses swipe gestures to navigate pages, SwipeGesture is a more general way of handling swipes.

The SwipeGesture itself is a child of the first panel after our Viewport element (More on this later). This ensures the entire app can take swipe events through that SwipeGesture. This is because SwipeGestures listen for swipes in the area of the screen their parent container populates.

For the actual translation of the cards, we put a SwipingAnimation and Translation element in the cards, like this:

<SwipingAnimation Source="swipeGesture">
	<Change pageTranslation.Y=".12" />
	<Change moreDiv.Opacity="1" Easing="ExponentialOut" />
	<Change infoPointPanel.Opacity="1" Easing="CubicIn" />
	<Change ratingsGrid.Opacity="1" Easing="ExponentialIn" />
</SwipingAnimation>
<Translation Y="0.46" RelativeTo="ParentSize" ux:Name="pageTranslation" />

The unused SwipeGesture in the preview image container

If you had a keen eye while looking over the example code, you might of noticed there is an unused SwipeGesture in the preview image container:

<Panel ux:Name="topPart" Alignment="Top" Height="60%">
	<SwipeGesture Direction="Up"/>
	<!-- [...] -->
</Panel>

This works as a mask, telling the SwipeGesture closer to the root node (App) to ignore swipes happening on that panel. This happens because SwipeGesture respects other SwipeGestures deeper down on the node tree. This way, by putting an unused SwipeGesture on the top container, the parent SwipeGesture will ignore that area.

Rotating the detail cards in 3D

Fuse supports 3D transformation out of the box. However, to pull this off, you need to wrap your UX in a Viewport element. This element handles perspective projection. We initialize the viewport like this:

<App>
	<Viewport Perspective="300" CullFace="Back">
		<!-- Your app -->
	</Viewport>
</App>

It is important to set CullFace to Back so any rotated elements aren’t visible if they face away from the camera.

The cards look rather ugly the last degrees before they are rotated out of the way, as there is a considerable amount of aliasing. To fix this, we fade out the cards as they leave the center of the screen using an exponential easing method.

Lastly, the other elements that make up the cloth display have to be moved out of the way as the clothing card is swiped away. In our example, this is the preview image navigation panel, and the progress dots for said navigation panel. The following snippet shows the enter/exit animations in their entirety:

<EnteringAnimation>
	<Rotate Target="bottomPartOuter" DegreesY="70" Duration="1" />
	<Change bottomPart.Opacity="0" Easing="ExponentialIn" Duration=".75" Delay=".0"/>
	<Change topScaling.Factor="0.5" Duration="1" />
	<Change topPart.Opacity="0" Duration="1" Easing="ExponentialOut" />
	<Change pageIndicatorPart.Opacity="0" Duration="1" Easing="ExponentialOut" />
</EnteringAnimation>
<ExitingAnimation>
	<Rotate Target="bottomPartOuter" DegreesY="-70" Duration="1" />
	<Change bottomPart.Opacity="0" Easing="ExponentialIn" Duration=".75" Delay=".0"/>
	<Change topScaling.Factor="0.5" Duration="1" />
	<Change topPart.Opacity="0" Duration="1" Easing="ExponentialOut" />
	<Change pageIndicatorPart.Opacity="0" Duration="1" Easing="ExponentialOut" />
</ExitingAnimation>

Styling

It is good practice when writing UX to define all custom UI elements as classes with semantic names. This results in the UX communicating well to a third party reader what different parts of the UX document does. The example has therefore almost all items that visually look like an UI element defined in classes. Additionally, due to the amount of classes used in this example, we put them in a separate UX file which we included. This reduces clutter in MainView.ux, something which is very useful when working with large projects.

We pre-define all our theme colors. This helps make the UX more understandable, as it is easier to comprehend which visual element you are looking at the UX for:

<float4 ux:Global="FontColor" ux:Value="#556E7A" />
<float4 ux:Global="LighterFontColor" ux:Value="#556D79" />
<float4 ux:Global="InactiveColor" ux:Value="#ABB5BC" />
<float4 ux:Global="HintColor" ux:Value="#DEEBF4" />

<float4 ux:Global="CardBackground" ux:Value="#FFF" />
<float4 ux:Global="BackgroundColor" ux:Value="#f3f3f5" />

<float4 ux:Global="ColorSelRed" ux:Value="#F25254" />
<float4 ux:Global="ColorSelBlue" ux:Value="#6389AF" />
<float4 ux:Global="ColorSelGreen" ux:Value="#70C1B3" />
<float4 ux:Global="ColorSelYellow" ux:Value="#FFE664" />

Further, we use classes to define both regularly used sets of properties, and more complicated custom UI elements. The following snippet shows how the circular SizeButton is defined. Notice how we utilize ux:Property to add our own property to our new class:

<Circle ux:Class="SizeButton" Width="40" Height="40" Margin="15">
  <string ux:Property="Label" />
  <Text Alignment="Center" Value="{ReadProperty Label}" Color="FontColor" />
  <Stroke Width="2">
    <SolidColor Color="InactiveColor" />
  </Stroke>
</Circle>

The following is an example of a more simple use of ux:Class, for often used properties.

<Image ux:Class="Icon" Margin="15" Color="FontColor" Height="40" />

Last but not least, we include Style.ux in our main file using:

<ux:Include File="Style.ux" />

That’s about it!