-
Couldn't load subscription status.
- Fork 1.4k
Description
See Technical Background below for more details.
Background (Summary)
As part of our refactoring work there are some Win2D based behaviors which don't fit in well in any particular package, see technical background below for more details. It's unclear if we would want to create a temporary home vs. utilizing existing work already in the toolkit and from the goal of #3108 to bootstrap a revitalization of our animations in general to support all the original scenarios.
This work item it to detail the new animation system and how everything can work together with these more independent package setups.
Describe the problem this feature would solve
The new animation system is focused primarily on extending the success we've seen with exposing the Implicit Composition Animation APIs to XAML as well as the flexibility we've seen from our Pipeline Brushes to leverage similar API surfaces with code-behind and XAML that are smaller in scope, but powerful to compose.
We have built this proposal with a variety of scenarios in mind to fill in gaps in our existing APIs, streamline the usage of other API dependencies like Behaviors and Win2D effects, and address new opportunities. We've done this as well with a focus on ease-of-use and composability to present this as a complete alternative to animations that are either composition or XAML based.
Describe the solution
Firstly, existing XAML Composition based Animation Support in the Toolkit should still be supported (with hopefully with no to minimal breaking changes). References:
This section is collapsed by scenario with embedded examples of a proposed XAML based syntax. Please expand each scenario to explore and see how it would be handled with the new system. Each example may build on previously defined examples, so it is best to review in order from top to bottom. This order does not necessarily imply any particular priority for each feature.
This section will be updated inline based on feedback.
Scenarios
Explicitly Defined Animations in XAML
This would expand upon our Implicit animation XAML API to create an independent animation that can be triggered or run in different cases outside of the normal show/hide cases of the composition API.
<!-- This animation causes the button to move left when clicked -->
<Button Content="Click Me"
Click="Button_Click"> <!-- C# code would trigger animation: E.g. MoveAnimation.Start() -->
<animations:Explicit.Animations>
<animations:AnimationSet x:Name="MoveAnimation">
<animations:TranslationAnimation From="0" To="0, -200, 0" Duration="0:0:1" Easing="Cubic"/>
</animations:AnimationSet>
</animations:Explicit.Animations>
</Button>Key framed animations are still supported (existing+)
Existing complex crafted key frame animations should still be supported.
<!-- This animation causes the button to move up with a bump down first when clicked -->
<Button Content="Click Me"
Click="Button_Click"> <!-- C# code would trigger animation: E.g. MoveAnimation.Start() -->
<animations:Explicit.Animations>
<animations:AnimationSet x:Name="MoveAnimation">
<animations:ScalarAnimation Target="Translation.Y" Duration="0:0:1" From="0" To="-200">
<animations:ScalarKeyFrame Key="0.1" Value="30"/>
<animations:ScalarKeyFrame Key="0.5" Value="0.0"/>
</animations:ScalarAnimation>
</animations:AnimationSet>
</animations:Explicit.Animations>
</Button>Multiple copies of the same animation type can be used
As an alternative to key frames for a single animation, multiple animations can be used within the same collection without overlapping time windows:
<!-- This animation causes the button to move left and back fancily when clicked -->
<Button Content="Click Me"
Click="Button_Click"> <!-- C# code would trigger animation: E.g. MoveAnimation.Start() -->
<animations:Explicit.Animations>
<animations:AnimationSet x:Name="MoveAnimation">
<animations:TranslationAnimation From="0" To="0, -200, 0" Duration="0:0:1" Easing="Cubic"/>
<animations:TranslationAnimation From="0, -200, 0" To="0" Delay="0:0:1" Duration="0:0:1" Easing="Bounce"/>
</animations:AnimationSet>
</animations:Explicit.Animations>
</Button>Expression animations can still be used (existing+)
Expression animations allow for animations to interact with dynamic values within the context of it's parent control.
<!-- TODO: Need to define FinalValue (it's not well defined scenario in our current docs) -->
<Button Content="Click Me"
Click="Button_Click"> <!-- C# code would trigger animation: E.g. MoveAnimation.Start() -->
<animations:Explicit.Animations>
<animations:AnimationSet x:Name="MoveAnimation">
<animations:OffsetAnimation Duration="0:0:1">
<animations:ExpressionKeyFrame Key="0.2" Value="This.FinalValue / 2"/>
</animations:OffsetAnimation>
</animations:AnimationSet>
</animations:Explicit.Animations>
</Button>Existing example with Implicit Animation:
<Page.Resources>
<animations:AnimationSet x:Key="OffRotAnim">
<animations:OffsetAnimation Duration="0:0:1"/>
<animations:ScalarAnimation Target="RotationAngleInDegrees" ImplicitTarget="Offset" Duration="0:0:1">
<animations:ExpressionKeyFrame Key="1" Value="This.StartingValue + 90"/>
</animations:ScalarAnimation>
</animations:AnimationSet>
</Page.Resources>
<Border x:Name="Element" Height="100" Width="100" Background="Red"
extensions:VisualEx.NormalizedCenterPoint="0.5,0.5,0" animations:Implicit.Animations="{StaticResource OffRotAnim}"/>
</Border>AnimationSets can be nested to share properties
This makes it easier to group similar timed and transitioned animations to build more complex timelines. These properties are shared with children and multiple collections can be nested together. The named parent collection can also define these properties.
<Button Content="Click Me"
Click="Button_Click"> <!-- C# code would trigger animation: E.g. MoveAnimation.Start() -->
<animations:Explicit.Animations>
<animations:AnimationSet x:Name="MoveAnimation" Duration="0:0:1">
<!-- This rotation gets its duration from the parent collection of 1 -->
<animation:RotateAnimation From="0" To="90"/>
<!-- Redefined properties will overwrite the default from the parent -->
<animations:AnimationScope Delay="0:0:1" Duration="0:0:2" Easing="Elastic">
<!-- Therefore, these two child animations both have a duration of 2, a delay of 1, and elastic easing -->
<animations:TranslationAnimation From="0" To="0, -200, 0" />
<animations:OpacityAnimation From="0" To="1.0" />
</animations:AnimationScope>
</animations:AnimationSet>
</animations:Explicit.Animations>
</Button>Ability to choose whether an Animation collection starts concurrently or sequentially
By default all animations start concurrently at the same time (this would remain the default). However, being able to create a timeline of animations that occur sequentially instead can make it easier to manage and visualize a sequence of complex animations.
<!-- The button will move left first wait a second and then move right -->
<Button Content="Click Me"
Click="Button_Click"> <!-- C# code would trigger animation: E.g. MyAnimation.Start() -->
<animations:Explicit.Animations>
<animations:AnimationSet x:Key="MyAnimation" Duration="0:0:1" IsSequential="True">
<animations:TranslationAnimation From="0" To="0, -200, 0"/>
<animations:TranslationAnimation From="0, -200, 0" To="0" Delay="0:0:1"/>
</animations:AnimationSet >
</animations:Explicit.Animations>
</Button>Complex example:
<Button Content="Click Me"
Click="Button_Click"> <!-- C# code would trigger animation: E.g. MyAnimation.Start() -->
<animations:Explicit.Animations>
<animations:AnimationSet x:Key="MyAnimation" IsSequential="True">
<!-- This set of Translation/Opacity Animations start first after a second delay for 2 seconds (the child collection does NOT inherit the IsSequential tag) -->
<animations:AnimationSet Delay="0:0:1" Duration="0:0:2">
<animations:TranslationAnimation From="0, -200, 0" To="0" />
<animations:OpacityAnimation From="0" To="1.0" />
</animations:AnimationSet >
<!-- After the previous two animations have finished, there's now a new delay of 1 second before the next Rotation/Translation animations occur -->
<animations:AnimationSet Delay="0:0:1" Duration="0:0:3">
<animation:RotateAnimation Duration="0:0:1" From="0" To="90"/> <!-- This one is shorter than the other as it's overridden still -->
<animations:TranslationAnimation From="0" To="0, -10, 0" />
</animations:AnimationSet >
<!-- Finally after the last set, this final translation waits another second and then plays -->
<animations:TranslationAnimation From="0, -10, 0" To="0" Delay="0:0:1" Duration="0:0:1"/>
</animations:AnimationSet>
<!-- The whole animation timeline is 9 seconds long (1 + 2 + 1 + 3 + 1 + 1 = sum of duration + delays)-->
</animations:Explicit.Animations>
</Button>Can re-use the same animation in a resource with multiple elements
In some scenarios there may be a common animation that will want to be played on different elements. They should be able to be added to a resource dictionary and used where appropriate. For an example (with the Behaviors, see more in section below):
<Page>
<Page.Resources>
<animations:AnimationSet x:Name="FadeAnimation">
<animations:OpacityAnimation From="1" To="0.3" Duration="0:0:2" Easing="Elastic"/>
</animations:AnimationSet >
</Page.Resources>
<StackPanel>
<Button>
<Interactivity:Interaction.Behaviors>
<Interactions:EventTriggerBehavior EventName="Click">
<behaviors:StartAnimationAction Animation="{x:Bind FadeAnimation}" />
</Interactions:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
<Button>
<Interactivity:Interaction.Behaviors>
<Interactions:EventTriggerBehavior EventName="Click">
<behaviors:StartAnimationAction Animation="{x:Bind FadeAnimation}" />
</Interactions:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
</StackPanel>
</Page>Ability to trigger other animations within an animation
Sometimes an animation may be composed of multiple elements. These may want to occur together or one after another, in combination with different delays (or using sequential mode defined above), this ability would allow complex compositing of animations with ease.
<!-- In this example when the button is clicked, the button will wiggle, wait a second, and then the image will fade-in -->
<Grid>
<Button Content="Click Me"
Click="Button_Click"> <!-- C# code would trigger animation: E.g. MyAnimation.Start() -->
<animations:Explicit.Animations>
<animations:AnimationSet x:Key="MyAnimation" Duration="0:0:0.25" IsSequential="True">
<animations:TranslationAnimation From="0" To="0, -50, 0"/>
<animations:TranslationAnimation From="0, -50, 0" To="0"/>
<animations:StartAnimationActivity Animation="{x:Bind FadeInAnimation}" Delay="0:0:1"/>
</animations:AnimationSet >
</animations:Explicit.Animations>
</Button>
<Image Source="...">
<animations:Explicit.Animations>
<animations:AnimationSet x:Key="FadeInAnimation" Duration="0:0:1">
<animations:OpacityAnimation From="0" To="1"/>
</animations:AnimationSet >
</animations:Explicit.Animations>
</Image>
</Grid>If we had a general animation in a resource we could use a TargetObject="{x:Bind OurImage}" instead to play that generalized animation on that particular object:
<Grid>
<Grid.Resources>
<animations:AnimationSet x:Key="FadeInAnimation" Duration="0:0:1">
<animations:OpacityAnimation From="0" To="1"/>
</animations:AnimationSet >
</Grid.Resources>
<Button Content="Click Me"
Click="Button_Click"> <!-- C# code would trigger animation: E.g. MyAnimation.Start() -->
<animations:Explicit.Animations>
<animations:AnimationSet x:Key="MyAnimation" Duration="0:0:0.25" IsSequential="True">
<animations:TranslationAnimation From="0" To="0, -50, 0"/>
<animations:TranslationAnimation From="0, -50, 0" To="0"/>
<animations:StartAnimationActivity TargetObject="{x:Bind OurImage}" Animation="{x:Bind FadeInAnimation}" Delay="0:0:1"/>
</animations:AnimationSet >
</animations:Explicit.Animations>
</Button>
<Image x:Name="OurImage" Source="..." Opacity="0"/>
</Grid>Multiple animations can be triggered from within a single animation collection
To allow for complex chaining, triggered animations should be allowed in any number (with reason) to either daisy-chain animations across different elements or to set-off a coordinated animation across multiple elements.
<!-- In this example when the button is clicked, the left and right images will start fading in with the middle one lagging behind half-a-second, they'll then disappear in the same order after 3 seconds from being visible -->
<Page>
<Page.Resources>
<animations:AnimationSet x:Name="CoordinationedAnimation"> <!-- Note: If we made this sequential, then the entire sequence of the first animation would have to finish before the 2nd began. -->
<animations:StartAnimationActivity TargetObject="{x:Bind Image1}" Animation="{x:Bind FadeInAnimation}"/>
<animations:StartAnimationActivity TargetObject="{x:Bind Image2}" Animation="{x:Bind FadeInAnimation}" Delay="0:0:0.5"/>
<animations:StartAnimationActivity TargetObject="{x:Bind Image3}" Animation="{x:Bind FadeInAnimation}"/>
</animations:AnimationSet>
<animations:AnimationSet x:Name="FadeInAnimation" IsSequential="True">
<animations:OpacityAnimation From="0" To="1" Duration="0:0:2" Easing="Elastic"/>
<animations:StartAnimationActivity Animation="{x:Bind FadeOutAnimation}" Delay="0:0:3"/>
</animations:AnimationSet >
<animations:AnimationSet x:Name="FadeOutAnimation">
<animations:OpacityAnimation From="1" To="0" Duration="0:0:2"/>
</animations:AnimationSet>
</Page.Resources>
<Grid>
<Button Content="Click Me"
Click="Button_Click"/> <!-- C# code would trigger animation: E.g. CoordinatedAnimation.Start() -->
<StackPanel Orientation="Horizontal">
<Image x:Name="Image1" Source="..." Opacity="0"/>
<Image x:Name="Image2" Source="..." Opacity="0"/>
<Image x:Name="Image3" Source="..." Opacity="0"/>
</StackPanel>
</Grid>
</Page>Support `RepeatBehaviors` on Collections and Animations
Animations alone are powerful, but there are times when you want them to repeat or loop for various reasons. We should support adding a RepeatBehavior element on both collections and animations. This would provide a greater amount of flexibility for animation definitions. P2: A stretch goal in the future would be to support a RepeatDirection enum for either Forward, Backward, ForwardThenBackward, or BackwardThenForward modes of controlling how an animation repeats itself.
<!-- The button will move left first wait a second and then move right and then immediately start over forever -->
<Button Content="Click Me"
Click="Button_Click"> <!-- C# code would trigger animation: E.g. MyAnimation.Start() -->
<animations:Explicit.Animations>
<animations:AnimationSet x:Key="MyAnimation" Duration="0:0:1" IsSequential="True" RepeatBehavior="Forever">
<animations:TranslationAnimation From="0" To="0, -200, 0"/>
<animations:TranslationAnimation From="0, -200, 0" To="0" Delay="0:0:1"/>
</animations:AnimationSet >
</animations:Explicit.Animations>
</Button>More complex future example:
<Button Content="Click Me"
Click="Button_Click"> <!-- C# code would trigger animation: E.g. MyAnimation.Start() -->
<animations:Explicit.Animations>
<animations:AnimationSet x:Key="MyAnimation" IsSequential="True" Delay="0:0:1" Duration="0:0:2"
RepeatBehavior="0:0:9"> <!-- This entire animation will repeat until 9 seconds have passed (so it should play 3 times in full) -->
<animations:TranslationAnimation From="0, -200, 0" To="0" />
<!-- This would cause the button to fade out and then back in again (a single time but by repeating the animation in reverse) -->
<animations:OpacityAnimation From="1.0" To="0" RepeatBehavior="2x" RepeatDirection="ForwardThenBackward"/>
</animations:AnimationSet>
</animations:Explicit.Animations>
</Button>TODO: Choose which framework the animation is running on (Composition vs. XAML)
TODO: Have C# API for Animations in General that's easy to use and exposes duration, delays, easings, enableDependentAnimations, layer (composition or XAML), calling action after animation.
Integration with Microsoft.Toolkit.Uwp.UI.Behaviors (new package)
Can trigger animations with Behaviors
This would allow the new Explicit animations to be triggered from a variety of scenarios within XAML, for instance events caused by user interaction, data loading, progress completion, etc...
<Button Content="Click Me">
<Interactivity:Interaction.Behaviors>
<Interactions:EventTriggerBehavior EventName="Click">
<behaviors:StartAnimationAction Animation="{x:Bind FadeInAnimation}" />
</Interactions:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<animations:Explicit.Animations>
<animations:AnimationSet x:Name="FadeInAnimation">
<animations:OpacityAnimation From="0" To="1" Duration="0:0:1"/>
</animations:AnimationSet>
</animations:Explicit.Animations>
</Button>Ability to trigger Behavior Actions
By including the behaviors package, we'd also have the ability to trigger other actions within this ecosystem.
<Button Content="Click Me">
...
<animations:Implicit.ShowAnimations>
<animations:OpacityAnimation From="0" To="1" Duration="0:0:1"/>
<behaviors:AnimationAction>
<Interactions:CallMethodAction TargetObject="{x:Bind MyPage}" MethodName="ShowAnimationCompleted"/>
</behaviors:AnimationAction>
</animations:Implicit.ShowAnimations>
</Button>Integration with Microsoft.Toolkit.Uwp.UI.Media (Win2D based Effects)
Expose Pipeline Effects to General Framework Effects
This is base work outside of animations specifically to expose the work done in the Pipeline Brush API effects to be usable on any general framework element to provide similar effects..
<TextBlock ...>
<media:VisualExtensions.Effects>
<media:PipelineVisual IsMasked="True" Placement="Background"> <!-- Default would be Foreground -->
<effects:OpacityEffect Value="0.8"/>
<effects:BlendEffect Mode="Multiply" Source="{effects:BackdropSource}"/>
<effects:BlurEffect x:Name="BlurEffect" Amount="16"/>
</media:PipelineVisual>
</media:VisualExtensions.Effects>
</TextBlock>Can specify to add animations to an effect with `IsAnimatable`
By including the behaviors package, we'd also have the ability to trigger other actions within this ecosystem.
<Button ...>
<media:VisualExtensions.Effects>
<media:PipelineVisual>
<effects:OpacityEffect Value="0.4"/>
<effects:BlendEffect Mode="Multiply" Source="{effects:BackdropSource}"/>
<effects:BlurEffect x:Name="BlurEffect" Amount="0" IsAnimatable="True"/>
</media:PipelineVisual>
</media:VisualExtensions.Effects>
<Interactivity:Interaction.Behaviors>
<Interactions:EventTriggerBehavior EventName="Click">
<Interactions:CallMethodAction TargetObject="{x:Bind TestAnimation}" MethodName="Start"/>
</Interactions:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<animations:Explicit.Animations>
<animations:AnimationSet x:Name="TestAnimation">
<animations:OpacityAnimation From="0" To="1" Duration="0:0:1"/>
<animations:TranslationAnimation Axis="X" From="-40" To="40" Duration="0:0:0.5" Layer="Xaml"/>
<media:EffectAnimation Target="{x:Bind BlurEffect}" PropertyName="Amount" Value="16" Duration="0:0:4" Easing="Cubic"/>
</animations:AnimationSet >
</animations:Explicit.Animations>
</Button>Visual effect animations work with implicit animations as well
This is base work outside of animations specifically to expose the work done in the Pipeline Brush API effects to be usable on any general framework element to provide similar effects..
<Button ...>
<extensions:PipelineVisual>
<effects:BlurEffect x:Name="BlurEffect" Amount="16" IsAnimatable="True">
<effects:ShadeEffect Color="#FF22222" Intensity="0.2"/>
</extensions:PipelineVisual>
<!-- Button will slide and fade in while going from blurry to clear -->
<animations:Implicit.ShowAnimations>
<animations:TranslationAnimation Duration="0:0:1" From="0, -200, 0" To="0" />
<animations:OpacityAnimation Duration="0:0:1" From="0" To="1.0" />
<effects:EffectAnimation Target="{x:Bind BlurEffect}" Property="Amount" From="16" To="0" Duration="0:0:2"/>
</animations:Implicit.ShowAnimations>
</Button>Currently Lower-Priority Scenarios/Goals
TODO: Expose our Implicit Animation API via C# (or do we do this already?)
TODO: Existing Implicit Animation Toolkit users can re-use the same code without any modification... (it's important to note that the base scenarios of attaching to implicit/show/hide animations are still a requirement as you can do today with the current animations package, this spec is more about detailing all the additions to the base system we had here and changing some of the underlying infrastructure.)
Describe alternatives you've considered
We know from experience that there are many scenarios that are difficult to accomplish with Storyboards, as we've seen the great power and flexibility that comes with our Implicit animation work. We also know that there are some scenarios more easily handled in code, but that exposing XAML helps better define workflow patterns between view and function. We want this to build upon the work we've already seen within the Toolkit and the community to move the platform forward in this space.
Additional context & Screenshots
Quick test example of an explicitly defined mixed animation triggered from code-behind:

(see above examples for proposed syntax)
Animating an Effect using a Behavior on a Button Click (all in XAML!):

Custom XAML animation combining new types (eg. clip) with also Win2D effect animations:

Example of the declarative C# APIs to create complex animations, combining composition and XAML:
hMFUNA88vr.mp4
Technical Background (Detailed)
This is a follow-on from work being done in #3594 and PR #3634 which is trying to sort out the dependencies of the toolkit and clean-up their complexities. Currently the Animations package pulls in both Win2D and the XAML Behaviors package, which then get also included in the Controls package.
The goal here is to have a more light-weight animations package which doesn't pull in these dependencies directly. Instead, both our Win2D based Media package and the new Behaviors package (in #3634) would add the animations library for supporting those scenarios if desired by the end developer.
Migrations:
- AnimationExtensions -> AnimationBuilder extensions (Blur, Rotate, etc... extension methods on FrameworkElement in Code-behind in animations package or media package)
- 'Effects' (in Animation) ->
- Media PipelineBuilder Effects on Visuals AttachAsync (new expose in XAML)
- Media +IsAnimatable to existing effects Allow Animations on them (in XAML)
- Animation Behaviors -> Goes away - new system of using a regular Behavior to Trigger custom Animation (see above) or new visual effects attached to visuals for non-animation scenarios.
This will streamline the ability for developers to consume the toolkit and focus the purpose of each individual package.