Windows (UWP) Point Animations

A week or so ago I noticed a really slick piece of animation done as part of a Flutter app. Unfortunately I can no longer find the tweet as the account has been removed. However, it perked my interest enough to go playing with animations in XAML to see what I can do. Note that … Continue reading “Windows (UWP) Point Animations”

A week or so ago I noticed a really slick piece of animation done as part of a Flutter app. Unfortunately I can no longer find the tweet as the account has been removed. However, it perked my interest enough to go playing with animations in XAML to see what I can do. Note that I’m not trying to reproduce the original sample, just demonstrate that can be done with a bit of XAML and a bit of code.

Since I can’t show you the original animation, I’ll skip to the end and show you what I came up with. What you’re seeing in the following animated GIF is a drag animation that follows the mouse/touch pointer across the screen – imagine you’re using this to reveal a side menu or a different part of the UI of the app.

Ok, so how did I achieve this. Well, whilst I’d love to say that it was as easy as opening Blend, drawing a curve and then recording a storyboard, that is NOT what happened. In fact there’s almost no designer support for creating either the curve or the storyboard, mainly because it involves defining and then animation a path. However, with this said, I did do all the coding in Blend for Visual Studio and it did update the designer as I made changes.

If we take a look at a single frame, we can see what the shape is that we’re going to try to create.

Starting in the top left corner (0,0) we need a path that consists of

  • Horizontal line to (20,0)
  • Vertical line to (20,100)
  • A curve to (80,150)
  • A curve to (20,200)
  • Vertical line to (20,1000)
  • Horizontal line to (0,1000)
  • Vertical line to (0,0) to close the shape

In XAML this looks like

<Path x:Name="path"
        Stroke="Red"
        Fill="Red"
        StrokeThickness="2">
    <Path.Data>
        <PathGeometry>
            <PathGeometry.Figures>
                <PathFigureCollection>

                    <PathFigure StartPoint="0,0">
                        <PathFigure.Segments>
                            <PathSegmentCollection>
                                <LineSegment Point="20,0"
                                                x:Name="StartLine" />
                                <LineSegment Point="20,100"
                                                x:Name="TopLine"/>
                                <BezierSegment Point1="20,125"
                                                x:Name="Part1"
                                                Point2="80,135"
                                                Point3="80,150" />
                                <BezierSegment Point1="80,165"
                                                x:Name="Part2"
                                                Point2="20,175"
                                                Point3="20,200" />
                                <LineSegment Point="20,1000" x:Name="BottomLine"
                                                />
                                <LineSegment Point="0,1000"
                                                x:Name="EndLine" />
                            </PathSegmentCollection>
                        </PathFigure.Segments>
                    </PathFigure>
                </PathFigureCollection>
            </PathGeometry.Figures>
        </PathGeometry>
    </Path.Data>
</Path>

The next part is to animate this shape, which we can do by creating a storyboard and then moving each of the points on the shape. From the Objects and Timeline tool window, click the green + button to create a new Storyboard, which we’ve called our customStoryboard. Unfortunately this is where the designer support for animations runs out.

Dropping into XAML we can define the animations that make up the storyboard

<Page.Resources>
    <Storyboard x:Name="customStoryboard">
        <PointAnimationUsingKeyFrames Storyboard.TargetProperty="Point"
                                        Storyboard.TargetName="StartLine"
                                        EnableDependentAnimation="True">
            <LinearPointKeyFrame KeyTime="0:0:0.5"
                                    x:Name="StartLinePoint"
                                    Value="20,0"/>
        </PointAnimationUsingKeyFrames>
        <PointAnimationUsingKeyFrames Storyboard.TargetProperty="Point"
                                        Storyboard.TargetName="TopLine"
                                        EnableDependentAnimation="True">
            <LinearPointKeyFrame KeyTime="0:0:0.5"
                                    x:Name="TopLinePoint" 
                                    Value="20,100"/>
        </PointAnimationUsingKeyFrames>
        <PointAnimationUsingKeyFrames Storyboard.TargetProperty="Point1"
                                        Storyboard.TargetName="Part1"
                                        EnableDependentAnimation="True">
            <LinearPointKeyFrame KeyTime="0:0:0.5"
                                    x:Name="Part1Point1" 
                                    Value="20,125"/>
        </PointAnimationUsingKeyFrames>
        <PointAnimationUsingKeyFrames Storyboard.TargetProperty="Point2"
                                        Storyboard.TargetName="Part1"
                                        EnableDependentAnimation="True">
            <LinearPointKeyFrame KeyTime="0:0:0.5"
                                    x:Name="Part1Point2"
                                    Value="150,135"/>
        </PointAnimationUsingKeyFrames>
        <PointAnimationUsingKeyFrames Storyboard.TargetProperty="Point3"
                                        Storyboard.TargetName="Part1"
                                        EnableDependentAnimation="True">
            <LinearPointKeyFrame KeyTime="0:0:0.5"
                                    x:Name="Part1Point3" 
                                    Value="150,150"/>
        </PointAnimationUsingKeyFrames>
        <PointAnimationUsingKeyFrames Storyboard.TargetProperty="Point1"
                                        Storyboard.TargetName="Part2"
                                        EnableDependentAnimation="True">
            <LinearPointKeyFrame KeyTime="0:0:0.5"
                                    x:Name="Part2Point1" 
                                    Value="150,165"/>
        </PointAnimationUsingKeyFrames>
        <PointAnimationUsingKeyFrames Storyboard.TargetProperty="Point2"
                                        Storyboard.TargetName="Part2"
                                        EnableDependentAnimation="True">
            <LinearPointKeyFrame KeyTime="0:0:0.5"
                                    x:Name="Part2Point2" 
                                    Value="20,175"/>
        </PointAnimationUsingKeyFrames>
        <PointAnimationUsingKeyFrames Storyboard.TargetProperty="Point3"
                                        Storyboard.TargetName="Part2"
                                        EnableDependentAnimation="True">
            <LinearPointKeyFrame KeyTime="0:0:0.5"
                                    x:Name="Part2Point3" 
                                    Value="20,200"/>
        </PointAnimationUsingKeyFrames>
        <PointAnimationUsingKeyFrames Storyboard.TargetProperty="Point"
                                        Storyboard.TargetName="BottomLine"
                                        EnableDependentAnimation="True">
            <LinearPointKeyFrame KeyTime="0:0:0.5"
                                    x:Name="BottomLinePoint" 
                                    Value="20,1000"/>
        </PointAnimationUsingKeyFrames>
    </Storyboard>
</Page.Resources>

After defining the storyboard, now you can return to the Objects and Timeline window and select the storyboard from the dropdown. Once selected you can position the cursor in the timezone.

At close to the 0.5 second mark (close to completion for most animations) the screen looks like this – definitely on the right track

Up to this point we’ve defined a set of line segments that make up a connected entity and a basic animation that simply adjusts the position of the points. What’s left to do is for the animation to track the mouse cursor or touch point.

We’re going to intercept and handle the Pointer_Moved event that is raised whenever a pointer moves across the page. In the event handler we need to calculate and apply the new position co-ordinates so that the animation can be amended.

The following code illustrate updating values (points) in the animation.

   public MainPage()
        {
            this.InitializeComponent();

            SizeChanged += MainPage_SizeChanged;
        }

        private void MainPage_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Part1.Point3 = new Point(500, e.NewSize.Height / 2);
            Part2.Point2 = new Point(50, e.NewSize.Height - 100);
            Part2.Point3 = new Point(0, e.NewSize.Height);
            EndLine.Point = new Point(0, e.NewSize.Height);
        }

        private void Start_Animation(object sender, RoutedEventArgs e)
        {
            //myStoryboard.Begin();
        }
        const int StartWidth = 50;
        const int MaxDiffX = 400;
        const double HeightToWidthRatio = 1.5;
        private void path_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            // The location of the center is where the mouse/touch point is
            var newCenter = e.GetCurrentPoint(this).Position;
            var part1_point3 = newCenter;

            // Next determine the width of the shape
            var startX = Math.Max(StartWidth, newCenter.X - MaxDiffX);
            var width = newCenter.X - startX;

            // Calculate the height of the shape using the ratio
            var height = width * HeightToWidthRatio;

            // As we approach the other size, calculate how much space there is
            // and if close, reduce the width of the shape accordingly
            var remainingWidth = ActualWidth - newCenter.X;
            if (remainingWidth < width)
            {
                width = remainingWidth;
                startX = Math.Max(StartWidth, newCenter.X - width);
            }

            // The first line goes from 0,0 out along the X axis
            var start_point = new Point(startX, 0);
            // The last line goes from 0,<actual height> out along the X axis
            var end_point = new Point(startX, this.ActualHeight);


            // Calculate top and bottom of the shape
            var top = newCenter.Y - height / 2;
            var top_point = new Point(start_point.X, top);
            var bottom = newCenter.Y + height / 2;
            var part2_point3 = new Point(start_point.X, bottom);

            // Spread the remaining points in the shape
            var part1_point1 = new Point(start_point.X, top+ height * 0.25);
            var part1_point2 = new Point(newCenter.X, top+ height * 0.35);
            var part2_point1 = new Point(newCenter.X, top + height * 0.65);
            var part2_point2 = new Point(start_point.X, top+ height * 0.75);


            StartLinePoint.Value = start_point;
            TopLinePoint.Value = top_point;
            Part1Point1.Value = part1_point1;
            Part1Point2.Value = part1_point2;
            Part1Point3.Value = part1_point3;
            Part2Point1.Value = part2_point1;
            Part2Point2.Value = part2_point2;
            Part2Point3.Value = part2_point3;
            BottomLinePoint.Value = end_point;

            if (customStoryboard.GetCurrentState() != Windows.UI.Xaml.Media.Animation.ClockState.Stopped)
            {
                customStoryboard.Pause();
                customStoryboard.Resume();
            }
            else
            {
                customStoryboard.Begin();

            }
        }

And that’s it – a basic animation written in XAML, with a bit of code behind to check temp. It’s worth pointing out that the use of point based animations will result in smooth animations, rather than simply setting the points directly on the shape.

Page State with the Visual States Manager for Xamarin.Forms

Page State with the Visual States Manager for Xamarin.Forms

One of the current limitations of the Xamarin.Forms implementation of the Visual State Manager (VSM) is that it only works for setting properties on an individual control. Whilst this is great for control state management (think button states like disabled, pressed etc), its incredibly limiting and makes it unsuitable for some typical visual state scenarios. The one that often comes up in a mobile application is for pages that load data. In this scenario you typically have a least three states: Loading, DataLoaded, DataFailedToLoad. In some cases you might even extend this to have states such as Refreshing or LoadingMoreData. For these states you probably want to show/hide different elements on the screen, which is why the current Xamarin.Forms implementation of the VSM isn’t a great option.

Luckily there’s an alternative, which is the Visual State Manager that’s part of the BuildIt.Forms library. Here’s a quick example of it in action:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<ContentPage
             
             
              x_Class=”App14.MainPage”>
     <vsm:VisualStateManager.VisualStateGroups>
         <vsm:VisualStateGroups>
             <vsm:VisualStateGroup Name=”LoadingStates”>
                 <vsm:VisualState Name=”Loading”>
                     <vsm:VisualState.Setters>
                         <vsm:Setter Value=”True” Element=”{x:Reference LoadingLabel}” Property=”IsVisible” />
                     </vsm:VisualState.Setters>
                 </vsm:VisualState>
                 <vsm:VisualState Name=”DataLoaded”>
                     <vsm:VisualState.Setters>
                         <vsm:Setter Value=”True” Element=”{x:Reference LoadedLabel}” Property=”IsVisible” />
                     </vsm:VisualState.Setters>
                 </vsm:VisualState>
                 <vsm:VisualState Name=”DataFailedToLoad”>
                     <vsm:VisualState.Setters>
                         <vsm:Setter Value=”True” Element=”{x:Reference FailedLabel}” Property=”IsVisible” />
                     </vsm:VisualState.Setters>
                 </vsm:VisualState>
             </vsm:VisualStateGroup>
         </vsm:VisualStateGroups>
     </vsm:VisualStateManager.VisualStateGroups>

     <StackLayout VerticalOptions=”Center”>
         <Label Text=”Loading…” x_Name=”LoadingLabel” HorizontalOptions=”Center”  IsVisible=”False” />
         <Label Text=”Success: Data Loaded!!” x_Name=”LoadedLabel” HorizontalOptions=”Center” IsVisible=”False” />
         <Label Text=”Failure :-(” x_Name=”FailedLabel” HorizontalOptions=”Center” IsVisible=”False” />
         <Button Text=”Load Data” Clicked=”LoadClicked” />
     </StackLayout>
</ContentPage>

In this case we’ve defined three Visual States that correspond to showing the LoadingLabel, LoadedLabel and FailedLabel respectively. The code behind for the LoadClicked method illustrates how easily you can switch between the states:

private readonly Random rnd = new Random();


private async void LoadClicked(object sender, EventArgs e)
{
     var success = rnd.Next(0, 1000) % 2 == 0;
     await VisualStateManager.GoToState(this, “Loading”);
     await Task.Delay(2000);
     await VisualStateManager.GoToState(this, success ? “DataLoaded” : “DataFailedToLoad”);
}

Ok, so one last thing we can add in is a bit of animation to make the transition between states a little smoother. Let’s fade our labels in and out:

<vsm:VisualState Name=”Loading”>
     <vsm:VisualState.ArrivingAnimations>
         <animations:AnimationGroup>
             <animations:AnimationGroup.PostAnimations>
                 <animations:FadeAnimation Opacity=”1″
                                           Duration=”500″
                                           Element=”{x:Reference LoadingLabel}” />
             </animations:AnimationGroup.PostAnimations>
         </animations:AnimationGroup>
     </vsm:VisualState.ArrivingAnimations>
     <vsm:VisualState.LeavingAnimations>
         <animations:AnimationGroup>
             <animations:AnimationGroup.PreAnimations>
                 <animations:FadeAnimation Opacity=”0″
                                           Duration=”500″
                                           Element=”{x:Reference LoadingLabel}” />
             </animations:AnimationGroup.PreAnimations>
         </animations:AnimationGroup>
     </vsm:VisualState.LeavingAnimations>

     <vsm:VisualState.Setters>
         <vsm:Setter Value=”True” Element=”{x:Reference LoadingLabel}” Property=”IsVisible” />
     </vsm:VisualState.Setters>
</vsm:VisualState>

The XAML adds a fade in after the state transition has occurred (Post animation) when transitioning to (Arriving at) the the Loading state, and a fade out before the state transition has occurred (Pre animation) when transitioning from (Leaving) the Loading state. As you can see the XAML gets fairly verbose but it’s structured this way to allow for complex combinations and sequences of animations:

<animations:AnimationGroup.PostAnimations>
     <animations:SequenceAnimation>
         <animations:ParallelAnimation>
             <animations:FadeAnimation Opacity=”1″ Duration=”500″ Element=”{x:Reference LoadingLabel}” />
             <animations:ScaleAnimation Scale=”5″ Duration=”500″ Element=”{x:Reference LoadingLabel}” />
             <animations:SequenceAnimation>
                 <animations:RotateAnimation Rotation=”10″ Duration=”250″ Target=”LoadingLabel” />
                 <animations:RotateAnimation Rotation=”0″ Duration=”250″ Target=”LoadingLabel” />
                 <animations:RotateAnimation Rotation=”-10″ Duration=”250″ Target=”LoadingLabel” />
                 <animations:RotateAnimation Rotation=”0″ Duration=”250″ Target=”LoadingLabel” />
             </animations:SequenceAnimation>
         </animations:ParallelAnimation>
         <animations:ScaleAnimation Scale=”1″ Duration=”500″ Element=”{x:Reference LoadingLabel}” />
     </animations:SequenceAnimation>
</animations:AnimationGroup.PostAnimations>

And let’s see this in action:

App14UWP20190317022241

Hopefully you can see from this short post how you can leverage the BuildIt.Forms Visual State Manager to do complex page state management as well as animations. We’ve just released a new beta package compatible with the latest Xamarin.Forms v3.6 and would love feedback (https://www.nuget.org/packages/BuildIt.Forms/2.0.0.27-beta).

Functionality and Usability beats Visual Design and Animations any day of the week!

Functionality and Usability beats Visual Design and Animations any day of the week!

Since Windows 10 was released I’ve periodically opened and attempted to use the Mail and Calendar applications that come preinstalled and in theory provide an integrated experience. As far as UWP applications go, there aren’t many that I look to as great showcases of what the platform is capable of but I figured that Microsoft would invest in their first party applications (there’s also Maps and a few others). It’s taken a few iterations but both Mail and Calendar are actually working quite well – they both support multi-window (although it’s a tad annoying that you can’t tell it to open items in a new window by default), and the UI generally has a lot of polish. However, that’s pretty much where the fairy tale ends.

The reality is that the Mail and Calendar applications are separate applications, and thus when you attempt to do things like “create a meeting from an email”, something that’s a simple drag and drop in Outlook, you are completely out of luck. Further more, despite quite a lot of effort having gone into the visual design, I still find that I’m struggling with basic things like scanning my email and seeing which items are read/unread and which ones I should be focussing on.

I know that these applications aren’t designed as a complete substitute for Outlook but I do wonder who makes the decisions on where the investment in these products is spent. Is there no focus on productivity, or is it just a competition to have the best looking design?