Windows 10 Anniversary Update Preview–Composition and Expressions

When I first looked at the composition APIs for animation in this post I said that I didn’t understand what animation expressions were and that;

“I read through Kenny’s explanation of ‘Expressions’ but I need to dig into that more in order to really get a grip as to what’s going on there.”

For a little while, I’ve felt happy with that ignorance but then I saw this video on C9 a few weeks ago;

image

and what I saw in there was pretty cool and I thought it was about time I tried to give a little bit more thought into these ‘expression animations’ to see what they are.

In the meantime, I’d also read Rob’s excellent post;

“Fun with Expressions”

From that article I think I understand “The basics of an expression” and “Property sets” but I must admit that at the time of writing I skipped the part about parallax scrolling in a ListView – I’ve seen this example in a couple of places before but I feel I need to play with the pieces some more before I attempt to climb that hill. I’m too dumb to make so many mental leaps in one go Smile

Whether it’s right or wrong, I’m starting to think of these ‘expressions’ and ‘CompositionObjects’ as almost being a form of sibling to DependencyObjects and their dependency properties but with a stronger notion of ‘binding’ that has been augmented by an expression language that allows for calculations and functions.

I wanted to try out some pieces for myself though and after scanning the list of functions  for expressions, the first thing I thought of was how I could “bind” the rotation of some Visual to the value of a slider and so that’s what I started to experiment with.

I made a small UI;

<Page
    x:Class="App24.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App24"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition
                Height="Auto" />
        </Grid.RowDefinitions>
        <Rectangle
            Width="100"
            Height="100"
            Fill="Blue"
            Stroke="Black"
            StrokeThickness="2"
            x:Name="rectangle">
            
        </Rectangle>
        <Slider
            Margin="12"
            Grid.Row="1"
            Minimum="0"
            Maximum="360"
            Value="{x:Bind SliderValue,Mode=TwoWay}" />
    </Grid>
</Page>

and then I wrote a little code behind;

using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Hosting;

namespace App24
{
  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();

      this.Loaded += OnLoaded;
    }

    public double SliderValue
    {
      get
      {
        // reach into my underlying CompositionPropertySet for the value
        float value = 0.0f;

        this.propertySet?.TryGetScalar("r", out value);

        return (value);
      }
      set
      {
        // reach into my underlying CompositionPropertySet for the value
        this.propertySet.InsertScalar("r", (float)value);
      }
    }
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      // Get hold of my rectangle's visual
      this.rectangleVisual = ElementCompositionPreview.GetElementVisual(
        this.rectangle);

      // Keep hold of the compositor
      this.compositor = this.rectangleVisual.Compositor;

      // Change the centre point for the animation.
      this.rectangleVisual.CenterPoint = new System.Numerics.Vector3(
        (float)this.rectangle.ActualWidth / 2.0f,
        (float)this.rectangle.ActualHeight / 2.0f,
        0);

      // Make my own CompositionPropertySet
      this.propertySet = this.compositor.CreatePropertySet();

      // Make sure there's a value called 'r' in there set to 0.0f.
      this.SliderValue = 0.0f;

      // Make my animation.
      var expressionAnimation = this.compositor.CreateExpressionAnimation(
        "myProps.r");

      // Explain to the animation that when I say "myProps" I mean this set of
      // properties I'm passing in here...
      expressionAnimation.SetReferenceParameter("myProps", this.propertySet);

      // Kick off the animation.
      this.rectangleVisual.StartAnimation("RotationAngleInDegrees", expressionAnimation);
    }
    CompositionPropertySet propertySet;
    Visual rectangleVisual;
    Compositor compositor;
  }
}

Now, this is more code (of course) than just putting a transform onto the rectangle and then binding it directly to the value of the slider but the aim is to try out the new APIs here rather than to produce the smallest example Smile

In this code, I’ve created my first CompositionPropertySet and I’ve added one property to it that I’d named “r” and it’s a simple scalar which I’ve exposed behind the getter/setter for the class property called SliderValue.

I’ve then created an expression animation that picks up that value (and changes to it) and associates it with the rotation of the rectangle’s visual.

That all works quite nicely.

image

but it’s not exactly enabling any new scenario for me.

My next quick test was to change the expression “myProps.r” into a new expression “myProps.r * 0.5” and, sure enough, my slider now binds to a range from 0 to 180 degrees of rotation on my rectangle.

I duplicated the Rectangle in the XAML file 3 more times, taking away the rectangle’s name and parenting those 4 elements into a Grid named “parentGrid”.

I then changed the OnLoaded function so that it animated each of the 4 rectangles in a different direction while still rotating them – i.e. 2 expression animations applied to each element and a slightly more complex (and dynamic) expression in one of those animations;

 void OnLoaded(object sender, RoutedEventArgs e)
    {
      // Keep hold of the compositor
      this.compositor = ElementCompositionPreview.GetElementVisual(
        this.parentGrid).Compositor;

      // Make my own CompositionPropertySet
      this.propertySet = this.compositor.CreatePropertySet();

      // Make sure there's a value called 'r' in there set to 0.0f.
      this.SliderValue = 0.0f;

      var increment = 360.0d / this.parentGrid.Children.Count;

      for (int i = 0; i < this.parentGrid.Children.Count; i++)
      {
        var angle = (i + 1) * increment;
        var rectangle = (Rectangle)this.parentGrid.Children[i];
        var visual = ElementCompositionPreview.GetElementVisual(rectangle);

        visual.CenterPoint = new System.Numerics.Vector3(
          (float)rectangle.ActualWidth / 2.0f,
          (float)rectangle.ActualHeight / 2.0f,
          0);

        var animation = this.compositor.CreateExpressionAnimation(
           $"Vector3(Cos(ToRadians({angle})) * myProps.r, Sin(ToRadians({angle})) * myProps.r, 0)");

        animation.SetReferenceParameter("myProps", this.propertySet);

        visual.StartAnimation("Offset" , animation);

        animation = this.compositor.CreateExpressionAnimation("myProps.r");
        animation.SetReferenceParameter("myProps", this.propertySet);
        visual.StartAnimation("RotationAngleInDegrees", animation);
      }
    }

and that also worked out quite nicely although debugging missing parentheses in that magic string passed to the CreateExpressionAnimation function starts to get a little bit ‘fun’ Smile

I wanted to take that a little further and so I took out the 4 rectangles from the XAML file and switched the code such that it created the rectangles dynamically, making 30 rectangles and then trying to animate them out along some spiral while rotating and based on the value of the slider still;

and that’s with relatively little code in that OnLoaded method;

    void OnLoaded(object sender, RoutedEventArgs e)
    {
      // Keep hold of the compositor
      this.compositor = ElementCompositionPreview.GetElementVisual(
        this.parentGrid).Compositor;

      // Make my own CompositionPropertySet
      this.propertySet = this.compositor.CreatePropertySet();

      // Make sure there's a value called 'r' in there set to 0.0f.
      this.SliderValue = 0.0f;

      var brush = new SolidColorBrush(Colors.Blue);

      // have 30 rectangles
      var rectangleCount = 30.0f;

      // Space them out so that 10 fit around a circle
      var angularSpacing = Math.PI / 5.0f;
      var radius = 10.0f;

      for (int i = 0; i < rectangleCount; i++)
      {
        var rectangle = new Rectangle()
        {
          Width = 20,
          Height = 20,
          Fill = brush
        };
        this.parentGrid.Children.Add(rectangle);

        var visual = ElementCompositionPreview.GetElementVisual(rectangle);

        visual.CenterPoint = new System.Numerics.Vector3(
          (float)rectangle.ActualWidth / 2.0f,
          (float)rectangle.ActualHeight / 2.0f,
          0);

        // The rectangle should animate to an angle 
        var angle = angularSpacing * i;

        // And a distance governed by some constant. 360 here is the
        // max value of the slider in the UI.
        var distance = i * radius / 360.0f;

        // We want to animate out to this position based on the value
        // in the slider.
        var animation = this.compositor.CreateExpressionAnimation(
           $"Vector3(Cos({angle}) * {distance} * myProps.r, " +
           $"Sin({angle}) * {distance} * myProps.r, 0)");
      
        animation.SetReferenceParameter("myProps", this.propertySet);

        visual.StartAnimation("Offset", animation);

        animation = this.compositor.CreateExpressionAnimation("myProps.r");
        animation.SetReferenceParameter("myProps", this.propertySet);
        visual.StartAnimation("RotationAngleInDegrees", animation);
      }
    }

and, so far, I’ve driven everything off the value of my slider. That value is bound to a setter which changes a property in my property set.

All the animations are then driven from the value of r” in that property set.

However, Visuals also have property sets and so I could (e.g.) get rid of my slider altogether and have the positions and rotation values of my rectangles bound to some other property which is also animating.

Taking the slider out of the UI;

<Page
    x:Class="App24.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App24"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition
                Height="Auto" />
        </Grid.RowDefinitions>
        <Grid
            x:Name="parentGrid">
        </Grid>
    </Grid>
</Page>

and re-working the code-behind such that it creates a Visual (spriteVisual in the code below) in the parentGrid Grid with a blue background and an opacity which is then animated from 0->1->0 over a second period.

I can then anchor my other animations off that changing Opacity property;

using System;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Hosting;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace App24
{
  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();

      this.Loaded += this.OnLoaded;
    }
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      // Get the background grid visual
      var gridVisual = ElementCompositionPreview.GetElementVisual(this.parentGrid);

      // Keep hold of the compositor
      this.compositor = gridVisual.Compositor;

      // Create a new visual to put on top of the grid
      var spriteVisual = this.compositor.CreateSpriteVisual();

      // Paint it blue
      var colorBrush = this.compositor.CreateColorBrush(Colors.CornflowerBlue);
      spriteVisual.Brush = colorBrush;

      // We should handle re-size too, we don't.
      spriteVisual.Size = new System.Numerics.Vector2(
        (float)this.parentGrid.ActualWidth,
        (float)this.parentGrid.ActualHeight);

      // Put it into the Grid.
      ElementCompositionPreview.SetElementChildVisual(this.parentGrid, spriteVisual);

      // Animate the opacity 0->1->0
      var animation = this.compositor.CreateScalarKeyFrameAnimation();
      animation.InsertKeyFrame(0.0f, 0.0f);
      animation.InsertKeyFrame(0.5f, 1.0f);
      animation.InsertKeyFrame(1.0f, 0.0f);
      animation.Duration = TimeSpan.FromSeconds(2);
      animation.IterationBehavior = AnimationIterationBehavior.Forever;
      spriteVisual.StartAnimation("Opacity", animation);

      // Create the XAML rectangles.
      var brush = new SolidColorBrush(Colors.Blue);

      // have 30 rectangles
      var rectangleCount = 30.0f;

      // Space them out so that 10 fit around a circle
      var angularSpacing = Math.PI / 5.0f;
      var radius = 10.0f;

      for (int i = 0; i < rectangleCount; i++)
      {
        var rectangle = new Rectangle()
        {
          Width = 20,
          Height = 20,
          Fill = brush
        };
        this.parentGrid.Children.Add(rectangle);

        var visual = ElementCompositionPreview.GetElementVisual(rectangle);

        visual.CenterPoint = new System.Numerics.Vector3(
          (float)rectangle.ActualWidth / 2.0f,
          (float)rectangle.ActualHeight / 2.0f,
          0);

        // The rectangle should animate to an angle 
        var angle = angularSpacing * i;

        // And a distance governed by some constant. 
        var distance = i * radius;

        // We animate the position of the rectangle based on the Opacity property
        // of the backdrop which is also being animated.
        var exprAnimation = this.compositor.CreateExpressionAnimation(
           $"Vector3(Cos({angle}) * {distance} * myProps.Opacity, " +
           $"Sin({angle}) * {distance} * myProps.Opacity, 0)");

        exprAnimation.SetReferenceParameter("myProps", spriteVisual);

        visual.StartAnimation("Offset", exprAnimation);

        // We animate the rotation of the rectangle based on the Opacity property
        // of the backdrop which is also being animated.
        exprAnimation = this.compositor.CreateExpressionAnimation("myProps.Opacity * 360");
        exprAnimation.SetReferenceParameter("myProps", spriteVisual);
        visual.StartAnimation("RotationAngleInDegrees", exprAnimation);
      }
    }
    Compositor compositor;
  }
}

and this gives a smooth flashing effect;

and, while I’m sure this can be done to some level in XAML it’s starting to be obvious that there’s a lot of power here that can be expressed quite succinctly and which seems to give smooth, predictable performance and these ideas of;

    1. Property Sets having values that can be monitored for some kind of change notification
    2. Visuals having Property Sets
    3. Animations being able to set property values based on calculated expressions from values in Property Sets

start to hit home and seem to be really clever Smile

I wanted to see how this plays with effects and so I modified my UI again to have both an image and a rectangle and it’s worth saying that everything that I’ve experimented with so far in this post ran on build 10586 but at this point I switched to build 14332 in order to pick up the CompositionBackdropBrush that I tried out in my previous post.

<Page
    x:Class="App24.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App24"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid x:Name="parentGrid">
        <Grid
            x:Name="imgGrid">
            <Image
                Source="http://www.travelandescape.ca/wp-content/uploads/2012/05/Thailand-The-Beach.jpg"
                Stretch="UniformToFill" />
        </Grid>
        <Rectangle
            Width="100"
            Height="100"
            Fill="Blue"
            VerticalAlignment="Center"
            HorizontalAlignment="Center"
            x:Name="myRectangle" />
    </Grid>
</Page>

and then I wrote code behind this in order to rotate the rectangle from 0 to 360 degrees and back over a 5 second interval and to blur the image behind the rectangle by dividing that rotation angle by 36 to scale it from 0 to 10;

using Microsoft.Graphics.Canvas.Effects;
using System;
using System.Numerics;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Hosting;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace App24
{
  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();

      this.Loaded += this.OnLoaded;
    }
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      // grab the visual for the rectangle
      var rectangleVisual = ElementCompositionPreview.GetElementVisual(this.myRectangle);

      rectangleVisual.CenterPoint =
        new Vector3(
          (float)this.myRectangle.ActualWidth / 2.0f,
          (float)this.myRectangle.ActualHeight / 2.0f,
          0.0f);

      // grab the compositor
      this.compositor = rectangleVisual.Compositor;

      // animate the rectangle's rotation from 0->360->0 over 5 seconds.
      var animation = this.compositor.CreateScalarKeyFrameAnimation();
      animation.InsertKeyFrame(0.0f, 0.0f);
      animation.InsertKeyFrame(0.5f, 360.0f);
      animation.InsertKeyFrame(1.0f, 0.0f);
      animation.Duration = TimeSpan.FromSeconds(5);
      animation.IterationBehavior = AnimationIterationBehavior.Forever;

      // Start that animation on the rectangle visual.
      rectangleVisual.StartAnimation("RotationAngleInDegrees", animation);

      // Now create a blur effect description.
      var blur = new GaussianBlurEffect()
      {
        Name = "Blur", // Name needed here so we can animate it.
        BorderMode = EffectBorderMode.Hard,
        BlurAmount = 0.0f,
        Source = new CompositionEffectSourceParameter("source")
      };
      // Make the factory.
      var effectFactory = compositor.CreateEffectFactory(blur,
        new string[] { "Blur.BlurAmount" });

      // Make the brush.
      var effectBrush = effectFactory.CreateBrush();

      // Put a backdrop brush as the source of the effect.
      effectBrush.SetSourceParameter("source", compositor.CreateBackdropBrush());

      // Make a visual.
      var visual = this.compositor.CreateSpriteVisual();
      visual.Size = new Vector2(
        (float)this.parentGrid.ActualWidth, (float)this.parentGrid.ActualHeight);

      // Paint it with the brush
      visual.Brush = effectBrush;

      // Put it on top of our image so the effect applies to the image
      ElementCompositionPreview.SetElementChildVisual(this.imgGrid, visual);

      // Finally, make an expression animation to set the blur amount to
      // 1/36 * the rotation angle of the rectangle
      var exprAnimation = this.compositor.CreateExpressionAnimation(
        "rectangle.RotationAngleInDegrees / 36");

      // tell the expression what "rectangle" is meant to mean
      exprAnimation.SetReferenceParameter("rectangle", rectangleVisual);

      // Start that animation.
      effectBrush.StartAnimation("Blur.BlurAmount", exprAnimation);
    }
    Compositor compositor;
  }
}

Giving the effect;

and so now the blur of that effect brush is tied to the rotation of the rectangle.

The last experiment that I wanted to try was to mix these expressions with implicit animations and also to try and set up some ‘chain’ of visuals such that each visual had an expression that referenced an earlier visual in the chain.

I wasn’t quite sure what effect I wanted other than to experiment and the effect that I ended up with is as below;

What’s going on in that demo is;

    1. I create a blue rectangle and I move it around the screen whenever the mouse moves (this is all just XAML)
    2. I have an implicit animation which animates any changes to its Offset.
    3. I have an animation that rotates it over a 10 second period through 0->360->0.
    4. I create 10 green rectangles N = 1 to N = 10 with the blue rectangle playing the part of N = 0 and each rectangle M uses an expression to;
      1. Make its X,Y offset equal to 0.75 * that of the M-1 rectangle.
      2. Make its scale equal to 0.75 * that of the M-1 rectangle.
      3. Make its opacity equal to 0.75 * that of the M-1 rectangle.
      4. Make its rotation equal to that of the M-1 rectangle minus 45 degrees.

The XAML here is very simple;

<Page
  x:Class="App24.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="using:App24"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d">

  <Canvas
    x:Name="parentCanvas"
    PointerMoved="OnPointerMoved"
    Background="Silver">
    <Rectangle
      Width="100"
      Height="100"
      Fill="Blue"
      VerticalAlignment="Center"
      HorizontalAlignment="Center"
      x:Name="myRectangle"
      Visibility="Collapsed" />
  </Canvas>
</Page>

and the code looks a bit lengthy but then it’d be fairly easy for me to refactor this into some common functions but I left it in its ‘longer form’ for now;

using Microsoft.Graphics.Canvas.Effects;
using System;
using System.Numerics;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Hosting;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace App24
{
  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();

      this.Loaded += this.OnLoaded;
    }
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      // grab the visual for the rectangle
      this.rectangleVisual = ElementCompositionPreview.GetElementVisual(this.myRectangle);

      // grab the compositor
      this.compositor = rectangleVisual.Compositor;

      // Create an animation for whenever this visual changes its offset
      var animation = this.compositor.CreateVector3KeyFrameAnimation();
      animation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");
      animation.Target = "Offset";
      animation.Duration = TimeSpan.FromMilliseconds(500);

      // Make an implicit animations collection
      var collection = this.compositor.CreateImplicitAnimationCollection();

      // Whenever our visual changes its offset, we want it implicitly animated.
      collection["Offset"] = animation;
      this.rectangleVisual.ImplicitAnimations = collection;

      // Set the center point for rotation
      this.rectangleVisual.CenterPoint = new Vector3(
        (float)this.myRectangle.ActualWidth / 2.0f,
        (float)this.myRectangle.ActualHeight / 2.0f,
        0.0f);

      // animate the rectangle's rotation from 0->360->0 over 2 seconds.
      var rotationAnimation = this.compositor.CreateScalarKeyFrameAnimation();
      rotationAnimation.InsertKeyFrame(0.0f, 0.0f);
      rotationAnimation.InsertKeyFrame(0.5f, 360.0f);
      rotationAnimation.InsertKeyFrame(1.0f, 0.0f);
      rotationAnimation.Duration = TimeSpan.FromSeconds(10);
      rotationAnimation.IterationBehavior = AnimationIterationBehavior.Forever;

      // set that going.
      this.rectangleVisual.StartAnimation("RotationAngleInDegrees", rotationAnimation);
    }
    void CreateRectangles()
    {
      // Let's make 10, green rectangles.
      var rectangleCount = 10;
      var brush = new SolidColorBrush(Colors.Green);

      var previousVisual = this.rectangleVisual;

      for (int i = 0; i < rectangleCount; i++)
      {
        // XAML rectangle.
        var rectangle = new Rectangle()
        {
          Fill = brush,
          Width = 100,
          Height = 100
        };
        // Add to canvas.
        this.parentCanvas.Children.Add(rectangle);

        // Get visual.
        var visual = ElementCompositionPreview.GetElementVisual(rectangle);

        // Center it for rotation.
        visual.CenterPoint = new Vector3(
          (float)rectangle.ActualWidth / 2.0f,
          (float)rectangle.ActualHeight / 2.0f,
          0.0f);

        // We offset it as 0.75* the previous rectangle (X,Y only)
        var exprAnimation = this.compositor.CreateExpressionAnimation(
          $"Vector3(previous.Offset.X * 0.75, previous.Offset.Y * 0.75, 0)");
        exprAnimation.SetReferenceParameter("previous", previousVisual);
        visual.StartAnimation("Offset", exprAnimation);

        // We scale it as 0.75* the previous rectangle (X,Y only)
        exprAnimation = this.compositor.CreateExpressionAnimation(
          "Vector3(previous.Scale.X * 0.75, previous.Scale.Y * 0.75, previous.Scale.Z)");
        exprAnimation.SetReferenceParameter("previous", previousVisual);
        visual.StartAnimation("Scale", exprAnimation);

        // We rotate it 45 degrees behind the previous rectangle
        exprAnimation = this.compositor.CreateExpressionAnimation(
          "previous.RotationAngleInDegrees - 45");
        exprAnimation.SetReferenceParameter("previous", previousVisual);
        visual.StartAnimation("RotationAngleInDegrees", exprAnimation);

        // We make it 0.75* as opaque as the previous rectangle.
        exprAnimation = this.compositor.CreateExpressionAnimation(
          "previous.Opacity * 0.75");
        exprAnimation.SetReferenceParameter("previous", previousVisual);
        visual.StartAnimation("Opacity", exprAnimation);

        // Current becomes previous.
        previousVisual = visual;
      }
    }
    void OnPointerMoved(object sender, PointerRoutedEventArgs e)
    {
      var pos = e.GetCurrentPoint(this.parentCanvas).Position;

      if (this.myRectangle.Visibility == Visibility.Collapsed)
      {
        this.myRectangle.Visibility = Visibility.Visible;
        this.CreateRectangles();
      }
      Canvas.SetLeft(
        this.myRectangle, pos.X - (this.myRectangle.ActualWidth / 2));
      Canvas.SetTop(
        this.myRectangle, pos.Y - (this.myRectangle.ActualHeight / 2));
    }
    Visual rectangleVisual;
    Compositor compositor;
  }
}

Note that the OnLoaded handler does a bit of the work and then the CreateRectangles function does the rest when it sees the first PointerMoved event.

It turns out then that ‘chaining’ expression animations on a set of visuals is a simple thing to do and, no doubt, could lead to some fairly fancy effects when combined with functions.

The more I play with this layer, the more powerful it seems to be to me. One thing I think that I would like to see is some abstraction around sizing such that sizes could be specified as percentages or similar and perhaps some way of auto-sizing a visual when its container changes. Other than that, things are looking good Smile

2 thoughts on “Windows 10 Anniversary Update Preview–Composition and Expressions

Comments are closed.