📜 ⬆️ ⬇️

Animated screen orientation changes in a Windows Phone application

It is easy to see that the Windows Phone Store has a lot of applications that work only in portrait orientation. This is partly due to the fact that this is the default state of things in Windows Phone. The standard application calculator can be considered an example of the application that maximizes the possibility of tilting the screen.



In portrait orientation, we get a simple calculator. And in the landscape already - engineering.


')
But the most beautiful thing is the animation of the transition from one mode to another.

Developing a small project: the application TapHint , implemented several things that improve the perception of the program interface. For some reason, it seemed to me a non-trivial implementation of such a useful thing as an animation of a phone coup. And on the Internet, information on this topic was found with some effort. The application itself works with standard NFC-tags of the NDEF format, recording information into them and, accordingly, reading it from them.

For the application page showing the recorded information in the label, it was decided to apply support for animated rollovers. The result is as follows.

Portrait and landscape view:





Frames from intermediate states animation:





As it turned out, the implementation of all possible types of tilting the phone is quite cumbersome.

XAML pages ShowPage.xaml
<phone:PhoneApplicationPage x:Class="Tap_Hint.ShowPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="PortraitOrLandscape" Orientation="Portrait" mc:Ignorable="d" shell:SystemTray.IsVisible="True" Name="showPage" OrientationChanged="showPage_OrientationChanged"> <phone:PhoneApplicationPage.Projection> <PlaneProjection x:Name="showPageRotation" CenterOfRotationX="0" RotationY="0"/> </phone:PhoneApplicationPage.Projection> <phone:PhoneApplicationPage.Resources> <Storyboard x:Name="showPageStoryboardTo"> <DoubleAnimation From="-55.0" To="0.0" Duration="00:00:00.35" Storyboard.TargetName="showPageRotation" Storyboard.TargetProperty="RotationY"> <DoubleAnimation.EasingFunction> <CubicEase EasingMode="EaseIn"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> </Storyboard> </phone:PhoneApplicationPage.Resources> <!--LayoutRoot is the root grid where all page content is placed--> <Grid x:Name="LayoutRoot" Background="Transparent"> <!-- VisualStateManager.VisualStateGroups must be defined in main grid --> <VisualStateManager.VisualStateGroups> <VisualStateGroup> <VisualState x:Name="FromPToLR"> <!-- from portrait to landscape right and so on --> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="showTransform" Storyboard.TargetProperty="TranslateY"> <DiscreteObjectKeyFrame KeyTime="0" Value="-16"/> </ObjectAnimationUsingKeyFrames> <DoubleAnimation From="190.0" To="0.0" Duration="00:00:00.50" Storyboard.TargetName="showTransform" Storyboard.TargetProperty="TranslateX"> <DoubleAnimation.EasingFunction> <CubicEase EasingMode="EaseInOut"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> <DoubleAnimation From="90.0" To="0.0" Duration="00:00:00.50" Storyboard.TargetName="showTransform" Storyboard.TargetProperty="Rotation"> <DoubleAnimation.EasingFunction> <CubicEase EasingMode="EaseInOut"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> </Storyboard> </VisualState> <VisualState x:Name="FromPToLL"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="showTransform" Storyboard.TargetProperty="TranslateY"> <DiscreteObjectKeyFrame KeyTime="0" Value="-16"/> </ObjectAnimationUsingKeyFrames> <DoubleAnimation From="-190.0" To="0.0" Duration="00:00:00.50" Storyboard.TargetName="showTransform" Storyboard.TargetProperty="TranslateX"> <DoubleAnimation.EasingFunction> <CubicEase EasingMode="EaseInOut"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> <DoubleAnimation From="-90.0" To="0.0" Duration="00:00:00.50" Storyboard.TargetName="showTransform" Storyboard.TargetProperty="Rotation"> <DoubleAnimation.EasingFunction> <CubicEase EasingMode="EaseInOut"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> </Storyboard> </VisualState> <VisualState x:Name="FromLRToP"> <Storyboard> <DoubleAnimation From="0.0" To="-190.0" Duration="00:00:00.50" Storyboard.TargetName="showTransform" Storyboard.TargetProperty="TranslateY"> <DoubleAnimation.EasingFunction> <CubicEase EasingMode="EaseInOut"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> <DoubleAnimation From="-90.0" To="0.0" Duration="00:00:00.50" Storyboard.TargetName="showTransform" Storyboard.TargetProperty="Rotation"> <DoubleAnimation.EasingFunction> <CubicEase EasingMode="EaseInOut"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> </Storyboard> </VisualState> <VisualState x:Name="FromLLToP"> <Storyboard> <DoubleAnimation From="0.0" To="-190.0" Duration="00:00:00.50" Storyboard.TargetName="showTransform" Storyboard.TargetProperty="TranslateY"> <DoubleAnimation.EasingFunction> <CubicEase EasingMode="EaseInOut"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> <DoubleAnimation From="90.0" To="0.0" Duration="00:00:00.50" Storyboard.TargetName="showTransform" Storyboard.TargetProperty="Rotation"> <DoubleAnimation.EasingFunction> <CubicEase EasingMode="EaseInOut"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> </Storyboard> </VisualState> <VisualState x:Name="FromLRToLL"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="showTransform" Storyboard.TargetProperty="TranslateY"> <DiscreteObjectKeyFrame KeyTime="0" Value="-16"/> </ObjectAnimationUsingKeyFrames> <DoubleAnimation From="-180.0" To="0.0" Duration="00:00:00.50" Storyboard.TargetName="showTransform" Storyboard.TargetProperty="Rotation"> <DoubleAnimation.EasingFunction> <CubicEase EasingMode="EaseInOut"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> </Storyboard> </VisualState> <VisualState x:Name="FromLLToLR"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="showTransform" Storyboard.TargetProperty="TranslateY"> <DiscreteObjectKeyFrame KeyTime="0" Value="-16"/> </ObjectAnimationUsingKeyFrames> <DoubleAnimation From="180.0" To="0.0" Duration="00:00:00.50" Storyboard.TargetName="showTransform" Storyboard.TargetProperty="Rotation"> <DoubleAnimation.EasingFunction> <CubicEase EasingMode="EaseInOut"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--TitlePanel contains the name of the application and page title--> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="0,0,0,0" Orientation="Horizontal"> <Image Width="32" Height="32" Source="/Tap Hint - logo mini (tr, white).png" Margin="5,0,0,0"/> <TextBlock Canvas.ZIndex="8" Text="{Binding Path=LocalizedResources.AboutPage_ApplicationTitle, Source={StaticResource LocalizedStrings}}" FontSize="22" Margin="5,0,0,0"/> <!--<TextBlock Name="textBlockPlus" Canvas.ZIndex="8" Text="{Binding Path=LocalizedResources.AboutPage_ApplicationTitle_Plus, Source={StaticResource LocalizedStrings}}" FontSize="20" Margin="5,0,0,0" Foreground="#FF4959FF"/>--> </StackPanel> <!--ContentPanel - place additional content here--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Border x:Name="showBorder" Grid.Row="1" Width="400" Height="300" BorderThickness="5" RenderTransformOrigin="0.5,0.5"> <Border.RenderTransform> <CompositeTransform x:Name="showTransform" Rotation="0" TranslateY="-190" TranslateX="0"/> </Border.RenderTransform> <ScrollViewer x:Name="showScroller" VerticalScrollBarVisibility="Auto"> <TextBlock x:Name="showTextBlock" TextAlignment="Center" TextWrapping="Wrap" ScrollViewer.VerticalScrollBarVisibility="Auto"/> </ScrollViewer> </Border> </Grid> </Grid> </phone:PhoneApplicationPage> 


First you need to specify using the parameter SupportedOrientations = "PortraitOrLandscape", which supports both orientations of the application page. The parameter Orientation = “Portrait” sets the default orientation. But it will only tilt the page back and forth without any animations. To describe a set of animations, you need to use the VisualStateManager. In it you can describe all the transitions-VisualState'y, which we want to use in different situations. In each VisualState, you can describe Storyboard with animations (if there are several, they are played at the same time).

For example, a VisualState to switch from portrait orientation to landscape right can be called x: Name = " FromPToLR ". It can be explained that the “landscape right” is the landscape one when the right side of the phone is located at the bottom. Thus we get one portrait orientation and two landscape ones. The situation is aggravated by the fact that it is necessary to describe transitions from one orientation to another in both directions. And what may be unobvious is the transition from one landscape position to another and vice versa. Total six transitions.

CS pages ShowPage.xaml.cs
 using System.Linq; using System.Windows; using System.Windows.Navigation; using System.Windows.Media; using Microsoft.Phone.Controls; namespace Tap_Hint { public partial class ShowPage : PhoneApplicationPage { private PageOrientation m_ePageOrientation = PageOrientation.PortraitUp; public ShowPage() { InitializeComponent(); bool isDefaultColor = SettingsStore.get().extractParamBool(SettingsStore.StoringParams.USE_DEFAULT_COLOR.ToString()); showTextBlock.FontSize = SettingsStore.g_FontSizeStep * (SettingsStore.get().extractParamInt(SettingsStore.StoringParams.FONT_SIZE.ToString()) + 1); if (isDefaultColor) { showTextBlock.Foreground = (Brush)Application.Current.Resources["PhoneAccentBrush"]; } else { showTextBlock.Foreground = new SolidColorBrush(Colors.White); } } protected override void OnNavigatedTo(NavigationEventArgs e) { if (e.NavigationMode == NavigationMode.New) { showPageStoryboardTo.Begin(); if (NavigationContext.QueryString.Values != null && NavigationContext.QueryString.Values.ToArray() != null && NavigationContext.QueryString.Values.ToArray().Length > 0) { if ("tagParam".Equals(NavigationContext.QueryString.Keys.ElementAt(0))) { showTextBlock.Text = NavigationContext.QueryString.Values.ElementAt(0); } if ("isEnc".Equals(NavigationContext.QueryString.Keys.ElementAt(1)) && "true".Equals(NavigationContext.QueryString.Values.ElementAt(1))) { showBorder.BorderBrush = new SolidColorBrush(Color.FromArgb(255, 250, 250, 0)); } else if ("isEnc".Equals(NavigationContext.QueryString.Keys.ElementAt(1)) && "false".Equals(NavigationContext.QueryString.Values.ElementAt(1))) { showBorder.BorderBrush = new SolidColorBrush(Color.FromArgb(255, 20, 230, 30)); } else if ("isEnc".Equals(NavigationContext.QueryString.Keys.ElementAt(1)) && "spec".Equals(NavigationContext.QueryString.Values.ElementAt(1))) {//spec - error or encoded on enother device showBorder.BorderBrush = new SolidColorBrush(Color.FromArgb(255, 240, 30, 30)); } } else { showTextBlock.Text = "no param"; } } } protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) { if (e.NavigationMode != NavigationMode.Back) {//leave the page due to long back button or Windows button, stay on the page return; } App.Current.Terminate(); //application exiting } private void showPage_OrientationChanged(object sender, OrientationChangedEventArgs e) { //playback animations on orientation change if (m_ePageOrientation == PageOrientation.PortraitUp && e.Orientation == PageOrientation.LandscapeRight) { VisualStateManager.GoToState(this, "FromPToLR", true); } else if (m_ePageOrientation == PageOrientation.PortraitUp && e.Orientation == PageOrientation.LandscapeLeft) { VisualStateManager.GoToState(this, "FromPToLL", true); } else if (m_ePageOrientation == PageOrientation.LandscapeRight && e.Orientation == PageOrientation.PortraitUp) { VisualStateManager.GoToState(this, "FromLRToP", true); } else if (m_ePageOrientation == PageOrientation.LandscapeLeft && e.Orientation == PageOrientation.PortraitUp) { VisualStateManager.GoToState(this, "FromLLToP", true); } else if (m_ePageOrientation == PageOrientation.LandscapeRight && e.Orientation == PageOrientation.LandscapeLeft) { VisualStateManager.GoToState(this, "FromLRToLL", true); } else if (m_ePageOrientation == PageOrientation.LandscapeLeft && e.Orientation == PageOrientation.LandscapeRight) { VisualStateManager.GoToState(this, "FromLLToLR", true); } //saving current orientation mode if (e.Orientation == PageOrientation.PortraitUp) { m_ePageOrientation = PageOrientation.PortraitUp; } else if (e.Orientation == PageOrientation.LandscapeRight) { VisualStateManager.GoToState(this, "LandscapeState", true); m_ePageOrientation = PageOrientation.LandscapeRight; } else if (e.Orientation == PageOrientation.LandscapeLeft) { m_ePageOrientation = PageOrientation.LandscapeLeft; } } } } 


In the page code, it remains to use the orientation change handler — showPage_OrientationChanged (). Analyzing what the orientation was with the help of the value of the m_ePageOrientation variable and what orientation the screen has become in e.Orientation, you can apply the desired animation.

It remains to hope that there will be more applications for phones, in which they will not be lazy to make these useful decorations.

Source: https://habr.com/ru/post/272875/


All Articles