From 9a4c2d358fa6d98e09b30fb4787b0b74beac28df Mon Sep 17 00:00:00 2001 From: hhchaos Date: Fri, 22 Oct 2021 18:09:48 +0800 Subject: [PATCH 01/94] Add TransitionHelper for morphs between two controls. --- .../Enums/AnimationTarget.cs | 37 +++ .../Enums/VisualStateToggleMethod.cs | 22 ++ .../Helpers/AnimationConfig.cs | 38 +++ .../TransitionHelper.AttachedProperty.cs | 94 ++++++ .../Helpers/TransitionHelper.Logic.cs | 306 ++++++++++++++++++ .../Helpers/TransitionHelper.Properties.cs | 96 ++++++ .../Helpers/TransitionHelper.cs | 135 ++++++++ 7 files changed, 728 insertions(+) create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Enums/AnimationTarget.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Enums/VisualStateToggleMethod.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/AnimationTarget.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/AnimationTarget.cs new file mode 100644 index 00000000000..b64b029d9e3 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/AnimationTarget.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// Indicates the target property of the UI element to be animated. + /// + public enum AnimationTarget + { + /// + /// The translation property of a UI element. + /// + Translation, + + /// + /// The scale property of a UI element. + /// + Scale, + + /// + /// The horizontal scale property of a UI element. + /// + ScaleX, + + /// + /// The vertical scale property of a UI element. + /// + ScaleY, + + /// + /// The opacity property of a UI element. + /// + Opacity + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/VisualStateToggleMethod.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/VisualStateToggleMethod.cs new file mode 100644 index 00000000000..1b275f2e038 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/VisualStateToggleMethod.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// Indicates the method of changing the visibility of UI elements. + /// + public enum VisualStateToggleMethod + { + /// + /// Change the visibility of UI elements by modifying the Visibility property. + /// + ByVisibility, + + /// + /// Change the visibility of UI elements by modifying the Opacity property. + /// + ByOpacity + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs new file mode 100644 index 00000000000..1f309b645af --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// Configuration used for UI element animation. + /// + public class AnimationConfig + { + private readonly AnimationTarget[] defaultAnimations = new AnimationTarget[] { AnimationTarget.Translation, AnimationTarget.Opacity }; + + /// + /// Gets or sets id of UI element. + /// + public string Id { get; set; } + + /// + /// Gets or sets additional animations applied to UI elements. + /// + public AnimationTarget[] AdditionalAnimations { get; set; } + + /// + /// Gets all animations applied to UI elements. + /// + public IEnumerable Animations + { + get + { + return new HashSet(this.AdditionalAnimations?.Concat(this.defaultAnimations) ?? this.defaultAnimations); + } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs new file mode 100644 index 00000000000..1123f1d7801 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// A animation helper that morphs between two controls. + /// + public sealed partial class TransitionHelper + { + private const string IdPropertyName = "Id"; + private const string IsIgnoredPropertyName = "IsIgnored"; + + /// + /// Get the animation id of the UI element. + /// + /// The animation id of the UI element + public static string GetId(DependencyObject obj) + { + return (string)obj.GetValue(IdProperty); + } + + /// + /// Set the animation id of the UI element. + /// + public static void SetId(DependencyObject obj, string value) + { + obj.SetValue(IdProperty, value); + } + + /// + /// Id is used to mark the animation id of UI elements. + /// Two elements of the same id on different controls will be connected by animation. + /// + public static readonly DependencyProperty IdProperty = + DependencyProperty.RegisterAttached(IdPropertyName, typeof(string), typeof(TransitionHelper), null); + + /// + /// Get the value indicating whether the UI element needs to be connected by animation. + /// + /// A bool value indicating whether the UI element needs to be connected by animation. + public static bool GetIsIgnored(DependencyObject obj) + { + return (bool)obj.GetValue(IsIgnoredProperty); + } + + /// + /// Set the value indicating whether the UI element needs to be connected by animation. + /// + public static void SetIsIgnored(DependencyObject obj, bool value) + { + obj.SetValue(IsIgnoredProperty, value); + } + + /// + /// IsIgnored is used to mark controls that do not need to be connected by animation, it will disappear/show independently. + /// + public static readonly DependencyProperty IsIgnoredProperty = + DependencyProperty.RegisterAttached(IsIgnoredPropertyName, typeof(bool), typeof(TransitionHelper), new PropertyMetadata(false)); + + private static IEnumerable GetAnimatedElements(UIElement targetElement, IEnumerable filters) + { + if (targetElement == null) + { + return null; + } + + var elements = targetElement.FindDescendants().ToList() ?? new List(); + elements.Add(targetElement); + + return filters == null + ? elements.Where(element => GetId(element) != null).OfType() + : elements.Where(element => GetId(element) != null && filters.Contains(GetId(element))).OfType(); + } + + private static IEnumerable GetIgnoredElements(UIElement targetElement) + { + if (targetElement == null) + { + return null; + } + + var elements = targetElement.FindDescendants().ToList() ?? new List(); + elements.Add(targetElement); + + return elements.Where(element => GetIsIgnored(element)).OfType(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs new file mode 100644 index 00000000000..bf8409aecc3 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -0,0 +1,306 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Threading; +using System.Threading.Tasks; +using Windows.UI.Composition; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Hosting; + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// A animation helper that morphs between two controls. + /// + public sealed partial class TransitionHelper + { + private void UpdateSourceAnimatedElements() + { + this.sourceAnimatedElements.Clear(); + var filters = this.animationConfigs.Select(config => config.Id); + + foreach (var item in GetAnimatedElements(this.Source, filters)) + { + this.sourceAnimatedElements[GetId(item)] = item; + } + } + + private void UpdateTargetAnimatedElements() + { + this.targetAnimatedElements.Clear(); + var filters = this.animationConfigs.Select(config => config.Id); + + foreach (var item in GetAnimatedElements(this.Target, filters)) + { + this.targetAnimatedElements[GetId(item)] = item; + } + } + + private void ToggleVisualState(UIElement target, VisualStateToggleMethod method, bool isVisible) + { + if (target == null) + { + return; + } + + switch (method) + { + case VisualStateToggleMethod.ByVisibility: + target.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed; + break; + case VisualStateToggleMethod.ByOpacity: + var targetVisual = ElementCompositionPreview.GetElementVisual(target); + targetVisual.IsVisible = isVisible; + target.IsHitTestVisible = isVisible; + break; + default: + break; + } + } + + private (UIElement, UIElement) GetPairElements(AnimationConfig config) + { + return this.sourceAnimatedElements.ContainsKey(config.Id) && this.targetAnimatedElements.ContainsKey(config.Id) + ? (this.sourceAnimatedElements[config.Id], this.targetAnimatedElements[config.Id]) + : (null, null); + } + + private async Task AnimateFromSourceToTargetAsync(CancellationToken token) + { + var duration = this.AnimationDuration; + if (this._isInterruptedAnimation) + { + duration *= this.interruptedAnimationReverseDurationRatio; + } + + var animationTasks = new List(); + foreach (var item in this.animationConfigs) + { + (var source, var target) = this.GetPairElements(item); + if (source == null || target == null) + { + continue; + } + + animationTasks.Add(this.AnimateElementsAsync(source, target, duration, item, token)); + } + + animationTasks.Add(this.AnimateIgnoredElementsAsync(this.Source, false, token)); + animationTasks.Add(this.AnimateIgnoredElementsAsync(this.Target, true, token)); + + await Task.WhenAll(animationTasks); + } + + private async Task AnimateFromTargetToSourceAsync(CancellationToken token) + { + var duration = this.AnimationDuration; + if (this._isInterruptedAnimation) + { + duration *= this.interruptedAnimationReverseDurationRatio; + } + + var animationTasks = new List(); + foreach (var item in this.animationConfigs) + { + (var target, var source) = this.GetPairElements(item); + if (source == null || target == null) + { + continue; + } + + animationTasks.Add(this.AnimateElementsAsync(source: source, target, duration, item, token)); + } + + animationTasks.Add(this.AnimateIgnoredElementsAsync(this.Source, true, token)); + animationTasks.Add(this.AnimateIgnoredElementsAsync(this.Target, false, token)); + + await Task.WhenAll(animationTasks); + } + + private void RestoreState(bool isTargetState) + { + this.IsTargetState = isTargetState; + this.ToggleVisualState(this.Source, this.SourceToggleMethod, !isTargetState); + this.ToggleVisualState(this.Target, this.TargetToggleMethod, isTargetState); + } + + private async Task InitStateAsync(bool forceUpdateAnimatedElements = false) + { + this.ToggleVisualState(this.Source, this.SourceToggleMethod, true); + this.ToggleVisualState(this.Target, this.TargetToggleMethod, true); + if (this._needUpdateTargetLayout && this.TargetToggleMethod == VisualStateToggleMethod.ByVisibility) + { + await this.UpdateTargetLayoutAsync(); + } + + if (forceUpdateAnimatedElements) + { + this.UpdateSourceAnimatedElements(); + this.UpdateTargetAnimatedElements(); + } + } + + private async Task UpdateTargetLayoutAsync() + { + this._updateTargetLayoutTaskSource = new TaskCompletionSource(); + this.ToggleVisualState(this.Target, VisualStateToggleMethod.ByOpacity, false); + this.Target.LayoutUpdated += this.TargetControl_LayoutUpdated; + this.Target.InvalidateArrange(); + this.Target.UpdateLayout(); + _ = await this._updateTargetLayoutTaskSource.Task; + this._updateTargetLayoutTaskSource = null; + } + + private void TargetControl_LayoutUpdated(object sender, object e) + { + this.Target.LayoutUpdated -= this.TargetControl_LayoutUpdated; + this.ToggleVisualState(this.Target, VisualStateToggleMethod.ByOpacity, true); + this._needUpdateTargetLayout = false; + _ = this._updateTargetLayoutTaskSource.TrySetResult(null); + } + + private async Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan duration, AnimationConfig config, CancellationToken token) + { + var animationTasks = new List(); + foreach (var animation in config.Animations) + { + var sourceBuilder = AnimationBuilder.Create(); + var targetBuilder = AnimationBuilder.Create(); + switch (animation) + { + case AnimationTarget.Translation: + this.AnimateUIElementsTranslation(sourceBuilder, targetBuilder, source, target, duration); + break; + case AnimationTarget.Scale: + this.AnimateUIElementsScale(sourceBuilder, targetBuilder, source, target, duration); + break; + case AnimationTarget.ScaleX: + this.AnimateUIElementsScaleX(sourceBuilder, targetBuilder, source, target, duration); + break; + case AnimationTarget.ScaleY: + this.AnimateUIElementsScaleY(sourceBuilder, targetBuilder, source, target, duration); + break; + case AnimationTarget.Opacity: + this.AnimateUIElementsOpacity(sourceBuilder, targetBuilder, duration * 1 / 3); // Make opacity animation faster + break; + default: + break; + } + + animationTasks.Add(sourceBuilder.StartAsync(source, token)); + animationTasks.Add(targetBuilder.StartAsync(target, token)); + } + + await Task.WhenAll(animationTasks); + } + + private async Task AnimateIgnoredElementsAsync(UIElement parent, bool isShow, CancellationToken token) + { + if (parent == null) + { + return; + } + + var animationTasks = new List(); + var elements = GetIgnoredElements(parent); + var duration = isShow ? this.IgnoredElementShowDuration : this.IgnoredElementHideDuration; + var delay = isShow ? this.IgnoredElementShowDelayDuration : TimeSpan.Zero; + if (this._isInterruptedAnimation) + { + duration *= this.interruptedAnimationReverseDurationRatio; + delay *= this.interruptedAnimationReverseDurationRatio; + } + + foreach (var item in elements) + { + if (this.IgnoredElementHideTranslation != Vector3.Zero) + { + animationTasks.Add(AnimationBuilder.Create().Translation( + to: isShow ? Vector3.Zero : this.IgnoredElementHideTranslation, + duration: duration, + delay: delay).StartAsync(item, token)); + } + + animationTasks.Add(AnimationBuilder.Create().Opacity( + to: isShow ? 1 : 0, + duration: duration, + delay: delay).StartAsync(item, token)); + + if (isShow) + { + delay += this.IgnoredElementShowStepDuration; + } + } + + await Task.WhenAll(animationTasks); + } + + private void AnimateUIElementsTranslation(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, UIElement target, TimeSpan duration) + { + if (this._isInterruptedAnimation) + { + _ = sourceBuilder.Translation(to: Vector3.Zero, duration: TimeSpan.FromMilliseconds(1)); + _ = targetBuilder.Translation(to: Vector3.Zero, duration: duration); + return; + } + + var diff = target.TransformToVisual(source).TransformPoint(default); + _ = sourceBuilder.Translation().TimedKeyFrames( + build: b => b + .KeyFrame(duration - TimeSpan.FromMilliseconds(1), new Vector3((float)diff.X, (float)diff.Y, 0)) + .KeyFrame(duration, Vector3.Zero)); + _ = targetBuilder.Translation().TimedKeyFrames( + delayBehavior: AnimationDelayBehavior.SetInitialValueBeforeDelay, + build: b => b + .KeyFrame(TimeSpan.Zero, new Vector3((float)-diff.X, (float)-diff.Y, 0)) + .KeyFrame(duration - TimeSpan.FromMilliseconds(1), Vector3.Zero) + .KeyFrame(duration, Vector3.Zero)); + } + + private void AnimateUIElementsScale(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, UIElement target, TimeSpan duration) + { + var scaleX = target.ActualSize.X / source.ActualSize.X; + var scaleY = target.ActualSize.Y / source.ActualSize.Y; + var scale = new Vector3((float)scaleX, (float)scaleY, 1); + this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration); + } + + private void AnimateUIElementsScaleX(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, UIElement target, TimeSpan duration) + { + var scaleX = target.ActualSize.X / source.ActualSize.X; + var scale = new Vector3((float)scaleX, (float)scaleX, 1); + this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration); + } + + private void AnimateUIElementsScaleY(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, UIElement target, TimeSpan duration) + { + var scaleY = target.ActualSize.Y / source.ActualSize.Y; + var scale = new Vector3((float)scaleY, (float)scaleY, 1); + this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration); + } + + private void AnimateUIElementsScale(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, Vector3 targetScale, TimeSpan duration) + { + _ = sourceBuilder.Scale(to: targetScale, duration: duration); + _ = targetBuilder.Scale(to: Vector3.One, duration: duration); + } + + private void AnimateUIElementsOpacity(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, TimeSpan duration) + { + if (this._isInterruptedAnimation) + { + _ = sourceBuilder.Opacity(to: 0, duration: TimeSpan.FromMilliseconds(1)); + _ = targetBuilder.Opacity(to: 1, duration: TimeSpan.FromMilliseconds(1)); + return; + } + + _ = sourceBuilder.Opacity(to: 0, duration: duration); + _ = targetBuilder.Opacity(to: 1, duration: duration); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs new file mode 100644 index 00000000000..3b4df5ff703 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Numerics; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// A animation helper that morphs between two controls. + /// + public sealed partial class TransitionHelper + { + /// + /// Gets or sets the source control. + /// + public FrameworkElement Source + { + get + { + return this._source; + } + + set + { + this._source = value; + this.UpdateSourceAnimatedElements(); + } + } + + /// + /// Gets or sets the target control. + /// + public FrameworkElement Target + { + get + { + return this._target; + } + + set + { + this._target = value; + this._needUpdateTargetLayout = true; + this.UpdateTargetAnimatedElements(); + } + } + + /// + /// Gets a value indicating whether the source control has been morphed to the target control. + /// + public bool IsTargetState { get; private set; } = false; + + /// + /// Gets or sets the method of changing the visibility of the source control. + /// + public VisualStateToggleMethod SourceToggleMethod { get; set; } = VisualStateToggleMethod.ByVisibility; + + /// + /// Gets or sets the method of changing the visibility of the target control. + /// + public VisualStateToggleMethod TargetToggleMethod { get; set; } = VisualStateToggleMethod.ByVisibility; + + /// + /// Gets or sets the duration of the connected animation between two UI elements. + /// + public TimeSpan AnimationDuration { get; set; } = TimeSpan.FromMilliseconds(600); + + /// + /// Gets or sets the duration of the show animation of ignored UI elements. + /// + public TimeSpan IgnoredElementShowDuration { get; set; } = TimeSpan.FromMilliseconds(200); + + /// + /// Gets or sets the delay of the show animation of ignored UI elements. + /// + public TimeSpan IgnoredElementShowDelayDuration { get; set; } = TimeSpan.FromMilliseconds(300); + + /// + /// Gets or sets the duration of the interval between the show animations of ignored UI elements. + /// + public TimeSpan IgnoredElementShowStepDuration { get; set; } = TimeSpan.FromMilliseconds(50); + + /// + /// Gets or sets the duration of the hide animation of ignored UI elements. + /// + public TimeSpan IgnoredElementHideDuration { get; set; } = TimeSpan.FromMilliseconds(100); + + /// + /// Gets or sets the translation of the hide animation of ignored UI elements. + /// + public Vector3 IgnoredElementHideTranslation { get; set; } = new Vector3(0, 20, 0); + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs new file mode 100644 index 00000000000..b053e7135d7 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// A animation helper that morphs between two controls. + /// + public sealed partial class TransitionHelper + { + private readonly Dictionary sourceAnimatedElements = new(); + private readonly Dictionary targetAnimatedElements = new(); + private readonly IEnumerable animationConfigs; + private readonly double interruptedAnimationReverseDurationRatio = 0.7; + + private CancellationTokenSource _animateCancellationTokenSource; + private CancellationTokenSource _reverseCancellationTokenSource; + private TaskCompletionSource _animateTaskSource; + private TaskCompletionSource _reverseTaskSource; + private TaskCompletionSource _updateTargetLayoutTaskSource; + private FrameworkElement _source; + private FrameworkElement _target; + private bool _needUpdateTargetLayout = false; + private bool _isInterruptedAnimation = false; + + /// + /// Initializes a new instance of the class. + /// + /// A collection of animation configurations of UI elements that need to be connected by animation + public TransitionHelper(AnimationConfig[] configs) + { + this.animationConfigs = configs; + } + + /// + /// Morphs from source control to target control. + /// + /// A that completes when all animations have completed. + public async Task AnimateAsync(bool forceUpdateAnimatedElements = false) + { + if (this._animateCancellationTokenSource != null) + { + return; + } + + this._animateCancellationTokenSource = new CancellationTokenSource(); + + if (this._reverseCancellationTokenSource != null) + { + if (!this._isInterruptedAnimation) + { + this._reverseCancellationTokenSource?.Cancel(); + this._isInterruptedAnimation = true; + } + else + { + this._isInterruptedAnimation = false; + } + + _ = await this._reverseTaskSource.Task; + this._reverseTaskSource = null; + } + + if (!this.IsTargetState && !this._animateCancellationTokenSource.IsCancellationRequested) + { + this._animateTaskSource = new TaskCompletionSource(); + await this.InitStateAsync(forceUpdateAnimatedElements); + await this.AnimateFromSourceToTargetAsync(this._animateCancellationTokenSource.Token); + this.RestoreState(true); + _ = this._animateTaskSource?.TrySetResult(null); + } + + this.IsTargetState = true; + if (!this._animateCancellationTokenSource.IsCancellationRequested) + { + this._isInterruptedAnimation = false; + } + + this._animateCancellationTokenSource = null; + } + + /// + /// Reverse animation, morphs from target control to source control. + /// + /// A that completes when all animations have completed. + public async Task ReverseAsync(bool forceUpdateAnimatedElements = false) + { + if (this._reverseCancellationTokenSource != null) + { + return; + } + + this._reverseCancellationTokenSource = new CancellationTokenSource(); + + if (this._animateCancellationTokenSource != null) + { + if (!this._isInterruptedAnimation) + { + this._animateCancellationTokenSource?.Cancel(); + this._isInterruptedAnimation = true; + } + else + { + this._isInterruptedAnimation = false; + } + + _ = await this._animateTaskSource.Task; + this._animateTaskSource = null; + } + + if (this.IsTargetState && !this._reverseCancellationTokenSource.IsCancellationRequested) + { + this._reverseTaskSource = new TaskCompletionSource(); + await this.InitStateAsync(forceUpdateAnimatedElements); + await this.AnimateFromTargetToSourceAsync(this._reverseCancellationTokenSource.Token); + this.RestoreState(false); + _ = this._reverseTaskSource?.TrySetResult(null); + } + + this.IsTargetState = false; + if (!this._reverseCancellationTokenSource.IsCancellationRequested) + { + this._isInterruptedAnimation = false; + } + + this._reverseCancellationTokenSource = null; + } + } +} From b890cad82fd1bacd7c97d91a3fc6cce31437e5ce Mon Sep 17 00:00:00 2001 From: hhchaos Date: Fri, 22 Oct 2021 21:06:06 +0800 Subject: [PATCH 02/94] Add sample for TransitionHelper. --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 10 + .../TransitionHelper/TransitionHelper.png | Bin 0 -> 1246 bytes .../TransitionHelperCode.bind | 27 +++ .../TransitionHelperPage.xaml | 10 + .../TransitionHelperPage.xaml.cs | 144 +++++++++++++++ .../TransitionHelperXaml.bind | 172 ++++++++++++++++++ .../SamplePages/samples.json | 11 ++ .../Helpers/TransitionHelper.Logic.cs | 8 +- .../Helpers/TransitionHelper.Properties.cs | 7 + .../Helpers/TransitionHelper.cs | 10 - 10 files changed, 385 insertions(+), 14 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelper.png create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 0a9bf08f523..8abf8245a98 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -297,6 +297,7 @@ --> + @@ -562,6 +563,9 @@ TokenizingTextBoxPage.xaml + + TransitionHelperPage.xaml + FullScreenModeStateTriggerPage.xaml @@ -644,6 +648,8 @@ + + @@ -1108,6 +1114,10 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + MSBuild:Compile Designer diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelper.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelper.png new file mode 100644 index 0000000000000000000000000000000000000000..9b4dbf5609bc368836c22b837f0c44f8bb8c77a9 GIT binary patch literal 1246 zcmeAS@N?(olHy`uVBq!ia0y~yU{nCI4{)#n$t4^1Z39v)#ZI0f96(URk5@OP1(cIhgzE=g-VA>$%J5o9 zVS!sigA^lE77ND;!C}dnHTfIUjV0?R*X%C;<35Rh>DCV&oC3ywW<0ri?_>Ge)(<-* z4@$gs{U<-aTjl?>YloE89eTd~J@_$yYUl?Q4uQpg3kv?Er;6HeG9B?arvJ{RLE_|( zqjNa~lqL7?5N1pgc`RP8=FpS$=g~QqjNIp*e(bf^YLBhId{-)B3d7phsgt&Q)}1}9 zTr&UT>yq+!+jno>puiY)`Ls%Y+5Y{ffa2FbzBaO(ez8heFlrLR+H`xn#XUQIsXB2B zE!bM4X(#Sk=lSX7y!n+t?U&!p5odqjeIbb@L)Q4w3Xg{+(x%w(1AjK9@&g3PJz z6P%xN@1DM3R0l)u>5Y#jpQ-)Q#-Z!fu05cVya0i^eU1%c zpC@qi%IrVVuEMlsk-qB-v$Xp)8g{@SF@5X)Vq5jmG`lM28nLfB^Zy&(IsVOQg@yko z|Gd8Q-758OW#d(TRv!52Ke=Y|lfMn;AHB4+&RpNH>h`~f=WD*S{m?nXaV+=Do!>_f zv2@Hfk>7jp7^i^w%-XNrtw7qa&ajuEQ1-ESFsHzEAZIF!6At9el@{E2>a&XdTiO42 zqd0D>I>bCZ{EmH@VH2Y%6U!A2fdFL(hhB!^$|>GdZMzy+YFw(HW4)EnQF~4%u)t#Q MboFyt=akR{06O*Bi~s-t literal 0 HcmV?d00001 diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind new file mode 100644 index 00000000000..098000079a4 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind @@ -0,0 +1,27 @@ +//create a TransitionHelper. +var TransitionHelper transitionHelper = new TransitionHelper(); + +//Configure TransitionHelper. +transitionHelper.AnimationConfigs = new AnimationConfig[]{ + new AnimationConfig + { + Id = "background" + }, + new AnimationConfig + { + Id = "image", + AdditionalAnimations = new AnimationTarget[]{ AnimationTarget.Scale } + }, + new AnimationConfig + { + Id = "guide" + }, +}; +transitionHelper.Source = FirstControl; +transitionHelper.Target = SecondControl; + +//Animate. +await transitionHelper.AnimateAsync() + +//Reverse. +await transitionHelper.ReverseAsync(); \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml new file mode 100644 index 00000000000..4f9b1738cd4 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs new file mode 100644 index 00000000000..1a1c9efbe96 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs @@ -0,0 +1,144 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using Microsoft.Toolkit.Uwp.UI; +using Microsoft.Toolkit.Uwp.UI.Animations; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; + +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + /// + /// A page that shows how to use the helper. + /// + public sealed partial class TransitionHelperPage : Page, IXamlRenderListener + { + private readonly TransitionHelper _transitionHelper = new(); + private FrameworkElement _firstControl; + private FrameworkElement _secondControl; + private FrameworkElement _thirdControl; + private FrameworkElement _secondNameTextBlock; + private FrameworkElement _secondDescTextBlock; + private FrameworkElement _thirdNameTextBlock; + private FrameworkElement _thirdDescTextBlock; + private Button _secondButton; + private Button _secondBackButton; + private Button _thirdButton; + + /// + /// Initializes a new instance of the class. + /// + public TransitionHelperPage() + { + this.InitializeComponent(); + } + + public void OnXamlRendered(FrameworkElement control) + { + this._firstControl = control.FindChild("FirstControl"); + this._secondControl = control.FindChild("SecondControl"); + this._thirdControl = control.FindChild("ThirdControl"); + this._secondNameTextBlock = control.FindChild("SecondNameTextBlock"); + this._secondDescTextBlock = control.FindChild("SecondDescTextBlock"); + this._thirdNameTextBlock = control.FindChild("ThirdNameTextBlock"); + this._thirdDescTextBlock = control.FindChild("ThirdDescTextBlock"); + this._secondButton = control.FindChild("SecondButton") as Button; + this._secondBackButton = control.FindChild("SecondBackButton") as Button; + this._thirdButton = control.FindChild("ThirdButton") as Button; + this._firstControl.Tapped += FirstControl_Tapped; + this._secondBackButton.Click += SecondBackButton_Click; + this._secondButton.Click += SecondButton_Click; + this._thirdButton.Click += ThirdButton_Click; + } + + private void FirstControl_Tapped(object sender, TappedRoutedEventArgs e) + { + _transitionHelper.AnimationConfigs = new AnimationConfig[]{ + new AnimationConfig + { + Id = "background" + }, + new AnimationConfig + { + Id = "image", + AdditionalAnimations = new AnimationTarget[]{ AnimationTarget.Scale } + }, + new AnimationConfig + { + Id = "guide" + }, + }; + _transitionHelper.Source = _firstControl; + _transitionHelper.Target = _secondControl; + _transitionHelper.IgnoredElementHideTranslation = new Vector3(20, 0, 0); + TransitionHelper.SetIsIgnored(_secondNameTextBlock, true); + TransitionHelper.SetIsIgnored(_secondDescTextBlock, true); + _ = _transitionHelper.AnimateAsync(); + } + + private void SecondBackButton_Click(object sender, RoutedEventArgs e) + { + _ = _transitionHelper.ReverseAsync(); + } + + private void SecondButton_Click(object sender, RoutedEventArgs e) + { + _transitionHelper.AnimationConfigs = new AnimationConfig[]{ + new AnimationConfig + { + Id = "background" + }, + new AnimationConfig + { + Id = "image", + AdditionalAnimations = new AnimationTarget[]{ AnimationTarget.Scale } + }, + new AnimationConfig + { + Id = "guide" + }, + new AnimationConfig + { + Id = "name", + }, + new AnimationConfig + { + Id = "desc", + }, + }; + _transitionHelper.Source = _secondControl; + _transitionHelper.Target = _thirdControl; + _transitionHelper.IgnoredElementHideTranslation = new Vector3(0, 20, 0); + TransitionHelper.SetIsIgnored(_secondNameTextBlock, false); + TransitionHelper.SetIsIgnored(_secondDescTextBlock, false); + _ = _transitionHelper.AnimateAsync(); + } + + private void ThirdButton_Click(object sender, RoutedEventArgs e) + { + _transitionHelper.AnimationConfigs = new AnimationConfig[]{ + new AnimationConfig + { + Id = "background" + }, + new AnimationConfig + { + Id = "image", + AdditionalAnimations = new AnimationTarget[]{ AnimationTarget.Scale } + }, + new AnimationConfig + { + Id = "guide" + } + }; + _transitionHelper.Source = _thirdControl; + _transitionHelper.Target = _firstControl; + TransitionHelper.SetIsIgnored(_thirdNameTextBlock, true); + TransitionHelper.SetIsIgnored(_thirdDescTextBlock, true); + _ = _transitionHelper.AnimateAsync(); + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind new file mode 100644 index 00000000000..c8753bf6f87 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -0,0 +1,172 @@ + + + + + + + + + + + + + ⬅ Click Here + + + + + + + + + + + + Magic + Magic is a cute cat. + + + + + + + + + + + + + + + + + + Magic + + + Magic is a cute cat, but sometimes very naughty. + + + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 8cdfcc3a619..392a588e2b2 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -671,6 +671,17 @@ "XamlCodeFile": "/SamplePages/Animations/Shadows/AnimatedCardShadowXaml.bind", "Icon": "/SamplePages/Shadows/DropShadowPanel.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/helpers/AttachedShadows.md" + }, + { + "Name": "TransitionHelper", + "Type": "TransitionHelperPage", + "Subcategory": "Helpers", + "About": "A animation helper that morphs between two controls.", + "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Animations/Helpers", + "XamlCodeFile": "TransitionHelperXaml.bind", + "CodeFile": "TransitionHelperCode.bind", + "Icon": "/SamplePages/TransitionHelper/TransitionHelper.png", + "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/animations/TransitionHelper.md" } ] }, diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index bf8409aecc3..906fc87c3be 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -22,7 +22,7 @@ public sealed partial class TransitionHelper private void UpdateSourceAnimatedElements() { this.sourceAnimatedElements.Clear(); - var filters = this.animationConfigs.Select(config => config.Id); + var filters = this.AnimationConfigs.Select(config => config.Id); foreach (var item in GetAnimatedElements(this.Source, filters)) { @@ -33,7 +33,7 @@ private void UpdateSourceAnimatedElements() private void UpdateTargetAnimatedElements() { this.targetAnimatedElements.Clear(); - var filters = this.animationConfigs.Select(config => config.Id); + var filters = this.AnimationConfigs.Select(config => config.Id); foreach (var item in GetAnimatedElements(this.Target, filters)) { @@ -79,7 +79,7 @@ private async Task AnimateFromSourceToTargetAsync(CancellationToken token) } var animationTasks = new List(); - foreach (var item in this.animationConfigs) + foreach (var item in this.AnimationConfigs) { (var source, var target) = this.GetPairElements(item); if (source == null || target == null) @@ -105,7 +105,7 @@ private async Task AnimateFromTargetToSourceAsync(CancellationToken token) } var animationTasks = new List(); - foreach (var item in this.animationConfigs) + foreach (var item in this.AnimationConfigs) { (var target, var source) = this.GetPairElements(item); if (source == null || target == null) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 3b4df5ff703..37925fa5100 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Numerics; using Windows.UI.Xaml; @@ -26,6 +27,7 @@ public FrameworkElement Source set { this._source = value; + this.IsTargetState = false; this.UpdateSourceAnimatedElements(); } } @@ -48,6 +50,11 @@ public FrameworkElement Target } } + /// + /// Gets or sets the collection of animation configurations of UI elements that need to be connected by animation. + /// + public IEnumerable AnimationConfigs { get; set; } + /// /// Gets a value indicating whether the source control has been morphed to the target control. /// diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index b053e7135d7..cae74e4cd90 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -16,7 +16,6 @@ public sealed partial class TransitionHelper { private readonly Dictionary sourceAnimatedElements = new(); private readonly Dictionary targetAnimatedElements = new(); - private readonly IEnumerable animationConfigs; private readonly double interruptedAnimationReverseDurationRatio = 0.7; private CancellationTokenSource _animateCancellationTokenSource; @@ -29,15 +28,6 @@ public sealed partial class TransitionHelper private bool _needUpdateTargetLayout = false; private bool _isInterruptedAnimation = false; - /// - /// Initializes a new instance of the class. - /// - /// A collection of animation configurations of UI elements that need to be connected by animation - public TransitionHelper(AnimationConfig[] configs) - { - this.animationConfigs = configs; - } - /// /// Morphs from source control to target control. /// From da04e1d92599bc693e19e750e9b124a5455f2da0 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Fri, 22 Oct 2021 21:33:48 +0800 Subject: [PATCH 03/94] Fix format --- .../TransitionHelper/TransitionHelperPage.xaml.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs index 1a1c9efbe96..b8e61ae82a1 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs @@ -56,7 +56,8 @@ public void OnXamlRendered(FrameworkElement control) private void FirstControl_Tapped(object sender, TappedRoutedEventArgs e) { - _transitionHelper.AnimationConfigs = new AnimationConfig[]{ + _transitionHelper.AnimationConfigs = new AnimationConfig[] + { new AnimationConfig { Id = "background" @@ -64,7 +65,7 @@ private void FirstControl_Tapped(object sender, TappedRoutedEventArgs e) new AnimationConfig { Id = "image", - AdditionalAnimations = new AnimationTarget[]{ AnimationTarget.Scale } + AdditionalAnimations = new AnimationTarget[] { AnimationTarget.Scale } }, new AnimationConfig { @@ -86,7 +87,8 @@ private void SecondBackButton_Click(object sender, RoutedEventArgs e) private void SecondButton_Click(object sender, RoutedEventArgs e) { - _transitionHelper.AnimationConfigs = new AnimationConfig[]{ + _transitionHelper.AnimationConfigs = new AnimationConfig[] + { new AnimationConfig { Id = "background" @@ -94,7 +96,7 @@ private void SecondButton_Click(object sender, RoutedEventArgs e) new AnimationConfig { Id = "image", - AdditionalAnimations = new AnimationTarget[]{ AnimationTarget.Scale } + AdditionalAnimations = new AnimationTarget[] { AnimationTarget.Scale } }, new AnimationConfig { @@ -119,7 +121,8 @@ private void SecondButton_Click(object sender, RoutedEventArgs e) private void ThirdButton_Click(object sender, RoutedEventArgs e) { - _transitionHelper.AnimationConfigs = new AnimationConfig[]{ + _transitionHelper.AnimationConfigs = new AnimationConfig[] + { new AnimationConfig { Id = "background" @@ -127,7 +130,7 @@ private void ThirdButton_Click(object sender, RoutedEventArgs e) new AnimationConfig { Id = "image", - AdditionalAnimations = new AnimationTarget[]{ AnimationTarget.Scale } + AdditionalAnimations = new AnimationTarget[] { AnimationTarget.Scale } }, new AnimationConfig { From c6292e60c3071009a6ea040c6513388a1a91f71d Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 25 Oct 2021 10:03:24 +0800 Subject: [PATCH 04/94] Fix sample format --- .../TransitionHelperPage.xaml.cs | 133 ++++++++++-------- .../TransitionHelperXaml.bind | 2 +- 2 files changed, 78 insertions(+), 57 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs index b8e61ae82a1..bfd5e77286a 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs @@ -16,17 +16,17 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages /// public sealed partial class TransitionHelperPage : Page, IXamlRenderListener { - private readonly TransitionHelper _transitionHelper = new(); - private FrameworkElement _firstControl; - private FrameworkElement _secondControl; - private FrameworkElement _thirdControl; - private FrameworkElement _secondNameTextBlock; - private FrameworkElement _secondDescTextBlock; - private FrameworkElement _thirdNameTextBlock; - private FrameworkElement _thirdDescTextBlock; - private Button _secondButton; - private Button _secondBackButton; - private Button _thirdButton; + private readonly TransitionHelper transitionHelper = new (); + private FrameworkElement firstControl; + private FrameworkElement secondControl; + private FrameworkElement thirdControl; + private FrameworkElement secondNameTextBlock; + private FrameworkElement secondDescTextBlock; + private FrameworkElement thirdNameTextBlock; + private FrameworkElement thirdDescTextBlock; + private Button secondButton; + private Button secondBackButton; + private Button thirdButton; /// /// Initializes a new instance of the class. @@ -38,69 +38,83 @@ public TransitionHelperPage() public void OnXamlRendered(FrameworkElement control) { - this._firstControl = control.FindChild("FirstControl"); - this._secondControl = control.FindChild("SecondControl"); - this._thirdControl = control.FindChild("ThirdControl"); - this._secondNameTextBlock = control.FindChild("SecondNameTextBlock"); - this._secondDescTextBlock = control.FindChild("SecondDescTextBlock"); - this._thirdNameTextBlock = control.FindChild("ThirdNameTextBlock"); - this._thirdDescTextBlock = control.FindChild("ThirdDescTextBlock"); - this._secondButton = control.FindChild("SecondButton") as Button; - this._secondBackButton = control.FindChild("SecondBackButton") as Button; - this._thirdButton = control.FindChild("ThirdButton") as Button; - this._firstControl.Tapped += FirstControl_Tapped; - this._secondBackButton.Click += SecondBackButton_Click; - this._secondButton.Click += SecondButton_Click; - this._thirdButton.Click += ThirdButton_Click; + this.firstControl = control.FindChild("FirstControl"); + this.secondControl = control.FindChild("SecondControl"); + this.thirdControl = control.FindChild("ThirdControl"); + this.secondNameTextBlock = control.FindChild("SecondNameTextBlock"); + this.secondDescTextBlock = control.FindChild("SecondDescTextBlock"); + this.thirdNameTextBlock = control.FindChild("ThirdNameTextBlock"); + this.thirdDescTextBlock = control.FindChild("ThirdDescTextBlock"); + this.secondButton = control.FindChild("SecondButton") as Button; + this.secondBackButton = control.FindChild("SecondBackButton") as Button; + this.thirdButton = control.FindChild("ThirdButton") as Button; + this.firstControl.Tapped += this.FirstControl_Tapped; + this.secondBackButton.Click += this.SecondBackButton_Click; + this.secondButton.Click += this.SecondButton_Click; + this.thirdButton.Click += this.ThirdButton_Click; } private void FirstControl_Tapped(object sender, TappedRoutedEventArgs e) { - _transitionHelper.AnimationConfigs = new AnimationConfig[] + this.transitionHelper.AnimationConfigs = new AnimationConfig[] { new AnimationConfig { - Id = "background" + Id = "background", + AdditionalAnimations = new AnimationTarget[] + { + AnimationTarget.Scale, + }, }, new AnimationConfig { Id = "image", - AdditionalAnimations = new AnimationTarget[] { AnimationTarget.Scale } + AdditionalAnimations = new AnimationTarget[] + { + AnimationTarget.Scale, + }, }, new AnimationConfig { - Id = "guide" + Id = "guide", }, }; - _transitionHelper.Source = _firstControl; - _transitionHelper.Target = _secondControl; - _transitionHelper.IgnoredElementHideTranslation = new Vector3(20, 0, 0); - TransitionHelper.SetIsIgnored(_secondNameTextBlock, true); - TransitionHelper.SetIsIgnored(_secondDescTextBlock, true); - _ = _transitionHelper.AnimateAsync(); + this.transitionHelper.Source = this.firstControl; + this.transitionHelper.Target = this.secondControl; + this.transitionHelper.IgnoredElementHideTranslation = new Vector3(20, 0, 0); + TransitionHelper.SetIsIgnored(this.secondNameTextBlock, true); + TransitionHelper.SetIsIgnored(this.secondDescTextBlock, true); + _ = this.transitionHelper.AnimateAsync(); } private void SecondBackButton_Click(object sender, RoutedEventArgs e) { - _ = _transitionHelper.ReverseAsync(); + _ = this.transitionHelper.ReverseAsync(); } private void SecondButton_Click(object sender, RoutedEventArgs e) { - _transitionHelper.AnimationConfigs = new AnimationConfig[] + this.transitionHelper.AnimationConfigs = new AnimationConfig[] { new AnimationConfig { - Id = "background" + Id = "background", + AdditionalAnimations = new AnimationTarget[] + { + AnimationTarget.Scale, + }, }, new AnimationConfig { Id = "image", - AdditionalAnimations = new AnimationTarget[] { AnimationTarget.Scale } + AdditionalAnimations = new AnimationTarget[] + { + AnimationTarget.Scale, + }, }, new AnimationConfig { - Id = "guide" + Id = "guide", }, new AnimationConfig { @@ -111,37 +125,44 @@ private void SecondButton_Click(object sender, RoutedEventArgs e) Id = "desc", }, }; - _transitionHelper.Source = _secondControl; - _transitionHelper.Target = _thirdControl; - _transitionHelper.IgnoredElementHideTranslation = new Vector3(0, 20, 0); - TransitionHelper.SetIsIgnored(_secondNameTextBlock, false); - TransitionHelper.SetIsIgnored(_secondDescTextBlock, false); - _ = _transitionHelper.AnimateAsync(); + this.transitionHelper.Source = this.secondControl; + this.transitionHelper.Target = this.thirdControl; + this.transitionHelper.IgnoredElementHideTranslation = new Vector3(0, 20, 0); + TransitionHelper.SetIsIgnored(this.secondNameTextBlock, false); + TransitionHelper.SetIsIgnored(this.secondDescTextBlock, false); + _ = this.transitionHelper.AnimateAsync(); } private void ThirdButton_Click(object sender, RoutedEventArgs e) { - _transitionHelper.AnimationConfigs = new AnimationConfig[] + this.transitionHelper.AnimationConfigs = new AnimationConfig[] { new AnimationConfig { - Id = "background" + Id = "background", + AdditionalAnimations = new AnimationTarget[] + { + AnimationTarget.Scale, + }, }, new AnimationConfig { Id = "image", - AdditionalAnimations = new AnimationTarget[] { AnimationTarget.Scale } + AdditionalAnimations = new AnimationTarget[] + { + AnimationTarget.Scale, + }, }, new AnimationConfig { - Id = "guide" - } + Id = "guide", + }, }; - _transitionHelper.Source = _thirdControl; - _transitionHelper.Target = _firstControl; - TransitionHelper.SetIsIgnored(_thirdNameTextBlock, true); - TransitionHelper.SetIsIgnored(_thirdDescTextBlock, true); - _ = _transitionHelper.AnimateAsync(); + this.transitionHelper.Source = this.thirdControl; + this.transitionHelper.Target = this.firstControl; + TransitionHelper.SetIsIgnored(this.thirdNameTextBlock, true); + TransitionHelper.SetIsIgnored(this.thirdDescTextBlock, true); + _ = this.transitionHelper.AnimateAsync(); } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index c8753bf6f87..f2e1fa2bf53 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -39,7 +39,7 @@ From 0bd22c13b4b2b325964fd3d5ec08d58dab8fa273 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 25 Oct 2021 10:28:56 +0800 Subject: [PATCH 05/94] Add unpaired elements animation --- .../TransitionHelperPage.xaml.cs | 117 +++++------------- .../Helpers/TransitionHelper.Logic.cs | 57 ++++++--- .../Helpers/TransitionHelper.Properties.cs | 20 +-- 3 files changed, 79 insertions(+), 115 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs index bfd5e77286a..0aab97ebaa5 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs @@ -20,10 +20,6 @@ public sealed partial class TransitionHelperPage : Page, IXamlRenderListener private FrameworkElement firstControl; private FrameworkElement secondControl; private FrameworkElement thirdControl; - private FrameworkElement secondNameTextBlock; - private FrameworkElement secondDescTextBlock; - private FrameworkElement thirdNameTextBlock; - private FrameworkElement thirdDescTextBlock; private Button secondButton; private Button secondBackButton; private Button thirdButton; @@ -34,28 +30,6 @@ public sealed partial class TransitionHelperPage : Page, IXamlRenderListener public TransitionHelperPage() { this.InitializeComponent(); - } - - public void OnXamlRendered(FrameworkElement control) - { - this.firstControl = control.FindChild("FirstControl"); - this.secondControl = control.FindChild("SecondControl"); - this.thirdControl = control.FindChild("ThirdControl"); - this.secondNameTextBlock = control.FindChild("SecondNameTextBlock"); - this.secondDescTextBlock = control.FindChild("SecondDescTextBlock"); - this.thirdNameTextBlock = control.FindChild("ThirdNameTextBlock"); - this.thirdDescTextBlock = control.FindChild("ThirdDescTextBlock"); - this.secondButton = control.FindChild("SecondButton") as Button; - this.secondBackButton = control.FindChild("SecondBackButton") as Button; - this.thirdButton = control.FindChild("ThirdButton") as Button; - this.firstControl.Tapped += this.FirstControl_Tapped; - this.secondBackButton.Click += this.SecondBackButton_Click; - this.secondButton.Click += this.SecondButton_Click; - this.thirdButton.Click += this.ThirdButton_Click; - } - - private void FirstControl_Tapped(object sender, TappedRoutedEventArgs e) - { this.transitionHelper.AnimationConfigs = new AnimationConfig[] { new AnimationConfig @@ -78,12 +52,37 @@ private void FirstControl_Tapped(object sender, TappedRoutedEventArgs e) { Id = "guide", }, + new AnimationConfig + { + Id = "name", + }, + new AnimationConfig + { + Id = "desc", + }, }; + } + + /// + public void OnXamlRendered(FrameworkElement control) + { + this.firstControl = control.FindChild("FirstControl"); + this.secondControl = control.FindChild("SecondControl"); + this.thirdControl = control.FindChild("ThirdControl"); + this.secondButton = control.FindChild("SecondButton") as Button; + this.secondBackButton = control.FindChild("SecondBackButton") as Button; + this.thirdButton = control.FindChild("ThirdButton") as Button; + this.firstControl.Tapped += this.FirstControl_Tapped; + this.secondBackButton.Click += this.SecondBackButton_Click; + this.secondButton.Click += this.SecondButton_Click; + this.thirdButton.Click += this.ThirdButton_Click; + } + + private void FirstControl_Tapped(object sender, TappedRoutedEventArgs e) + { this.transitionHelper.Source = this.firstControl; this.transitionHelper.Target = this.secondControl; - this.transitionHelper.IgnoredElementHideTranslation = new Vector3(20, 0, 0); - TransitionHelper.SetIsIgnored(this.secondNameTextBlock, true); - TransitionHelper.SetIsIgnored(this.secondDescTextBlock, true); + this.transitionHelper.IgnoredAndUnpairedElementHideTranslation = new Vector3(20, 0, 0); _ = this.transitionHelper.AnimateAsync(); } @@ -94,74 +93,16 @@ private void SecondBackButton_Click(object sender, RoutedEventArgs e) private void SecondButton_Click(object sender, RoutedEventArgs e) { - this.transitionHelper.AnimationConfigs = new AnimationConfig[] - { - new AnimationConfig - { - Id = "background", - AdditionalAnimations = new AnimationTarget[] - { - AnimationTarget.Scale, - }, - }, - new AnimationConfig - { - Id = "image", - AdditionalAnimations = new AnimationTarget[] - { - AnimationTarget.Scale, - }, - }, - new AnimationConfig - { - Id = "guide", - }, - new AnimationConfig - { - Id = "name", - }, - new AnimationConfig - { - Id = "desc", - }, - }; this.transitionHelper.Source = this.secondControl; this.transitionHelper.Target = this.thirdControl; - this.transitionHelper.IgnoredElementHideTranslation = new Vector3(0, 20, 0); - TransitionHelper.SetIsIgnored(this.secondNameTextBlock, false); - TransitionHelper.SetIsIgnored(this.secondDescTextBlock, false); + this.transitionHelper.IgnoredAndUnpairedElementHideTranslation = new Vector3(0, 20, 0); _ = this.transitionHelper.AnimateAsync(); } private void ThirdButton_Click(object sender, RoutedEventArgs e) { - this.transitionHelper.AnimationConfigs = new AnimationConfig[] - { - new AnimationConfig - { - Id = "background", - AdditionalAnimations = new AnimationTarget[] - { - AnimationTarget.Scale, - }, - }, - new AnimationConfig - { - Id = "image", - AdditionalAnimations = new AnimationTarget[] - { - AnimationTarget.Scale, - }, - }, - new AnimationConfig - { - Id = "guide", - }, - }; this.transitionHelper.Source = this.thirdControl; this.transitionHelper.Target = this.firstControl; - TransitionHelper.SetIsIgnored(this.thirdNameTextBlock, true); - TransitionHelper.SetIsIgnored(this.thirdDescTextBlock, true); _ = this.transitionHelper.AnimateAsync(); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 906fc87c3be..92da8845d7a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -65,9 +65,8 @@ private void ToggleVisualState(UIElement target, VisualStateToggleMethod method, private (UIElement, UIElement) GetPairElements(AnimationConfig config) { - return this.sourceAnimatedElements.ContainsKey(config.Id) && this.targetAnimatedElements.ContainsKey(config.Id) - ? (this.sourceAnimatedElements[config.Id], this.targetAnimatedElements[config.Id]) - : (null, null); + return (this.sourceAnimatedElements.ContainsKey(config.Id) ? this.sourceAnimatedElements[config.Id] : null, + this.targetAnimatedElements.ContainsKey(config.Id) ? this.targetAnimatedElements[config.Id] : null); } private async Task AnimateFromSourceToTargetAsync(CancellationToken token) @@ -79,19 +78,31 @@ private async Task AnimateFromSourceToTargetAsync(CancellationToken token) } var animationTasks = new List(); + var sourceUnpairedElements = new List(); + var targetUnpairedElements = new List(); foreach (var item in this.AnimationConfigs) { (var source, var target) = this.GetPairElements(item); if (source == null || target == null) { + if (source != null) + { + sourceUnpairedElements.Add(source); + } + + if (target != null) + { + targetUnpairedElements.Add(target); + } + continue; } animationTasks.Add(this.AnimateElementsAsync(source, target, duration, item, token)); } - animationTasks.Add(this.AnimateIgnoredElementsAsync(this.Source, false, token)); - animationTasks.Add(this.AnimateIgnoredElementsAsync(this.Target, true, token)); + animationTasks.Add(this.AnimateIgnoredAndUnpairedElementsAsync(this.Source, sourceUnpairedElements, false, token)); + animationTasks.Add(this.AnimateIgnoredAndUnpairedElementsAsync(this.Target, targetUnpairedElements, true, token)); await Task.WhenAll(animationTasks); } @@ -105,19 +116,31 @@ private async Task AnimateFromTargetToSourceAsync(CancellationToken token) } var animationTasks = new List(); + var sourceUnpairedElements = new List(); + var targetUnpairedElements = new List(); foreach (var item in this.AnimationConfigs) { - (var target, var source) = this.GetPairElements(item); + (var source, var target) = this.GetPairElements(item); if (source == null || target == null) { + if (source != null) + { + sourceUnpairedElements.Add(source); + } + + if (target != null) + { + targetUnpairedElements.Add(target); + } + continue; } - animationTasks.Add(this.AnimateElementsAsync(source: source, target, duration, item, token)); + animationTasks.Add(this.AnimateElementsAsync(target, source, duration, item, token)); } - animationTasks.Add(this.AnimateIgnoredElementsAsync(this.Source, true, token)); - animationTasks.Add(this.AnimateIgnoredElementsAsync(this.Target, false, token)); + animationTasks.Add(this.AnimateIgnoredAndUnpairedElementsAsync(this.Source, sourceUnpairedElements, true, token)); + animationTasks.Add(this.AnimateIgnoredAndUnpairedElementsAsync(this.Target, targetUnpairedElements, false, token)); await Task.WhenAll(animationTasks); } @@ -199,7 +222,7 @@ private async Task AnimateElementsAsync(UIElement source, UIElement target, Time await Task.WhenAll(animationTasks); } - private async Task AnimateIgnoredElementsAsync(UIElement parent, bool isShow, CancellationToken token) + private async Task AnimateIgnoredAndUnpairedElementsAsync(UIElement parent, IEnumerable unpairedElements, bool isShow, CancellationToken token) { if (parent == null) { @@ -207,21 +230,21 @@ private async Task AnimateIgnoredElementsAsync(UIElement parent, bool isShow, Ca } var animationTasks = new List(); - var elements = GetIgnoredElements(parent); - var duration = isShow ? this.IgnoredElementShowDuration : this.IgnoredElementHideDuration; - var delay = isShow ? this.IgnoredElementShowDelayDuration : TimeSpan.Zero; + var ignoredElements = GetIgnoredElements(parent); + var duration = isShow ? this.IgnoredAndUnpairedElementShowDuration : this.IgnoredAndUnpairedElementHideDuration; + var delay = isShow ? this.IgnoredAndUnpairedElementShowDelayDuration : TimeSpan.Zero; if (this._isInterruptedAnimation) { duration *= this.interruptedAnimationReverseDurationRatio; delay *= this.interruptedAnimationReverseDurationRatio; } - foreach (var item in elements) + foreach (var item in ignoredElements.Concat(unpairedElements)) { - if (this.IgnoredElementHideTranslation != Vector3.Zero) + if (this.IgnoredAndUnpairedElementHideTranslation != Vector3.Zero) { animationTasks.Add(AnimationBuilder.Create().Translation( - to: isShow ? Vector3.Zero : this.IgnoredElementHideTranslation, + to: isShow ? Vector3.Zero : this.IgnoredAndUnpairedElementHideTranslation, duration: duration, delay: delay).StartAsync(item, token)); } @@ -233,7 +256,7 @@ private async Task AnimateIgnoredElementsAsync(UIElement parent, bool isShow, Ca if (isShow) { - delay += this.IgnoredElementShowStepDuration; + delay += this.IgnoredAndUnpairedElementShowStepDuration; } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 37925fa5100..94a78bb1e14 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -76,28 +76,28 @@ public FrameworkElement Target public TimeSpan AnimationDuration { get; set; } = TimeSpan.FromMilliseconds(600); /// - /// Gets or sets the duration of the show animation of ignored UI elements. + /// Gets or sets the duration of the show animation for ignored and unpaired UI elements. /// - public TimeSpan IgnoredElementShowDuration { get; set; } = TimeSpan.FromMilliseconds(200); + public TimeSpan IgnoredAndUnpairedElementShowDuration { get; set; } = TimeSpan.FromMilliseconds(200); /// - /// Gets or sets the delay of the show animation of ignored UI elements. + /// Gets or sets the delay of the show animation for ignored and unpaired UI elements. /// - public TimeSpan IgnoredElementShowDelayDuration { get; set; } = TimeSpan.FromMilliseconds(300); + public TimeSpan IgnoredAndUnpairedElementShowDelayDuration { get; set; } = TimeSpan.FromMilliseconds(300); /// - /// Gets or sets the duration of the interval between the show animations of ignored UI elements. + /// Gets or sets the duration of the interval between the show animations for ignored and unpaired UI elements. /// - public TimeSpan IgnoredElementShowStepDuration { get; set; } = TimeSpan.FromMilliseconds(50); + public TimeSpan IgnoredAndUnpairedElementShowStepDuration { get; set; } = TimeSpan.FromMilliseconds(50); /// - /// Gets or sets the duration of the hide animation of ignored UI elements. + /// Gets or sets the duration of the hide animation for ignored and unpaired UI elements. /// - public TimeSpan IgnoredElementHideDuration { get; set; } = TimeSpan.FromMilliseconds(100); + public TimeSpan IgnoredAndUnpairedElementHideDuration { get; set; } = TimeSpan.FromMilliseconds(100); /// - /// Gets or sets the translation of the hide animation of ignored UI elements. + /// Gets or sets the translation of the hide animation for ignored and unpaired UI elements. /// - public Vector3 IgnoredElementHideTranslation { get; set; } = new Vector3(0, 20, 0); + public Vector3 IgnoredAndUnpairedElementHideTranslation { get; set; } = new Vector3(0, 20, 0); } } From 6e405a15eab464431827ed8aaf4d66d490361861 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 25 Oct 2021 10:42:42 +0800 Subject: [PATCH 06/94] Update animated elements when setting AnimationConfigs --- .../Helpers/TransitionHelper.Properties.cs | 19 ++++++++++++++++++- .../Helpers/TransitionHelper.cs | 4 ++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 94a78bb1e14..57b2d650b4d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -14,6 +14,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// public sealed partial class TransitionHelper { + private FrameworkElement _source; + private FrameworkElement _target; + private IEnumerable _animationConfigs; + /// /// Gets or sets the source control. /// @@ -53,7 +57,20 @@ public FrameworkElement Target /// /// Gets or sets the collection of animation configurations of UI elements that need to be connected by animation. /// - public IEnumerable AnimationConfigs { get; set; } + public IEnumerable AnimationConfigs + { + get + { + return this._animationConfigs; + } + + set + { + this._animationConfigs = value; + this.UpdateSourceAnimatedElements(); + this.UpdateTargetAnimatedElements(); + } + } /// /// Gets a value indicating whether the source control has been morphed to the target control. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index cae74e4cd90..a805cc64593 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -23,14 +23,13 @@ public sealed partial class TransitionHelper private TaskCompletionSource _animateTaskSource; private TaskCompletionSource _reverseTaskSource; private TaskCompletionSource _updateTargetLayoutTaskSource; - private FrameworkElement _source; - private FrameworkElement _target; private bool _needUpdateTargetLayout = false; private bool _isInterruptedAnimation = false; /// /// Morphs from source control to target control. /// + /// Indicates whether to force the update of the child element list before the animation starts. /// A that completes when all animations have completed. public async Task AnimateAsync(bool forceUpdateAnimatedElements = false) { @@ -78,6 +77,7 @@ public async Task AnimateAsync(bool forceUpdateAnimatedElements = false) /// /// Reverse animation, morphs from target control to source control. /// + /// Indicates whether to force the update of the child element list before the animation starts. /// A that completes when all animations have completed. public async Task ReverseAsync(bool forceUpdateAnimatedElements = false) { From 40c7af65e49f8c2394200612f1d48dcd38caa3ed Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 25 Oct 2021 11:00:33 +0800 Subject: [PATCH 07/94] Fix scale animation. --- .../TransitionHelperCode.bind | 36 ++++++++++++++----- .../TransitionHelperPage.xaml.cs | 4 +++ .../Helpers/TransitionHelper.Logic.cs | 29 +++++++++++++-- 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind index 098000079a4..039e8e6eefe 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind @@ -1,27 +1,47 @@ -//create a TransitionHelper. +// Create a TransitionHelper. var TransitionHelper transitionHelper = new TransitionHelper(); -//Configure TransitionHelper. -transitionHelper.AnimationConfigs = new AnimationConfig[]{ +// Configure TransitionHelper. +transitionHelper.AnimationConfigs = new AnimationConfig[] +{ new AnimationConfig { - Id = "background" + Id = "background", + AdditionalAnimations = new AnimationTarget[] + { + AnimationTarget.Scale, + }, }, new AnimationConfig { Id = "image", - AdditionalAnimations = new AnimationTarget[]{ AnimationTarget.Scale } + AdditionalAnimations = new AnimationTarget[] + { + AnimationTarget.Scale, + }, }, new AnimationConfig { - Id = "guide" + Id = "guide", + }, + new AnimationConfig + { + Id = "name", + AdditionalAnimations = new AnimationTarget[] + { + AnimationTarget.ScaleY, + }, + }, + new AnimationConfig + { + Id = "desc", }, }; transitionHelper.Source = FirstControl; transitionHelper.Target = SecondControl; -//Animate. +// Animate. await transitionHelper.AnimateAsync() -//Reverse. +// Reverse. await transitionHelper.ReverseAsync(); \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs index 0aab97ebaa5..96b0146d02a 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs @@ -55,6 +55,10 @@ public TransitionHelperPage() new AnimationConfig { Id = "name", + AdditionalAnimations = new AnimationTarget[] + { + AnimationTarget.ScaleY, + }, }, new AnimationConfig { diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 92da8845d7a..1597562a330 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -21,6 +21,11 @@ public sealed partial class TransitionHelper { private void UpdateSourceAnimatedElements() { + if (this.Source == null) + { + return; + } + this.sourceAnimatedElements.Clear(); var filters = this.AnimationConfigs.Select(config => config.Id); @@ -32,6 +37,11 @@ private void UpdateSourceAnimatedElements() private void UpdateTargetAnimatedElements() { + if (this.Target == null) + { + return; + } + this.targetAnimatedElements.Clear(); var filters = this.AnimationConfigs.Select(config => config.Id); @@ -309,8 +319,23 @@ private void AnimateUIElementsScaleY(AnimationBuilder sourceBuilder, AnimationBu private void AnimateUIElementsScale(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, Vector3 targetScale, TimeSpan duration) { - _ = sourceBuilder.Scale(to: targetScale, duration: duration); - _ = targetBuilder.Scale(to: Vector3.One, duration: duration); + if (this._isInterruptedAnimation) + { + _ = sourceBuilder.Scale(to: Vector3.One, duration: TimeSpan.FromMilliseconds(1)); + _ = targetBuilder.Scale(to: Vector3.One, duration: duration); + return; + } + + _ = sourceBuilder.Scale().TimedKeyFrames( + build: b => b + .KeyFrame(duration - TimeSpan.FromMilliseconds(1), targetScale) + .KeyFrame(duration, Vector3.One)); + _ = targetBuilder.Scale().TimedKeyFrames( + delayBehavior: AnimationDelayBehavior.SetInitialValueBeforeDelay, + build: b => b + .KeyFrame(TimeSpan.Zero, new Vector3(1 / targetScale.X, 1 / targetScale.Y, 1)) + .KeyFrame(duration - TimeSpan.FromMilliseconds(1), Vector3.One) + .KeyFrame(duration, Vector3.One)); } private void AnimateUIElementsOpacity(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, TimeSpan duration) From ebb0ed22574e01a9ac000a8bd008292e1e0dc402 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 25 Oct 2021 11:14:49 +0800 Subject: [PATCH 08/94] Update TransitionHelperXaml.bind --- .../TransitionHelperXaml.bind | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index f2e1fa2bf53..1b7d974fd5a 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -27,11 +27,15 @@ Opacity="0.4" /> - + CornerRadius="18"> + + + + ⬅ Click Here @@ -58,17 +62,21 @@ Padding="20" Orientation="Horizontal" Spacing="20"> - + CornerRadius="25"> + + + + - Magic - Magic is a cute cat. + Magic + Magic is a cute 😺. @@ -96,28 +104,27 @@ - + CornerRadius="40"> + + + + Magic - Magic is a cute cat, but sometimes very naughty. + Magic is a cute 😺, but sometimes very naughty. From b32eef24ddad7470afbf5ee8b580ece42760f67c Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 25 Oct 2021 13:12:59 +0800 Subject: [PATCH 09/94] update method names --- .../TransitionHelperPage.xaml.cs | 4 ++-- .../Helpers/TransitionHelper.Logic.cs | 22 ++++++++++--------- .../Helpers/TransitionHelper.Properties.cs | 20 ++++++++--------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs index 96b0146d02a..fe777a2b92c 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs @@ -86,7 +86,7 @@ private void FirstControl_Tapped(object sender, TappedRoutedEventArgs e) { this.transitionHelper.Source = this.firstControl; this.transitionHelper.Target = this.secondControl; - this.transitionHelper.IgnoredAndUnpairedElementHideTranslation = new Vector3(20, 0, 0); + this.transitionHelper.IgnoredOrUnpairedElementHideTranslation = new Vector3(20, 0, 0); _ = this.transitionHelper.AnimateAsync(); } @@ -99,7 +99,7 @@ private void SecondButton_Click(object sender, RoutedEventArgs e) { this.transitionHelper.Source = this.secondControl; this.transitionHelper.Target = this.thirdControl; - this.transitionHelper.IgnoredAndUnpairedElementHideTranslation = new Vector3(0, 20, 0); + this.transitionHelper.IgnoredOrUnpairedElementHideTranslation = new Vector3(0, 20, 0); _ = this.transitionHelper.AnimateAsync(); } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 1597562a330..fb2b83f3318 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -111,8 +111,8 @@ private async Task AnimateFromSourceToTargetAsync(CancellationToken token) animationTasks.Add(this.AnimateElementsAsync(source, target, duration, item, token)); } - animationTasks.Add(this.AnimateIgnoredAndUnpairedElementsAsync(this.Source, sourceUnpairedElements, false, token)); - animationTasks.Add(this.AnimateIgnoredAndUnpairedElementsAsync(this.Target, targetUnpairedElements, true, token)); + animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Source, sourceUnpairedElements, false, token)); + animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Target, targetUnpairedElements, true, token)); await Task.WhenAll(animationTasks); } @@ -149,8 +149,8 @@ private async Task AnimateFromTargetToSourceAsync(CancellationToken token) animationTasks.Add(this.AnimateElementsAsync(target, source, duration, item, token)); } - animationTasks.Add(this.AnimateIgnoredAndUnpairedElementsAsync(this.Source, sourceUnpairedElements, true, token)); - animationTasks.Add(this.AnimateIgnoredAndUnpairedElementsAsync(this.Target, targetUnpairedElements, false, token)); + animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Source, sourceUnpairedElements, true, token)); + animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Target, targetUnpairedElements, false, token)); await Task.WhenAll(animationTasks); } @@ -232,7 +232,7 @@ private async Task AnimateElementsAsync(UIElement source, UIElement target, Time await Task.WhenAll(animationTasks); } - private async Task AnimateIgnoredAndUnpairedElementsAsync(UIElement parent, IEnumerable unpairedElements, bool isShow, CancellationToken token) + private async Task AnimateIgnoredOrUnpairedElementsAsync(UIElement parent, IEnumerable unpairedElements, bool isShow, CancellationToken token) { if (parent == null) { @@ -241,8 +241,8 @@ private async Task AnimateIgnoredAndUnpairedElementsAsync(UIElement parent, IEnu var animationTasks = new List(); var ignoredElements = GetIgnoredElements(parent); - var duration = isShow ? this.IgnoredAndUnpairedElementShowDuration : this.IgnoredAndUnpairedElementHideDuration; - var delay = isShow ? this.IgnoredAndUnpairedElementShowDelayDuration : TimeSpan.Zero; + var duration = isShow ? this.IgnoredOrUnpairedElementShowDuration : this.IgnoredOrUnpairedElementHideDuration; + var delay = isShow ? this.IgnoredOrUnpairedElementShowDelayDuration : TimeSpan.Zero; if (this._isInterruptedAnimation) { duration *= this.interruptedAnimationReverseDurationRatio; @@ -251,22 +251,24 @@ private async Task AnimateIgnoredAndUnpairedElementsAsync(UIElement parent, IEnu foreach (var item in ignoredElements.Concat(unpairedElements)) { - if (this.IgnoredAndUnpairedElementHideTranslation != Vector3.Zero) + if (this.IgnoredOrUnpairedElementHideTranslation != Vector3.Zero) { animationTasks.Add(AnimationBuilder.Create().Translation( - to: isShow ? Vector3.Zero : this.IgnoredAndUnpairedElementHideTranslation, + from: this._isInterruptedAnimation ? null : (isShow ? this.IgnoredOrUnpairedElementHideTranslation : Vector3.Zero), + to: isShow ? Vector3.Zero : this.IgnoredOrUnpairedElementHideTranslation, duration: duration, delay: delay).StartAsync(item, token)); } animationTasks.Add(AnimationBuilder.Create().Opacity( + from: this._isInterruptedAnimation ? null : (isShow ? 0 : 1), to: isShow ? 1 : 0, duration: duration, delay: delay).StartAsync(item, token)); if (isShow) { - delay += this.IgnoredAndUnpairedElementShowStepDuration; + delay += this.IgnoredOrUnpairedElementShowStepDuration; } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 57b2d650b4d..ac087189bd3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -93,28 +93,28 @@ public IEnumerable AnimationConfigs public TimeSpan AnimationDuration { get; set; } = TimeSpan.FromMilliseconds(600); /// - /// Gets or sets the duration of the show animation for ignored and unpaired UI elements. + /// Gets or sets the duration of the show animation for ignored or unpaired UI elements. /// - public TimeSpan IgnoredAndUnpairedElementShowDuration { get; set; } = TimeSpan.FromMilliseconds(200); + public TimeSpan IgnoredOrUnpairedElementShowDuration { get; set; } = TimeSpan.FromMilliseconds(200); /// - /// Gets or sets the delay of the show animation for ignored and unpaired UI elements. + /// Gets or sets the delay of the show animation for ignored or unpaired UI elements. /// - public TimeSpan IgnoredAndUnpairedElementShowDelayDuration { get; set; } = TimeSpan.FromMilliseconds(300); + public TimeSpan IgnoredOrUnpairedElementShowDelayDuration { get; set; } = TimeSpan.FromMilliseconds(300); /// - /// Gets or sets the duration of the interval between the show animations for ignored and unpaired UI elements. + /// Gets or sets the duration of the interval between the show animations for ignored or unpaired UI elements. /// - public TimeSpan IgnoredAndUnpairedElementShowStepDuration { get; set; } = TimeSpan.FromMilliseconds(50); + public TimeSpan IgnoredOrUnpairedElementShowStepDuration { get; set; } = TimeSpan.FromMilliseconds(50); /// - /// Gets or sets the duration of the hide animation for ignored and unpaired UI elements. + /// Gets or sets the duration of the hide animation for ignored or unpaired UI elements. /// - public TimeSpan IgnoredAndUnpairedElementHideDuration { get; set; } = TimeSpan.FromMilliseconds(100); + public TimeSpan IgnoredOrUnpairedElementHideDuration { get; set; } = TimeSpan.FromMilliseconds(100); /// - /// Gets or sets the translation of the hide animation for ignored and unpaired UI elements. + /// Gets or sets the translation of the hide animation for ignored or unpaired UI elements. /// - public Vector3 IgnoredAndUnpairedElementHideTranslation { get; set; } = new Vector3(0, 20, 0); + public Vector3 IgnoredOrUnpairedElementHideTranslation { get; set; } = new Vector3(0, 20, 0); } } From 499860225c3f26ba07ff9aac320d920ebb97dc7d Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 25 Oct 2021 14:14:32 +0800 Subject: [PATCH 10/94] Update sample --- .../TransitionHelperPage.xaml | 18 ++++- .../TransitionHelperPage.xaml.cs | 65 +++++++++++++------ .../TransitionHelperXaml.bind | 59 +++++++++++++---- 3 files changed, 106 insertions(+), 36 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml index 4f9b1738cd4..5a32cab46dc 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml @@ -5,6 +5,20 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs index fe777a2b92c..1fa99851e0d 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs @@ -20,9 +20,6 @@ public sealed partial class TransitionHelperPage : Page, IXamlRenderListener private FrameworkElement firstControl; private FrameworkElement secondControl; private FrameworkElement thirdControl; - private Button secondButton; - private Button secondBackButton; - private Button thirdButton; /// /// Initializes a new instance of the class. @@ -73,41 +70,69 @@ public void OnXamlRendered(FrameworkElement control) this.firstControl = control.FindChild("FirstControl"); this.secondControl = control.FindChild("SecondControl"); this.thirdControl = control.FindChild("ThirdControl"); - this.secondButton = control.FindChild("SecondButton") as Button; - this.secondBackButton = control.FindChild("SecondBackButton") as Button; - this.thirdButton = control.FindChild("ThirdButton") as Button; - this.firstControl.Tapped += this.FirstControl_Tapped; - this.secondBackButton.Click += this.SecondBackButton_Click; - this.secondButton.Click += this.SecondButton_Click; - this.thirdButton.Click += this.ThirdButton_Click; + var minToMidButton = control.FindChild("MinToMidButton") as Button; + var minToMaxButton = control.FindChild("MinToMaxButton") as Button; + var midGoBackButton = control.FindChild("MidGoBackButton") as Button; + var midToMinButton = control.FindChild("MidToMinButton") as Button; + var midToMaxButton = control.FindChild("MidToMaxButton") as Button; + var maxGoBackButton = control.FindChild("MaxGoBackButton") as Button; + var maxToMinButton = control.FindChild("MaxToMinButton") as Button; + var maxToMidButton = control.FindChild("MaxToMidButton") as Button; + minToMidButton.Click += this.MinToMidButton_Click; + minToMaxButton.Click += this.MinToMaxButton_Click; + midToMinButton.Click += this.MidToMinButton_Click; + midToMaxButton.Click += this.MidToMaxButton_Click; + maxToMinButton.Click += this.MaxToMinButton_Click; + maxToMidButton.Click += this.MaxToMidButton_Click; + midGoBackButton.Click += this.GoBackButton_Click; + maxGoBackButton.Click += this.GoBackButton_Click; } - private void FirstControl_Tapped(object sender, TappedRoutedEventArgs e) + private void MaxToMidButton_Click(object sender, RoutedEventArgs e) { - this.transitionHelper.Source = this.firstControl; + this.transitionHelper.Source = this.thirdControl; this.transitionHelper.Target = this.secondControl; - this.transitionHelper.IgnoredOrUnpairedElementHideTranslation = new Vector3(20, 0, 0); _ = this.transitionHelper.AnimateAsync(); } - private void SecondBackButton_Click(object sender, RoutedEventArgs e) + private void MaxToMinButton_Click(object sender, RoutedEventArgs e) { - _ = this.transitionHelper.ReverseAsync(); + this.transitionHelper.Source = this.thirdControl; + this.transitionHelper.Target = this.firstControl; + _ = this.transitionHelper.AnimateAsync(); } - private void SecondButton_Click(object sender, RoutedEventArgs e) + private void MidToMinButton_Click(object sender, RoutedEventArgs e) + { + this.transitionHelper.Source = this.secondControl; + this.transitionHelper.Target = this.firstControl; + _ = this.transitionHelper.AnimateAsync(); + } + + private void MidToMaxButton_Click(object sender, RoutedEventArgs e) { this.transitionHelper.Source = this.secondControl; this.transitionHelper.Target = this.thirdControl; - this.transitionHelper.IgnoredOrUnpairedElementHideTranslation = new Vector3(0, 20, 0); _ = this.transitionHelper.AnimateAsync(); } - private void ThirdButton_Click(object sender, RoutedEventArgs e) + private void MinToMaxButton_Click(object sender, RoutedEventArgs e) { - this.transitionHelper.Source = this.thirdControl; - this.transitionHelper.Target = this.firstControl; + this.transitionHelper.Source = this.firstControl; + this.transitionHelper.Target = this.thirdControl; _ = this.transitionHelper.AnimateAsync(); } + + private void MinToMidButton_Click(object sender, RoutedEventArgs e) + { + this.transitionHelper.Source = this.firstControl; + this.transitionHelper.Target = this.secondControl; + _ = this.transitionHelper.AnimateAsync(); + } + + private void GoBackButton_Click(object sender, RoutedEventArgs e) + { + _ = this.transitionHelper.ReverseAsync(); + } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index 1b7d974fd5a..a3986a2cd9e 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -37,9 +37,17 @@ - - ⬅ Click Here - + + + + Magic Magic is a cute 😺. - - - + + + + @@ -166,14 +186,25 @@ RadiusY="5" /> - + + + + + \ No newline at end of file From 22f6779634f9a096eec4af2953ea182eacedf98b Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 25 Oct 2021 16:05:48 +0800 Subject: [PATCH 11/94] Remove redundant code --- .../Helpers/TransitionHelper.Logic.cs | 52 ++++++------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index fb2b83f3318..d39fa8f665d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -81,43 +81,15 @@ private void ToggleVisualState(UIElement target, VisualStateToggleMethod method, private async Task AnimateFromSourceToTargetAsync(CancellationToken token) { - var duration = this.AnimationDuration; - if (this._isInterruptedAnimation) - { - duration *= this.interruptedAnimationReverseDurationRatio; - } - - var animationTasks = new List(); - var sourceUnpairedElements = new List(); - var targetUnpairedElements = new List(); - foreach (var item in this.AnimationConfigs) - { - (var source, var target) = this.GetPairElements(item); - if (source == null || target == null) - { - if (source != null) - { - sourceUnpairedElements.Add(source); - } - - if (target != null) - { - targetUnpairedElements.Add(target); - } - - continue; - } - - animationTasks.Add(this.AnimateElementsAsync(source, target, duration, item, token)); - } - - animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Source, sourceUnpairedElements, false, token)); - animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Target, targetUnpairedElements, true, token)); - - await Task.WhenAll(animationTasks); + await this.AnimateControlsAsync(false, token); } private async Task AnimateFromTargetToSourceAsync(CancellationToken token) + { + await this.AnimateControlsAsync(true, token); + } + + private async Task AnimateControlsAsync(bool reversed, CancellationToken token) { var duration = this.AnimationDuration; if (this._isInterruptedAnimation) @@ -146,11 +118,17 @@ private async Task AnimateFromTargetToSourceAsync(CancellationToken token) continue; } - animationTasks.Add(this.AnimateElementsAsync(target, source, duration, item, token)); + animationTasks.Add( + this.AnimateElementsAsync( + reversed ? target : source, + reversed ? source : target, + duration, + item, + token)); } - animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Source, sourceUnpairedElements, true, token)); - animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Target, targetUnpairedElements, false, token)); + animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Source, sourceUnpairedElements, reversed, token)); + animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Target, targetUnpairedElements, !reversed, token)); await Task.WhenAll(animationTasks); } From 27d5667471dd72c6f17bed3e9229c62c8983de2d Mon Sep 17 00:00:00 2001 From: hhchaos Date: Tue, 26 Oct 2021 14:03:14 +0800 Subject: [PATCH 12/94] Modify sample layout --- .../SamplePages/TransitionHelper/TransitionHelperCode.bind | 2 +- .../SamplePages/TransitionHelper/TransitionHelperXaml.bind | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind index 039e8e6eefe..ac8c884981d 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind @@ -1,5 +1,5 @@ // Create a TransitionHelper. -var TransitionHelper transitionHelper = new TransitionHelper(); +var transitionHelper = new TransitionHelper(); // Configure TransitionHelper. transitionHelper.AnimationConfigs = new AnimationConfig[] diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index a3986a2cd9e..2cd70278f19 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -10,7 +10,7 @@ @@ -51,7 +51,7 @@ @@ -106,7 +106,6 @@ From 51bb56c794f472c62747321cd38a066b4573d260 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Wed, 27 Oct 2021 18:34:07 +0800 Subject: [PATCH 13/94] Fix dark mode --- .../TransitionHelper/TransitionHelperXaml.bind | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index 2cd70278f19..0b4a6e58b05 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -18,8 +18,8 @@ + Background="{ThemeResource SystemControlBackgroundAltHighBrush}" + CornerRadius="4"> Date: Wed, 10 Nov 2021 15:42:50 +0800 Subject: [PATCH 14/94] Add ScaleMode --- .../TransitionHelperCode.bind | 45 ++++++++----------- .../TransitionHelperPage.xaml.cs | 15 ++----- .../Enums/AnimationTarget.cs | 37 --------------- .../Enums/ScaleMode.cs | 34 ++++++++++++++ .../Helpers/AnimationConfig.cs | 20 +-------- .../Helpers/TransitionHelper.Logic.cs | 45 ++++++++----------- 6 files changed, 76 insertions(+), 120 deletions(-) delete mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Enums/AnimationTarget.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind index ac8c884981d..22ddc6535e4 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind @@ -3,40 +3,31 @@ var transitionHelper = new TransitionHelper(); // Configure TransitionHelper. transitionHelper.AnimationConfigs = new AnimationConfig[] -{ - new AnimationConfig { - Id = "background", - AdditionalAnimations = new AnimationTarget[] + new AnimationConfig { - AnimationTarget.Scale, + Id = "background", + ScaleMode = ScaleMode.Scale }, - }, - new AnimationConfig - { - Id = "image", - AdditionalAnimations = new AnimationTarget[] + new AnimationConfig { - AnimationTarget.Scale, + Id = "image", + ScaleMode = ScaleMode.Scale }, - }, - new AnimationConfig - { - Id = "guide", - }, - new AnimationConfig - { - Id = "name", - AdditionalAnimations = new AnimationTarget[] + new AnimationConfig { - AnimationTarget.ScaleY, + Id = "guide", }, - }, - new AnimationConfig - { - Id = "desc", - }, -}; + new AnimationConfig + { + Id = "name", + ScaleMode = ScaleMode.ScaleY + }, + new AnimationConfig + { + Id = "desc", + }, + }; transitionHelper.Source = FirstControl; transitionHelper.Target = SecondControl; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs index 1fa99851e0d..a13cacb3a5c 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs @@ -32,18 +32,12 @@ public TransitionHelperPage() new AnimationConfig { Id = "background", - AdditionalAnimations = new AnimationTarget[] - { - AnimationTarget.Scale, - }, + ScaleMode = ScaleMode.Scale }, new AnimationConfig { Id = "image", - AdditionalAnimations = new AnimationTarget[] - { - AnimationTarget.Scale, - }, + ScaleMode = ScaleMode.Scale }, new AnimationConfig { @@ -52,10 +46,7 @@ public TransitionHelperPage() new AnimationConfig { Id = "name", - AdditionalAnimations = new AnimationTarget[] - { - AnimationTarget.ScaleY, - }, + ScaleMode = ScaleMode.ScaleY }, new AnimationConfig { diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/AnimationTarget.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/AnimationTarget.cs deleted file mode 100644 index b64b029d9e3..00000000000 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/AnimationTarget.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Toolkit.Uwp.UI.Animations -{ - /// - /// Indicates the target property of the UI element to be animated. - /// - public enum AnimationTarget - { - /// - /// The translation property of a UI element. - /// - Translation, - - /// - /// The scale property of a UI element. - /// - Scale, - - /// - /// The horizontal scale property of a UI element. - /// - ScaleX, - - /// - /// The vertical scale property of a UI element. - /// - ScaleY, - - /// - /// The opacity property of a UI element. - /// - Opacity - } -} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs new file mode 100644 index 00000000000..30f75733129 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// Indicates the strategy when the scale property of a UI element is animated. + /// + public enum ScaleMode + { + /// + /// Do not make any changes to the scale attribute of the UI element. + /// + None, + + /// + /// Apply the scale changes to the horizontal and vertical directions of the UI element. + /// + Scale, + + /// + /// Apply the scale changes to the horizontal and vertical directions of the UI element, + /// but the value is calculated based on the changes in the horizontal direction. + /// + ScaleX, + + /// + /// Apply scale changes to the horizontal and vertical directions of the UI element, + /// but the value is calculated based on the change in the vertical direction. + /// + ScaleY, + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs index 1f309b645af..f8cb6eb34f7 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs @@ -2,9 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; -using System.Linq; - namespace Microsoft.Toolkit.Uwp.UI.Animations { /// @@ -12,27 +9,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// public class AnimationConfig { - private readonly AnimationTarget[] defaultAnimations = new AnimationTarget[] { AnimationTarget.Translation, AnimationTarget.Opacity }; - /// /// Gets or sets id of UI element. /// public string Id { get; set; } /// - /// Gets or sets additional animations applied to UI elements. - /// - public AnimationTarget[] AdditionalAnimations { get; set; } - - /// - /// Gets all animations applied to UI elements. + /// Gets or sets the scale strategy of UI element. /// - public IEnumerable Animations - { - get - { - return new HashSet(this.AdditionalAnimations?.Concat(this.defaultAnimations) ?? this.defaultAnimations); - } - } + public ScaleMode ScaleMode { get; set; } = ScaleMode.None; } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index d39fa8f665d..10a47311b95 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -178,35 +178,28 @@ private void TargetControl_LayoutUpdated(object sender, object e) private async Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan duration, AnimationConfig config, CancellationToken token) { var animationTasks = new List(); - foreach (var animation in config.Animations) + var sourceBuilder = AnimationBuilder.Create(); + var targetBuilder = AnimationBuilder.Create(); + this.AnimateUIElementsTranslation(sourceBuilder, targetBuilder, source, target, duration); + this.AnimateUIElementsOpacity(sourceBuilder, targetBuilder, duration * 1 / 3); // Make opacity animation faster + switch (config.ScaleMode) { - var sourceBuilder = AnimationBuilder.Create(); - var targetBuilder = AnimationBuilder.Create(); - switch (animation) - { - case AnimationTarget.Translation: - this.AnimateUIElementsTranslation(sourceBuilder, targetBuilder, source, target, duration); - break; - case AnimationTarget.Scale: - this.AnimateUIElementsScale(sourceBuilder, targetBuilder, source, target, duration); - break; - case AnimationTarget.ScaleX: - this.AnimateUIElementsScaleX(sourceBuilder, targetBuilder, source, target, duration); - break; - case AnimationTarget.ScaleY: - this.AnimateUIElementsScaleY(sourceBuilder, targetBuilder, source, target, duration); - break; - case AnimationTarget.Opacity: - this.AnimateUIElementsOpacity(sourceBuilder, targetBuilder, duration * 1 / 3); // Make opacity animation faster - break; - default: - break; - } - - animationTasks.Add(sourceBuilder.StartAsync(source, token)); - animationTasks.Add(targetBuilder.StartAsync(target, token)); + case ScaleMode.Scale: + this.AnimateUIElementsScale(sourceBuilder, targetBuilder, source, target, duration); + break; + case ScaleMode.ScaleX: + this.AnimateUIElementsScaleX(sourceBuilder, targetBuilder, source, target, duration); + break; + case ScaleMode.ScaleY: + this.AnimateUIElementsScaleY(sourceBuilder, targetBuilder, source, target, duration); + break; + default: + break; } + animationTasks.Add(sourceBuilder.StartAsync(source, token)); + animationTasks.Add(targetBuilder.StartAsync(target, token)); + await Task.WhenAll(animationTasks); } From 25ed52f79a5ce89b1bf5d4b78eef1faf860693cb Mon Sep 17 00:00:00 2001 From: hhchaos Date: Wed, 10 Nov 2021 17:18:53 +0800 Subject: [PATCH 15/94] Added StartTransitionAction --- .../TransitionHelperPage.xaml | 11 ++ .../TransitionHelperPage.xaml.cs | 99 ------------ .../TransitionHelperXaml.bind | 141 +++++++++++++----- .../Helpers/TransitionHelper.Properties.cs | 4 +- .../Animations/StartTransitionAction.cs | 96 ++++++++++++ 5 files changed, 209 insertions(+), 142 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml index 5a32cab46dc..0cab022b6af 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml @@ -2,7 +2,10 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:animations="using:Microsoft.Toolkit.Uwp.UI.Animations" + xmlns:behaviors="using:Microsoft.Toolkit.Uwp.UI.Behaviors" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:interactions="using:Microsoft.Xaml.Interactions.Core" + xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs index a13cacb3a5c..a0057ad1eef 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs @@ -2,12 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Numerics; -using Microsoft.Toolkit.Uwp.UI; using Microsoft.Toolkit.Uwp.UI.Animations; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Input; namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages { @@ -16,10 +13,6 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages /// public sealed partial class TransitionHelperPage : Page, IXamlRenderListener { - private readonly TransitionHelper transitionHelper = new (); - private FrameworkElement firstControl; - private FrameworkElement secondControl; - private FrameworkElement thirdControl; /// /// Initializes a new instance of the class. @@ -27,103 +20,11 @@ public sealed partial class TransitionHelperPage : Page, IXamlRenderListener public TransitionHelperPage() { this.InitializeComponent(); - this.transitionHelper.AnimationConfigs = new AnimationConfig[] - { - new AnimationConfig - { - Id = "background", - ScaleMode = ScaleMode.Scale - }, - new AnimationConfig - { - Id = "image", - ScaleMode = ScaleMode.Scale - }, - new AnimationConfig - { - Id = "guide", - }, - new AnimationConfig - { - Id = "name", - ScaleMode = ScaleMode.ScaleY - }, - new AnimationConfig - { - Id = "desc", - }, - }; } /// public void OnXamlRendered(FrameworkElement control) { - this.firstControl = control.FindChild("FirstControl"); - this.secondControl = control.FindChild("SecondControl"); - this.thirdControl = control.FindChild("ThirdControl"); - var minToMidButton = control.FindChild("MinToMidButton") as Button; - var minToMaxButton = control.FindChild("MinToMaxButton") as Button; - var midGoBackButton = control.FindChild("MidGoBackButton") as Button; - var midToMinButton = control.FindChild("MidToMinButton") as Button; - var midToMaxButton = control.FindChild("MidToMaxButton") as Button; - var maxGoBackButton = control.FindChild("MaxGoBackButton") as Button; - var maxToMinButton = control.FindChild("MaxToMinButton") as Button; - var maxToMidButton = control.FindChild("MaxToMidButton") as Button; - minToMidButton.Click += this.MinToMidButton_Click; - minToMaxButton.Click += this.MinToMaxButton_Click; - midToMinButton.Click += this.MidToMinButton_Click; - midToMaxButton.Click += this.MidToMaxButton_Click; - maxToMinButton.Click += this.MaxToMinButton_Click; - maxToMidButton.Click += this.MaxToMidButton_Click; - midGoBackButton.Click += this.GoBackButton_Click; - maxGoBackButton.Click += this.GoBackButton_Click; - } - - private void MaxToMidButton_Click(object sender, RoutedEventArgs e) - { - this.transitionHelper.Source = this.thirdControl; - this.transitionHelper.Target = this.secondControl; - _ = this.transitionHelper.AnimateAsync(); - } - - private void MaxToMinButton_Click(object sender, RoutedEventArgs e) - { - this.transitionHelper.Source = this.thirdControl; - this.transitionHelper.Target = this.firstControl; - _ = this.transitionHelper.AnimateAsync(); - } - - private void MidToMinButton_Click(object sender, RoutedEventArgs e) - { - this.transitionHelper.Source = this.secondControl; - this.transitionHelper.Target = this.firstControl; - _ = this.transitionHelper.AnimateAsync(); - } - - private void MidToMaxButton_Click(object sender, RoutedEventArgs e) - { - this.transitionHelper.Source = this.secondControl; - this.transitionHelper.Target = this.thirdControl; - _ = this.transitionHelper.AnimateAsync(); - } - - private void MinToMaxButton_Click(object sender, RoutedEventArgs e) - { - this.transitionHelper.Source = this.firstControl; - this.transitionHelper.Target = this.thirdControl; - _ = this.transitionHelper.AnimateAsync(); - } - - private void MinToMidButton_Click(object sender, RoutedEventArgs e) - { - this.transitionHelper.Source = this.firstControl; - this.transitionHelper.Target = this.secondControl; - _ = this.transitionHelper.AnimateAsync(); - } - - private void GoBackButton_Click(object sender, RoutedEventArgs e) - { - _ = this.transitionHelper.ReverseAsync(); } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index 0b4a6e58b05..12d4d9fd0fb 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -5,12 +5,26 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ui="using:Microsoft.Toolkit.Uwp.UI" xmlns:animations="using:Microsoft.Toolkit.Uwp.UI.Animations" + xmlns:behaviors="using:Microsoft.Toolkit.Uwp.UI.Behaviors" xmlns:media="using:Microsoft.Toolkit.Uwp.UI.Media" + xmlns:interactions="using:Microsoft.Xaml.Interactions.Core" + xmlns:interactivity="using:Microsoft.Xaml.Interactivity" mc:Ignorable="d"> + + + + + + + + + + + - - - @@ -70,7 +101,7 @@ Padding="20" Orientation="Horizontal" Spacing="20"> - Magic Magic is a cute 😺. - - - - @@ -140,9 +186,7 @@ FontSize="24"> Magic - + Magic is a cute 😺, but sometimes very naughty. @@ -185,23 +229,38 @@ RadiusY="5" /> - - - - diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index ac087189bd3..c36f7a58261 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -16,7 +16,7 @@ public sealed partial class TransitionHelper { private FrameworkElement _source; private FrameworkElement _target; - private IEnumerable _animationConfigs; + private List _animationConfigs = new(); /// /// Gets or sets the source control. @@ -57,7 +57,7 @@ public FrameworkElement Target /// /// Gets or sets the collection of animation configurations of UI elements that need to be connected by animation. /// - public IEnumerable AnimationConfigs + public List AnimationConfigs { get { diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs new file mode 100644 index 00000000000..375764f5567 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Toolkit.Uwp.UI.Animations; +using Microsoft.Xaml.Interactivity; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Behaviors +{ + /// + /// An implementation that can trigger a target instance. + /// + public sealed class StartTransitionAction : DependencyObject, IAction + { + /// + /// Gets or sets the linked instance to invoke. + /// + public TransitionHelper Transition + { + get => (TransitionHelper)GetValue(TransitionProperty); + set => SetValue(TransitionProperty, value); + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty TransitionProperty = DependencyProperty.Register( + nameof(Transition), + typeof(TransitionHelper), + typeof(StartTransitionAction), + new PropertyMetadata(null)); + + /// + /// Gets or sets the source control of the . + /// + public FrameworkElement Source + { + get => (FrameworkElement)GetValue(SourceProperty); + set => SetValue(SourceProperty, value); + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty SourceProperty = DependencyProperty.Register( + nameof(Source), + typeof(FrameworkElement), + typeof(StartTransitionAction), + new PropertyMetadata(null)); + + /// + /// Gets or sets the target control of the . + /// + public FrameworkElement Target + { + get => (FrameworkElement)GetValue(TargetProperty); + set => SetValue(TargetProperty, value); + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty TargetProperty = DependencyProperty.Register( + nameof(Target), + typeof(FrameworkElement), + typeof(StartTransitionAction), + new PropertyMetadata(null)); + + /// + public object Execute(object sender, object parameter) + { + if (Transition is null) + { + throw new ArgumentNullException(nameof(Transition)); + } + + if (Source is null) + { + throw new ArgumentNullException(nameof(Source)); + } + + if (Target is null) + { + throw new ArgumentNullException(nameof(Target)); + } + + Transition.Source = Source; + Transition.Target = Target; + _ = Transition.AnimateAsync(); + + return null; + } + } +} \ No newline at end of file From 637800a38ad0b08cca35c0a35a0af36babaf6b9c Mon Sep 17 00:00:00 2001 From: hhchaos Date: Thu, 11 Nov 2021 17:25:54 +0800 Subject: [PATCH 16/94] Add ReverseTransitionAction --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 13 +- .../Helpers}/TransitionHelper.png | Bin .../Helpers}/TransitionHelperCode.bind | 2 +- .../Helpers/TransitionHelperXaml.bind | 292 ++++++++++++++++++ .../TransitionHelperPage.xaml | 35 --- .../TransitionHelperPage.xaml.cs | 30 -- .../TransitionHelperXaml.bind | 268 ---------------- .../SamplePages/samples.json | 7 +- .../Animations/ReverseTransitionAction.cs | 55 ++++ .../Animations/StartTransitionAction.cs | 51 ++- 10 files changed, 390 insertions(+), 363 deletions(-) rename Microsoft.Toolkit.Uwp.SampleApp/SamplePages/{TransitionHelper => Animations/Helpers}/TransitionHelper.png (100%) rename Microsoft.Toolkit.Uwp.SampleApp/SamplePages/{TransitionHelper => Animations/Helpers}/TransitionHelperCode.bind (92%) create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperXaml.bind delete mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml delete mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs delete mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind create mode 100644 Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/ReverseTransitionAction.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 8abf8245a98..c7dc63c7317 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -297,7 +297,7 @@ --> - + @@ -563,9 +563,6 @@ TokenizingTextBoxPage.xaml - - TransitionHelperPage.xaml - FullScreenModeStateTriggerPage.xaml @@ -648,8 +645,8 @@ - - + + @@ -1114,10 +1111,6 @@ Designer MSBuild:Compile - - MSBuild:Compile - Designer - MSBuild:Compile Designer diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelper.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelper.png similarity index 100% rename from Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelper.png rename to Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelper.png diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperCode.bind similarity index 92% rename from Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind rename to Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperCode.bind index 22ddc6535e4..22ee1d60a9e 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperCode.bind @@ -2,7 +2,7 @@ var transitionHelper = new TransitionHelper(); // Configure TransitionHelper. -transitionHelper.AnimationConfigs = new AnimationConfig[] +transitionHelper.AnimationConfigs = new List { new AnimationConfig { diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperXaml.bind new file mode 100644 index 00000000000..a03147387f3 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperXaml.bind @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Magic + Magic is a cute 😺. + + + + + + + + + + + + + + + + + + + + + + + Magic + + + Magic is a cute 😺, but sometimes very naughty. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml deleted file mode 100644 index 0cab022b6af..00000000000 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs deleted file mode 100644 index a0057ad1eef..00000000000 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Toolkit.Uwp.UI.Animations; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; - -namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages -{ - /// - /// A page that shows how to use the helper. - /// - public sealed partial class TransitionHelperPage : Page, IXamlRenderListener - { - - /// - /// Initializes a new instance of the class. - /// - public TransitionHelperPage() - { - this.InitializeComponent(); - } - - /// - public void OnXamlRendered(FrameworkElement control) - { - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind deleted file mode 100644 index 12d4d9fd0fb..00000000000 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ /dev/null @@ -1,268 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Magic - Magic is a cute 😺. - - - - - - - - - - - - - - - - - - - - - - Magic - - - Magic is a cute 😺, but sometimes very naughty. - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 392a588e2b2..ad5812d2d16 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -674,13 +674,12 @@ }, { "Name": "TransitionHelper", - "Type": "TransitionHelperPage", "Subcategory": "Helpers", "About": "A animation helper that morphs between two controls.", "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Animations/Helpers", - "XamlCodeFile": "TransitionHelperXaml.bind", - "CodeFile": "TransitionHelperCode.bind", - "Icon": "/SamplePages/TransitionHelper/TransitionHelper.png", + "XamlCodeFile": "/SamplePages/Animations/Helpers/TransitionHelperXaml.bind", + "CodeFile": "/SamplePages/Animations/Helpers/TransitionHelperCode.bind", + "Icon": "/SamplePages/Animations/Helpers/TransitionHelper.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/animations/TransitionHelper.md" } ] diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/ReverseTransitionAction.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/ReverseTransitionAction.cs new file mode 100644 index 00000000000..314d4e84899 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/ReverseTransitionAction.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Toolkit.Uwp.UI.Animations; +using Microsoft.Xaml.Interactivity; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Behaviors +{ + /// + /// An implementation that can trigger a target instance. + /// + public sealed class ReverseTransitionAction : DependencyObject, IAction + { + /// + /// Gets or sets the linked instance to reverse. + /// + public TransitionHelper Transition + { + get + { + return (TransitionHelper)this.GetValue(TransitionProperty); + } + + set + { + this.SetValue(TransitionProperty, value); + } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty TransitionProperty = DependencyProperty.Register( + nameof(Transition), + typeof(TransitionHelper), + typeof(StartTransitionAction), + new PropertyMetadata(null)); + + /// + public object Execute(object sender, object parameter) + { + if (this.Transition is null) + { + throw new ArgumentNullException(nameof(this.Transition)); + } + + _ = this.Transition.ReverseAsync(); + + return null; + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs index 375764f5567..3530cf17d5d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs @@ -19,8 +19,15 @@ public sealed class StartTransitionAction : DependencyObject, IAction /// public TransitionHelper Transition { - get => (TransitionHelper)GetValue(TransitionProperty); - set => SetValue(TransitionProperty, value); + get + { + return (TransitionHelper)this.GetValue(TransitionProperty); + } + + set + { + this.SetValue(TransitionProperty, value); + } } /// @@ -37,8 +44,15 @@ public TransitionHelper Transition /// public FrameworkElement Source { - get => (FrameworkElement)GetValue(SourceProperty); - set => SetValue(SourceProperty, value); + get + { + return (FrameworkElement)this.GetValue(SourceProperty); + } + + set + { + this.SetValue(SourceProperty, value); + } } /// @@ -55,8 +69,15 @@ public FrameworkElement Source /// public FrameworkElement Target { - get => (FrameworkElement)GetValue(TargetProperty); - set => SetValue(TargetProperty, value); + get + { + return (FrameworkElement)this.GetValue(TargetProperty); + } + + set + { + this.SetValue(TargetProperty, value); + } } /// @@ -71,24 +92,24 @@ public FrameworkElement Target /// public object Execute(object sender, object parameter) { - if (Transition is null) + if (this.Transition is null) { - throw new ArgumentNullException(nameof(Transition)); + throw new ArgumentNullException(nameof(this.Transition)); } - if (Source is null) + if (this.Source is null) { - throw new ArgumentNullException(nameof(Source)); + throw new ArgumentNullException(nameof(this.Source)); } - if (Target is null) + if (this.Target is null) { - throw new ArgumentNullException(nameof(Target)); + throw new ArgumentNullException(nameof(this.Target)); } - Transition.Source = Source; - Transition.Target = Target; - _ = Transition.AnimateAsync(); + this.Transition.Source = this.Source; + this.Transition.Target = this.Target; + _ = this.Transition.AnimateAsync(); return null; } From 850fc53e5eebe7a352f3c0e15ec1f55dc822ca93 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Thu, 11 Nov 2021 19:40:11 +0800 Subject: [PATCH 17/94] Update sample --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 13 +- .../Helpers/TransitionHelperXaml.bind | 292 ------------------ .../TransitionHelper.png | Bin .../TransitionHelperCode.bind | 0 .../TransitionHelperPage.xaml | 37 +++ .../TransitionHelperPage.xaml.cs | 20 ++ .../TransitionHelperXaml.bind | 283 +++++++++++++++++ .../SamplePages/samples.json | 7 +- 8 files changed, 354 insertions(+), 298 deletions(-) delete mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperXaml.bind rename Microsoft.Toolkit.Uwp.SampleApp/SamplePages/{Animations/Helpers => TransitionHelper}/TransitionHelper.png (100%) rename Microsoft.Toolkit.Uwp.SampleApp/SamplePages/{Animations/Helpers => TransitionHelper}/TransitionHelperCode.bind (100%) create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index c7dc63c7317..8abf8245a98 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -297,7 +297,7 @@ --> - + @@ -563,6 +563,9 @@ TokenizingTextBoxPage.xaml + + TransitionHelperPage.xaml + FullScreenModeStateTriggerPage.xaml @@ -645,8 +648,8 @@ - - + + @@ -1111,6 +1114,10 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + MSBuild:Compile Designer diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperXaml.bind deleted file mode 100644 index a03147387f3..00000000000 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperXaml.bind +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Magic - Magic is a cute 😺. - - - - - - - - - - - - - - - - - - - - - - - Magic - - - Magic is a cute 😺, but sometimes very naughty. - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelper.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelper.png similarity index 100% rename from Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelper.png rename to Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelper.png diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind similarity index 100% rename from Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Helpers/TransitionHelperCode.bind rename to Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml new file mode 100644 index 00000000000..e97fd1fafcb --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs new file mode 100644 index 00000000000..d45a16a20b0 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + public sealed partial class TransitionHelperPage : IXamlRenderListener + { + public TransitionHelperPage() + { + InitializeComponent(); + } + + public void OnXamlRendered(FrameworkElement control) + { + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind new file mode 100644 index 00000000000..14e5e543573 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Magic + Magic is a cute 😺. + + + + + + + + + + + + + + + + + + + + + + + Magic + + + Magic is a cute 😺, but sometimes very naughty. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index ad5812d2d16..b025d63448f 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -674,12 +674,13 @@ }, { "Name": "TransitionHelper", + "Type": "TransitionHelperPage", "Subcategory": "Helpers", "About": "A animation helper that morphs between two controls.", "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Animations/Helpers", - "XamlCodeFile": "/SamplePages/Animations/Helpers/TransitionHelperXaml.bind", - "CodeFile": "/SamplePages/Animations/Helpers/TransitionHelperCode.bind", - "Icon": "/SamplePages/Animations/Helpers/TransitionHelper.png", + "XamlCodeFile": "/SamplePages/TransitionHelper/TransitionHelperXaml.bind", + "CodeFile": "/SamplePages/TransitionHelper/TransitionHelperCode.bind", + "Icon": "/SamplePages/TransitionHelper/TransitionHelper.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/animations/TransitionHelper.md" } ] From c7f1c344b10aeaf2ab32044ca96ec6594ff59e8f Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 15 Nov 2021 17:06:19 +0800 Subject: [PATCH 18/94] Add ContentProperty for TransitionHelper. --- .../TransitionHelper/TransitionHelperXaml.bind | 12 +++++------- .../Helpers/TransitionHelper.cs | 2 ++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index 14e5e543573..d0825639ddf 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -13,13 +13,11 @@ mc:Ignorable="d"> - - - - - - - + + + + + diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index a805cc64593..f282301e232 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -6,12 +6,14 @@ using System.Threading; using System.Threading.Tasks; using Windows.UI.Xaml; +using Windows.UI.Xaml.Markup; namespace Microsoft.Toolkit.Uwp.UI.Animations { /// /// A animation helper that morphs between two controls. /// + [ContentProperty(Name = nameof(AnimationConfigs))] public sealed partial class TransitionHelper { private readonly Dictionary sourceAnimatedElements = new(); From c2b3d7ecc0a43128f30ddd8b3f59a986fa058d82 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Fri, 21 Jan 2022 15:56:20 +0800 Subject: [PATCH 19/94] resolve comments --- .../TransitionHelper.AttachedProperty.cs | 33 ++++----------- .../Helpers/TransitionHelper.Logic.cs | 40 +++++++++---------- .../Helpers/TransitionHelper.cs | 8 ++-- ...Microsoft.Toolkit.Uwp.UI.Animations.csproj | 4 ++ 4 files changed, 34 insertions(+), 51 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs index 1123f1d7801..8d0a8586bff 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs @@ -13,9 +13,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// public sealed partial class TransitionHelper { - private const string IdPropertyName = "Id"; - private const string IsIgnoredPropertyName = "IsIgnored"; - /// /// Get the animation id of the UI element. /// @@ -38,7 +35,7 @@ public static void SetId(DependencyObject obj, string value) /// Two elements of the same id on different controls will be connected by animation. /// public static readonly DependencyProperty IdProperty = - DependencyProperty.RegisterAttached(IdPropertyName, typeof(string), typeof(TransitionHelper), null); + DependencyProperty.RegisterAttached("Id", typeof(string), typeof(TransitionHelper), null); /// /// Get the value indicating whether the UI element needs to be connected by animation. @@ -61,34 +58,20 @@ public static void SetIsIgnored(DependencyObject obj, bool value) /// IsIgnored is used to mark controls that do not need to be connected by animation, it will disappear/show independently. /// public static readonly DependencyProperty IsIgnoredProperty = - DependencyProperty.RegisterAttached(IsIgnoredPropertyName, typeof(bool), typeof(TransitionHelper), new PropertyMetadata(false)); + DependencyProperty.RegisterAttached("IsIgnored", typeof(bool), typeof(TransitionHelper), new PropertyMetadata(false)); private static IEnumerable GetAnimatedElements(UIElement targetElement, IEnumerable filters) { - if (targetElement == null) - { - return null; - } - - var elements = targetElement.FindDescendants().ToList() ?? new List(); - elements.Add(targetElement); - - return filters == null - ? elements.Where(element => GetId(element) != null).OfType() - : elements.Where(element => GetId(element) != null && filters.Contains(GetId(element))).OfType(); + return targetElement?.FindDescendantsOrSelf(SearchType.BreadthFirst) + .Where(element => GetId(element) is string id && (filters is null || filters.Contains(id))) + .DistinctBy(element => GetId(element)) + .OfType(); } private static IEnumerable GetIgnoredElements(UIElement targetElement) { - if (targetElement == null) - { - return null; - } - - var elements = targetElement.FindDescendants().ToList() ?? new List(); - elements.Add(targetElement); - - return elements.Where(element => GetIsIgnored(element)).OfType(); + return targetElement?.FindDescendantsOrSelf(SearchType.BreadthFirst) + .Where(element => GetIsIgnored(element)).OfType(); } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 10a47311b95..142e4aa1088 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -21,7 +21,7 @@ public sealed partial class TransitionHelper { private void UpdateSourceAnimatedElements() { - if (this.Source == null) + if (this.Source is null) { return; } @@ -37,7 +37,7 @@ private void UpdateSourceAnimatedElements() private void UpdateTargetAnimatedElements() { - if (this.Target == null) + if (this.Target is null) { return; } @@ -53,7 +53,7 @@ private void UpdateTargetAnimatedElements() private void ToggleVisualState(UIElement target, VisualStateToggleMethod method, bool isVisible) { - if (target == null) + if (target is null) { return; } @@ -79,17 +79,17 @@ private void ToggleVisualState(UIElement target, VisualStateToggleMethod method, this.targetAnimatedElements.ContainsKey(config.Id) ? this.targetAnimatedElements[config.Id] : null); } - private async Task AnimateFromSourceToTargetAsync(CancellationToken token) + private Task AnimateFromSourceToTargetAsync(CancellationToken token) { - await this.AnimateControlsAsync(false, token); + return this.AnimateControlsAsync(false, token); } - private async Task AnimateFromTargetToSourceAsync(CancellationToken token) + private Task AnimateFromTargetToSourceAsync(CancellationToken token) { - await this.AnimateControlsAsync(true, token); + return this.AnimateControlsAsync(true, token); } - private async Task AnimateControlsAsync(bool reversed, CancellationToken token) + private Task AnimateControlsAsync(bool reversed, CancellationToken token) { var duration = this.AnimationDuration; if (this._isInterruptedAnimation) @@ -103,14 +103,14 @@ private async Task AnimateControlsAsync(bool reversed, CancellationToken token) foreach (var item in this.AnimationConfigs) { (var source, var target) = this.GetPairElements(item); - if (source == null || target == null) + if (source is null || target is null) { - if (source != null) + if (source is not null) { sourceUnpairedElements.Add(source); } - if (target != null) + if (target is not null) { targetUnpairedElements.Add(target); } @@ -130,7 +130,7 @@ private async Task AnimateControlsAsync(bool reversed, CancellationToken token) animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Source, sourceUnpairedElements, reversed, token)); animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Target, targetUnpairedElements, !reversed, token)); - await Task.WhenAll(animationTasks); + return Task.WhenAll(animationTasks); } private void RestoreState(bool isTargetState) @@ -175,9 +175,8 @@ private void TargetControl_LayoutUpdated(object sender, object e) _ = this._updateTargetLayoutTaskSource.TrySetResult(null); } - private async Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan duration, AnimationConfig config, CancellationToken token) + private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan duration, AnimationConfig config, CancellationToken token) { - var animationTasks = new List(); var sourceBuilder = AnimationBuilder.Create(); var targetBuilder = AnimationBuilder.Create(); this.AnimateUIElementsTranslation(sourceBuilder, targetBuilder, source, target, duration); @@ -197,17 +196,14 @@ private async Task AnimateElementsAsync(UIElement source, UIElement target, Time break; } - animationTasks.Add(sourceBuilder.StartAsync(source, token)); - animationTasks.Add(targetBuilder.StartAsync(target, token)); - - await Task.WhenAll(animationTasks); + return Task.WhenAll(sourceBuilder.StartAsync(source, token), targetBuilder.StartAsync(target, token)); } - private async Task AnimateIgnoredOrUnpairedElementsAsync(UIElement parent, IEnumerable unpairedElements, bool isShow, CancellationToken token) + private Task AnimateIgnoredOrUnpairedElementsAsync(UIElement parent, IEnumerable unpairedElements, bool isShow, CancellationToken token) { - if (parent == null) + if (parent is null) { - return; + return Task.CompletedTask; } var animationTasks = new List(); @@ -243,7 +239,7 @@ private async Task AnimateIgnoredOrUnpairedElementsAsync(UIElement parent, IEnum } } - await Task.WhenAll(animationTasks); + return Task.WhenAll(animationTasks); } private void AnimateUIElementsTranslation(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, UIElement target, TimeSpan duration) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index f282301e232..678cac8419c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -35,14 +35,14 @@ public sealed partial class TransitionHelper /// A that completes when all animations have completed. public async Task AnimateAsync(bool forceUpdateAnimatedElements = false) { - if (this._animateCancellationTokenSource != null) + if (this._animateCancellationTokenSource is not null) { return; } this._animateCancellationTokenSource = new CancellationTokenSource(); - if (this._reverseCancellationTokenSource != null) + if (this._reverseCancellationTokenSource is not null) { if (!this._isInterruptedAnimation) { @@ -83,14 +83,14 @@ public async Task AnimateAsync(bool forceUpdateAnimatedElements = false) /// A that completes when all animations have completed. public async Task ReverseAsync(bool forceUpdateAnimatedElements = false) { - if (this._reverseCancellationTokenSource != null) + if (this._reverseCancellationTokenSource is not null) { return; } this._reverseCancellationTokenSource = new CancellationTokenSource(); - if (this._animateCancellationTokenSource != null) + if (this._animateCancellationTokenSource is not null) { if (!this._isInterruptedAnimation) { diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj b/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj index 130fdd806ef..f27ed3965a9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj @@ -21,6 +21,10 @@ UI;XAML;Animations;Composition;Connected;Implicit;Expressions;Extensions + + + + From e3fb3837dffea6b0eb3411592b246fb3bc5b0213 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Fri, 21 Jan 2022 18:41:23 +0800 Subject: [PATCH 20/94] Fix build error --- .../TransitionHelper.AttachedProperty.cs | 19 ++++++++++++++++--- ...Microsoft.Toolkit.Uwp.UI.Animations.csproj | 4 ---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs index 8d0a8586bff..612f765f2b3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs @@ -13,6 +13,19 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// public sealed partial class TransitionHelper { + private class AnimatedElementIdComparer : IEqualityComparer + { + public bool Equals(DependencyObject x, DependencyObject y) + { + return GetId(x) is string xId && GetId(y) is string yId && xId == yId; + } + + public int GetHashCode(DependencyObject obj) + { + return 0; + } + } + /// /// Get the animation id of the UI element. /// @@ -62,15 +75,15 @@ public static void SetIsIgnored(DependencyObject obj, bool value) private static IEnumerable GetAnimatedElements(UIElement targetElement, IEnumerable filters) { - return targetElement?.FindDescendantsOrSelf(SearchType.BreadthFirst) + return targetElement?.FindDescendantsOrSelf() .Where(element => GetId(element) is string id && (filters is null || filters.Contains(id))) - .DistinctBy(element => GetId(element)) + .Distinct(new AnimatedElementIdComparer()) .OfType(); } private static IEnumerable GetIgnoredElements(UIElement targetElement) { - return targetElement?.FindDescendantsOrSelf(SearchType.BreadthFirst) + return targetElement?.FindDescendantsOrSelf() .Where(element => GetIsIgnored(element)).OfType(); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj b/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj index f27ed3965a9..130fdd806ef 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj @@ -21,10 +21,6 @@ UI;XAML;Animations;Composition;Connected;Implicit;Expressions;Extensions - - - - From 03700be525aad7b329fda723fb78340ded377e2b Mon Sep 17 00:00:00 2001 From: hhchaos Date: Wed, 9 Feb 2022 17:14:50 +0800 Subject: [PATCH 21/94] Modify VisualStateToggleMethod logic --- .../Enums/VisualStateToggleMethod.cs | 4 +- .../Helpers/TransitionHelper.Logic.cs | 69 +++++++++++++------ .../Helpers/TransitionHelper.cs | 1 - 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/VisualStateToggleMethod.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/VisualStateToggleMethod.cs index 1b275f2e038..3eb116ba948 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/VisualStateToggleMethod.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/VisualStateToggleMethod.cs @@ -15,8 +15,8 @@ public enum VisualStateToggleMethod ByVisibility, /// - /// Change the visibility of UI elements by modifying the Opacity property. + /// Change the visibility of UI elements by modifying the IsVisible property of it's Visual. /// - ByOpacity + ByIsVisible } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 142e4aa1088..7b79044f770 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -63,14 +63,15 @@ private void ToggleVisualState(UIElement target, VisualStateToggleMethod method, case VisualStateToggleMethod.ByVisibility: target.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed; break; - case VisualStateToggleMethod.ByOpacity: + case VisualStateToggleMethod.ByIsVisible: var targetVisual = ElementCompositionPreview.GetElementVisual(target); targetVisual.IsVisible = isVisible; - target.IsHitTestVisible = isVisible; break; default: break; } + + target.IsHitTestVisible = isVisible; } private (UIElement, UIElement) GetPairElements(AnimationConfig config) @@ -142,12 +143,9 @@ private void RestoreState(bool isTargetState) private async Task InitStateAsync(bool forceUpdateAnimatedElements = false) { - this.ToggleVisualState(this.Source, this.SourceToggleMethod, true); - this.ToggleVisualState(this.Target, this.TargetToggleMethod, true); - if (this._needUpdateTargetLayout && this.TargetToggleMethod == VisualStateToggleMethod.ByVisibility) - { - await this.UpdateTargetLayoutAsync(); - } + await Task.WhenAll( + this.InitUIElementState(this.Source, this._needUpdateTargetLayout), + this.InitUIElementState(this.Target, this._needUpdateTargetLayout)); if (forceUpdateAnimatedElements) { @@ -156,23 +154,52 @@ private async Task InitStateAsync(bool forceUpdateAnimatedElements = false) } } - private async Task UpdateTargetLayoutAsync() + private Task InitUIElementState(FrameworkElement target, bool needUpdateLayout) { - this._updateTargetLayoutTaskSource = new TaskCompletionSource(); - this.ToggleVisualState(this.Target, VisualStateToggleMethod.ByOpacity, false); - this.Target.LayoutUpdated += this.TargetControl_LayoutUpdated; - this.Target.InvalidateArrange(); - this.Target.UpdateLayout(); - _ = await this._updateTargetLayoutTaskSource.Task; - this._updateTargetLayoutTaskSource = null; + var updateLayoutTask = Task.CompletedTask; + if (target is null) + { + return updateLayoutTask; + } + + target.IsHitTestVisible = false; + + if (target.Visibility == Visibility.Collapsed) + { + target.Visibility = Visibility.Visible; + if (needUpdateLayout) + { + updateLayoutTask = this.UpdateLayoutAsync(target); + } + } + + if (target.Opacity < 0.01) + { + target.Opacity = 1; + } + + var targetVisual = ElementCompositionPreview.GetElementVisual(target); + if (!targetVisual.IsVisible) + { + targetVisual.IsVisible = true; + } + + return updateLayoutTask; } - private void TargetControl_LayoutUpdated(object sender, object e) + private Task UpdateLayoutAsync(FrameworkElement target) { - this.Target.LayoutUpdated -= this.TargetControl_LayoutUpdated; - this.ToggleVisualState(this.Target, VisualStateToggleMethod.ByOpacity, true); - this._needUpdateTargetLayout = false; - _ = this._updateTargetLayoutTaskSource.TrySetResult(null); + var updateTargetLayoutTaskSource = new TaskCompletionSource(); + void OnTargetLayoutUpdated(object sender, object e) + { + target.LayoutUpdated -= OnTargetLayoutUpdated; + this._needUpdateTargetLayout = false; + _ = updateTargetLayoutTaskSource.TrySetResult(null); + } + + target.LayoutUpdated += OnTargetLayoutUpdated; + target.UpdateLayout(); + return updateTargetLayoutTaskSource.Task; } private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan duration, AnimationConfig config, CancellationToken token) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 678cac8419c..4ee3bb4914c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -24,7 +24,6 @@ public sealed partial class TransitionHelper private CancellationTokenSource _reverseCancellationTokenSource; private TaskCompletionSource _animateTaskSource; private TaskCompletionSource _reverseTaskSource; - private TaskCompletionSource _updateTargetLayoutTaskSource; private bool _needUpdateTargetLayout = false; private bool _isInterruptedAnimation = false; From b3aa3c1a79caa98e05e8037ede820af3e4228e72 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Thu, 10 Feb 2022 11:30:25 +0800 Subject: [PATCH 22/94] update some method name --- .../Helpers/TransitionHelper.Logic.cs | 16 +++++++++------- .../Helpers/TransitionHelper.Properties.cs | 1 + .../Helpers/TransitionHelper.cs | 5 +++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 7b79044f770..acce78e7303 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -141,11 +141,14 @@ private void RestoreState(bool isTargetState) this.ToggleVisualState(this.Target, this.TargetToggleMethod, isTargetState); } - private async Task InitStateAsync(bool forceUpdateAnimatedElements = false) + private async Task InitControlsStateAsync(bool forceUpdateAnimatedElements = false) { await Task.WhenAll( - this.InitUIElementState(this.Source, this._needUpdateTargetLayout), - this.InitUIElementState(this.Target, this._needUpdateTargetLayout)); + this.InitControlStateAsync(this.Source, this._needUpdateSourceLayout), + this.InitControlStateAsync(this.Target, this._needUpdateTargetLayout)); + + this._needUpdateSourceLayout = false; + this._needUpdateTargetLayout = false; if (forceUpdateAnimatedElements) { @@ -154,7 +157,7 @@ await Task.WhenAll( } } - private Task InitUIElementState(FrameworkElement target, bool needUpdateLayout) + private Task InitControlStateAsync(FrameworkElement target, bool needUpdateLayout) { var updateLayoutTask = Task.CompletedTask; if (target is null) @@ -169,7 +172,7 @@ private Task InitUIElementState(FrameworkElement target, bool needUpdateLayout) target.Visibility = Visibility.Visible; if (needUpdateLayout) { - updateLayoutTask = this.UpdateLayoutAsync(target); + updateLayoutTask = this.UpdateControlLayoutAsync(target); } } @@ -187,13 +190,12 @@ private Task InitUIElementState(FrameworkElement target, bool needUpdateLayout) return updateLayoutTask; } - private Task UpdateLayoutAsync(FrameworkElement target) + private Task UpdateControlLayoutAsync(FrameworkElement target) { var updateTargetLayoutTaskSource = new TaskCompletionSource(); void OnTargetLayoutUpdated(object sender, object e) { target.LayoutUpdated -= OnTargetLayoutUpdated; - this._needUpdateTargetLayout = false; _ = updateTargetLayoutTaskSource.TrySetResult(null); } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index c36f7a58261..fe5b5dc9e1a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -31,6 +31,7 @@ public FrameworkElement Source set { this._source = value; + this._needUpdateSourceLayout = true; this.IsTargetState = false; this.UpdateSourceAnimatedElements(); } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 4ee3bb4914c..fbe65f23add 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -24,6 +24,7 @@ public sealed partial class TransitionHelper private CancellationTokenSource _reverseCancellationTokenSource; private TaskCompletionSource _animateTaskSource; private TaskCompletionSource _reverseTaskSource; + private bool _needUpdateSourceLayout = false; private bool _needUpdateTargetLayout = false; private bool _isInterruptedAnimation = false; @@ -60,7 +61,7 @@ public async Task AnimateAsync(bool forceUpdateAnimatedElements = false) if (!this.IsTargetState && !this._animateCancellationTokenSource.IsCancellationRequested) { this._animateTaskSource = new TaskCompletionSource(); - await this.InitStateAsync(forceUpdateAnimatedElements); + await this.InitControlsStateAsync(forceUpdateAnimatedElements); await this.AnimateFromSourceToTargetAsync(this._animateCancellationTokenSource.Token); this.RestoreState(true); _ = this._animateTaskSource?.TrySetResult(null); @@ -108,7 +109,7 @@ public async Task ReverseAsync(bool forceUpdateAnimatedElements = false) if (this.IsTargetState && !this._reverseCancellationTokenSource.IsCancellationRequested) { this._reverseTaskSource = new TaskCompletionSource(); - await this.InitStateAsync(forceUpdateAnimatedElements); + await this.InitControlsStateAsync(forceUpdateAnimatedElements); await this.AnimateFromTargetToSourceAsync(this._reverseCancellationTokenSource.Token); this.RestoreState(false); _ = this._reverseTaskSource?.TrySetResult(null); From cfecd968aae4020aad36a65b51f6946775b50018 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Thu, 10 Feb 2022 15:47:10 +0800 Subject: [PATCH 23/94] Add a new sample for StartTransitionAction/ReverseTransitionAction --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 2 +- .../Actions/StartTransitionActionXaml.bind | 85 +++++ .../TransitionHelper/TransitionHelper.png | Bin 1246 -> 0 bytes .../TransitionHelperXaml.bind | 353 ++++++++---------- .../SamplePages/samples.json | 11 +- .../Helpers/TransitionHelper.Properties.cs | 10 + 6 files changed, 269 insertions(+), 192 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind delete mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelper.png diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index fca50cf0e69..d6f0fa76a12 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -300,7 +300,6 @@ --> - @@ -653,6 +652,7 @@ + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind new file mode 100644 index 00000000000..959ce8a17ce --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + Windows Community Toolkit + + + + + + + + + + Windows Community Toolkit + + + The Windows Community Toolkit is a collection of helper functions, custom controls, and app services. It simplifies and demonstrates common developer patterns when building experiences for Windows. + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelper.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelper.png deleted file mode 100644 index 9b4dbf5609bc368836c22b837f0c44f8bb8c77a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1246 zcmeAS@N?(olHy`uVBq!ia0y~yU{nCI4{)#n$t4^1Z39v)#ZI0f96(URk5@OP1(cIhgzE=g-VA>$%J5o9 zVS!sigA^lE77ND;!C}dnHTfIUjV0?R*X%C;<35Rh>DCV&oC3ywW<0ri?_>Ge)(<-* z4@$gs{U<-aTjl?>YloE89eTd~J@_$yYUl?Q4uQpg3kv?Er;6HeG9B?arvJ{RLE_|( zqjNa~lqL7?5N1pgc`RP8=FpS$=g~QqjNIp*e(bf^YLBhId{-)B3d7phsgt&Q)}1}9 zTr&UT>yq+!+jno>puiY)`Ls%Y+5Y{ffa2FbzBaO(ez8heFlrLR+H`xn#XUQIsXB2B zE!bM4X(#Sk=lSX7y!n+t?U&!p5odqjeIbb@L)Q4w3Xg{+(x%w(1AjK9@&g3PJz z6P%xN@1DM3R0l)u>5Y#jpQ-)Q#-Z!fu05cVya0i^eU1%c zpC@qi%IrVVuEMlsk-qB-v$Xp)8g{@SF@5X)Vq5jmG`lM28nLfB^Zy&(IsVOQg@yko z|Gd8Q-758OW#d(TRv!52Ke=Y|lfMn;AHB4+&RpNH>h`~f=WD*S{m?nXaV+=Do!>_f zv2@Hfk>7jp7^i^w%-XNrtw7qa&ajuEQ1-ESFsHzEAZIF!6At9el@{E2>a&XdTiO42 zqd0D>I>bCZ{EmH@VH2Y%6U!A2fdFL(hhB!^$|>GdZMzy+YFw(HW4)EnQF~4%u)t#Q MboFyt=akR{06O*Bi~s-t diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index d0825639ddf..bf0b3ed7c03 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -1,277 +1,250 @@ - + - - + + - + - - - + + + - + - + - - - - - + + - + - - + + - + Magic Magic is a cute 😺. - - - - - - + + - + - - + + - + Magic - + Magic is a cute 😺, but sometimes very naughty. - - - - - + + + + + - - - - diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index b025d63448f..195763e07b2 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -680,7 +680,16 @@ "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Animations/Helpers", "XamlCodeFile": "/SamplePages/TransitionHelper/TransitionHelperXaml.bind", "CodeFile": "/SamplePages/TransitionHelper/TransitionHelperCode.bind", - "Icon": "/SamplePages/TransitionHelper/TransitionHelper.png", + "Icon": "/Assets/Helpers.png", + "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/animations/TransitionHelper.md" + }, + { + "Name": "StartTransitionAction", + "Subcategory": "Actions", + "About": "An action that can trigger the target TransitionHelper instance.", + "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs", + "XamlCodeFile": "/SamplePages/Animations/Actions/StartTransitionActionXaml.bind", + "Icon": "/Assets/Helpers.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/animations/TransitionHelper.md" } ] diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index fe5b5dc9e1a..2435be427f1 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -30,6 +30,11 @@ public FrameworkElement Source set { + if (this._source == value) + { + return; + } + this._source = value; this._needUpdateSourceLayout = true; this.IsTargetState = false; @@ -49,6 +54,11 @@ public FrameworkElement Target set { + if (this._target == value) + { + return; + } + this._target = value; this._needUpdateTargetLayout = true; this.UpdateTargetAnimatedElements(); From fb661cfcc86d16ab68c6c785bb41eec66a964323 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 21 Mar 2022 00:20:13 +0800 Subject: [PATCH 24/94] Add Reset --- .../Helpers/TransitionHelper.Logic.cs | 77 +++++++++++++++--- .../Helpers/TransitionHelper.cs | 79 ++++++------------- 2 files changed, 91 insertions(+), 65 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index acce78e7303..30f6d754bb5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -80,16 +80,6 @@ private void ToggleVisualState(UIElement target, VisualStateToggleMethod method, this.targetAnimatedElements.ContainsKey(config.Id) ? this.targetAnimatedElements[config.Id] : null); } - private Task AnimateFromSourceToTargetAsync(CancellationToken token) - { - return this.AnimateControlsAsync(false, token); - } - - private Task AnimateFromTargetToSourceAsync(CancellationToken token) - { - return this.AnimateControlsAsync(true, token); - } - private Task AnimateControlsAsync(bool reversed, CancellationToken token) { var duration = this.AnimationDuration; @@ -134,6 +124,60 @@ private Task AnimateControlsAsync(bool reversed, CancellationToken token) return Task.WhenAll(animationTasks); } + private async Task AnimateControlsAsync(bool reversed, CancellationToken token, bool forceUpdateAnimatedElements) + { + var reversedCancellationTokenSource = + reversed ? this._animateCancellationTokenSource : this._reverseCancellationTokenSource; + var reversedTaskSource = + reversed ? this._animateTaskSource : this._reverseTaskSource; + + if (reversedCancellationTokenSource is not null) + { + if (this._isInterruptedAnimation) + { + this._isInterruptedAnimation = false; + await reversedTaskSource.Task; + } + else + { + reversedCancellationTokenSource?.Cancel(); + this._isInterruptedAnimation = true; + } + } + + if (!this._isInterruptedAnimation && IsTargetState != reversed) + { + return; + } + + if (token.IsCancellationRequested) + { + return; + } + + TaskCompletionSource currentTaskSource; + if (reversed) + { + currentTaskSource = this._reverseTaskSource = new TaskCompletionSource(); + } + else + { + currentTaskSource = this._animateTaskSource = new TaskCompletionSource(); + } + + await this.InitControlsStateAsync(forceUpdateAnimatedElements); + await this.AnimateControlsAsync(reversed, token); + if (token.IsCancellationRequested) + { + return; + } + + this.RestoreState(!reversed); + _ = currentTaskSource.TrySetResult(null); + + this._isInterruptedAnimation = false; + } + private void RestoreState(bool isTargetState) { this.IsTargetState = isTargetState; @@ -141,6 +185,19 @@ private void RestoreState(bool isTargetState) this.ToggleVisualState(this.Target, this.TargetToggleMethod, isTargetState); } + private void RestoreUIElements(IEnumerable animatedElements) + { + foreach (var animatedElement in animatedElements) + { + var visual = ElementCompositionPreview.GetElementVisual(animatedElement); + visual.Opacity = 1; + visual.Scale = Vector3.One; + var transformMatrix = visual.TransformMatrix; + transformMatrix.Translation = Vector3.Zero; + visual.TransformMatrix = transformMatrix; + } + } + private async Task InitControlsStateAsync(bool forceUpdateAnimatedElements = false) { await Task.WhenAll( diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index fbe65f23add..0ce135a1c5e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Windows.UI.Xaml; @@ -41,38 +42,7 @@ public async Task AnimateAsync(bool forceUpdateAnimatedElements = false) } this._animateCancellationTokenSource = new CancellationTokenSource(); - - if (this._reverseCancellationTokenSource is not null) - { - if (!this._isInterruptedAnimation) - { - this._reverseCancellationTokenSource?.Cancel(); - this._isInterruptedAnimation = true; - } - else - { - this._isInterruptedAnimation = false; - } - - _ = await this._reverseTaskSource.Task; - this._reverseTaskSource = null; - } - - if (!this.IsTargetState && !this._animateCancellationTokenSource.IsCancellationRequested) - { - this._animateTaskSource = new TaskCompletionSource(); - await this.InitControlsStateAsync(forceUpdateAnimatedElements); - await this.AnimateFromSourceToTargetAsync(this._animateCancellationTokenSource.Token); - this.RestoreState(true); - _ = this._animateTaskSource?.TrySetResult(null); - } - - this.IsTargetState = true; - if (!this._animateCancellationTokenSource.IsCancellationRequested) - { - this._isInterruptedAnimation = false; - } - + await AnimateControlsAsync(false, _animateCancellationTokenSource.Token, forceUpdateAnimatedElements); this._animateCancellationTokenSource = null; } @@ -89,39 +59,38 @@ public async Task ReverseAsync(bool forceUpdateAnimatedElements = false) } this._reverseCancellationTokenSource = new CancellationTokenSource(); + await AnimateControlsAsync(true, _reverseCancellationTokenSource.Token, forceUpdateAnimatedElements); + this._reverseCancellationTokenSource = null; + } + /// + /// Reset to initial state. + /// + public void Reset() + { + var needRestoreChildElements = this.IsTargetState; if (this._animateCancellationTokenSource is not null) { - if (!this._isInterruptedAnimation) - { - this._animateCancellationTokenSource?.Cancel(); - this._isInterruptedAnimation = true; - } - else - { - this._isInterruptedAnimation = false; - } - - _ = await this._animateTaskSource.Task; - this._animateTaskSource = null; + needRestoreChildElements = true; + this._animateCancellationTokenSource.Cancel(); + this._animateCancellationTokenSource = null; } - if (this.IsTargetState && !this._reverseCancellationTokenSource.IsCancellationRequested) + if (_reverseCancellationTokenSource is not null) { - this._reverseTaskSource = new TaskCompletionSource(); - await this.InitControlsStateAsync(forceUpdateAnimatedElements); - await this.AnimateFromTargetToSourceAsync(this._reverseCancellationTokenSource.Token); - this.RestoreState(false); - _ = this._reverseTaskSource?.TrySetResult(null); + needRestoreChildElements = true; + this._reverseCancellationTokenSource.Cancel(); + this._reverseCancellationTokenSource = null; } - this.IsTargetState = false; - if (!this._reverseCancellationTokenSource.IsCancellationRequested) + this._isInterruptedAnimation = false; + this.RestoreState(false); + + if (needRestoreChildElements) { - this._isInterruptedAnimation = false; + this.RestoreUIElements(this.sourceAnimatedElements.Values.Concat(GetIgnoredElements(Source))); + this.RestoreUIElements(this.targetAnimatedElements.Values.Concat(GetIgnoredElements(Target))); } - - this._reverseCancellationTokenSource = null; } } } From 6d77c8fc702527d8f9b29d00d16ddaaadddffaa2 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 21 Mar 2022 00:37:33 +0800 Subject: [PATCH 25/94] update method name --- .../Helpers/TransitionHelper.Logic.cs | 2 +- .../Helpers/TransitionHelper.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 30f6d754bb5..102bfa34af4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -124,7 +124,7 @@ private Task AnimateControlsAsync(bool reversed, CancellationToken token) return Task.WhenAll(animationTasks); } - private async Task AnimateControlsAsync(bool reversed, CancellationToken token, bool forceUpdateAnimatedElements) + private async Task StartInterruptibleAnimationsAsync(bool reversed, CancellationToken token, bool forceUpdateAnimatedElements) { var reversedCancellationTokenSource = reversed ? this._animateCancellationTokenSource : this._reverseCancellationTokenSource; diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 0ce135a1c5e..a137975a6ed 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -42,7 +42,7 @@ public async Task AnimateAsync(bool forceUpdateAnimatedElements = false) } this._animateCancellationTokenSource = new CancellationTokenSource(); - await AnimateControlsAsync(false, _animateCancellationTokenSource.Token, forceUpdateAnimatedElements); + await StartInterruptibleAnimationsAsync(false, _animateCancellationTokenSource.Token, forceUpdateAnimatedElements); this._animateCancellationTokenSource = null; } @@ -59,7 +59,7 @@ public async Task ReverseAsync(bool forceUpdateAnimatedElements = false) } this._reverseCancellationTokenSource = new CancellationTokenSource(); - await AnimateControlsAsync(true, _reverseCancellationTokenSource.Token, forceUpdateAnimatedElements); + await StartInterruptibleAnimationsAsync(true, _reverseCancellationTokenSource.Token, forceUpdateAnimatedElements); this._reverseCancellationTokenSource = null; } From e6ab5433f4d1a2dd11a53a48ab44f9534badbef3 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 21 Mar 2022 00:41:06 +0800 Subject: [PATCH 26/94] format --- .../Helpers/TransitionHelper.Logic.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 102bfa34af4..2bc6a2c7d5a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -93,7 +93,7 @@ private Task AnimateControlsAsync(bool reversed, CancellationToken token) var targetUnpairedElements = new List(); foreach (var item in this.AnimationConfigs) { - (var source, var target) = this.GetPairElements(item); + var (source, target) = this.GetPairElements(item); if (source is null || target is null) { if (source is not null) From afc23149d6e46ba25b82cb25a8421b14599c2da3 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 21 Mar 2022 12:00:06 +0800 Subject: [PATCH 27/94] Add OpacityAnimationMode --- .../TransitionHelperXaml.bind | 5 ++--- .../Enums/OpacityAnimationMode.cs | 22 +++++++++++++++++++ .../Helpers/AnimationConfig.cs | 13 ++++++++--- .../Helpers/TransitionHelper.Logic.cs | 9 +++++++- 4 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Enums/OpacityAnimationMode.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index bf0b3ed7c03..a09cbcaf7ba 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -12,9 +12,8 @@ - + OpacityMode="Normal" /> + diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/OpacityAnimationMode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/OpacityAnimationMode.cs new file mode 100644 index 00000000000..de3af3ec9d1 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/OpacityAnimationMode.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// Indicates the strategy for animating the opacity of a UI element. + /// + public enum OpacityAnimationMode + { + /// + /// Animating the opacity of a UI element using default speed. + /// + Normal, + + /// + /// Animating the opacity of a UI element using faster speed. + /// + Faster, + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs index f8cb6eb34f7..f93fe36544b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs @@ -10,13 +10,20 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations public class AnimationConfig { /// - /// Gets or sets id of UI element. + /// Gets or sets id of a UI element. /// public string Id { get; set; } /// - /// Gets or sets the scale strategy of UI element. + /// Gets or sets the scale strategy of a UI element. + /// The default value is . /// - public ScaleMode ScaleMode { get; set; } = ScaleMode.None; + public ScaleMode ScaleMode { get; set; } = ScaleMode.Scale; + + /// + /// Gets or sets the strategy for animating the opacity of a UI element. + /// The default value is . + /// + public OpacityAnimationMode OpacityMode { get; set; } = OpacityAnimationMode.Faster; } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 2bc6a2c7d5a..054b67eaa0f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -266,7 +266,13 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d var sourceBuilder = AnimationBuilder.Create(); var targetBuilder = AnimationBuilder.Create(); this.AnimateUIElementsTranslation(sourceBuilder, targetBuilder, source, target, duration); - this.AnimateUIElementsOpacity(sourceBuilder, targetBuilder, duration * 1 / 3); // Make opacity animation faster + TimeSpan opacityAnimationDuration = config.OpacityMode switch + { + OpacityAnimationMode.Normal => duration, + OpacityAnimationMode.Faster => duration * 1 / 3, + _ => duration + }; + this.AnimateUIElementsOpacity(sourceBuilder, targetBuilder, opacityAnimationDuration); switch (config.ScaleMode) { case ScaleMode.Scale: @@ -278,6 +284,7 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d case ScaleMode.ScaleY: this.AnimateUIElementsScaleY(sourceBuilder, targetBuilder, source, target, duration); break; + case ScaleMode.None: default: break; } From e90a16c86c98781d83ca584d190bf6b77f4af1a0 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 21 Mar 2022 12:15:08 +0800 Subject: [PATCH 28/94] Fix default value --- .../SamplePages/TransitionHelper/TransitionHelperXaml.bind | 6 ++++-- .../Helpers/AnimationConfig.cs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index a09cbcaf7ba..20b03a638d9 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -12,8 +12,10 @@ - + OpacityMode="Normal" + ScaleMode="Scale" /> + diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs index f93fe36544b..f290422a18a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs @@ -16,9 +16,9 @@ public class AnimationConfig /// /// Gets or sets the scale strategy of a UI element. - /// The default value is . + /// The default value is . /// - public ScaleMode ScaleMode { get; set; } = ScaleMode.Scale; + public ScaleMode ScaleMode { get; set; } = ScaleMode.None; /// /// Gets or sets the strategy for animating the opacity of a UI element. From 8826477655cbc7320fe3b42cdcc833a140753633 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Tue, 22 Mar 2022 17:58:51 +0800 Subject: [PATCH 29/94] update the logic of setter --- .../Helpers/TransitionHelper.Properties.cs | 13 ++++++++++++- .../Helpers/TransitionHelper.cs | 7 ++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 2435be427f1..656f36670a9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Numerics; using Windows.UI.Xaml; @@ -35,9 +36,13 @@ public FrameworkElement Source return; } + if(this._source is not null) + { + this.RestoreUIElements(this.sourceAnimatedElements.Values.Concat(GetIgnoredElements(this._source))); + } + this._source = value; this._needUpdateSourceLayout = true; - this.IsTargetState = false; this.UpdateSourceAnimatedElements(); } } @@ -59,8 +64,14 @@ public FrameworkElement Target return; } + if (this._target is not null) + { + this.RestoreUIElements(this.targetAnimatedElements.Values.Concat(GetIgnoredElements(this._target))); + } + this._target = value; this._needUpdateTargetLayout = true; + this.IsTargetState = false; this.UpdateTargetAnimatedElements(); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index a137975a6ed..3461d84037b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -49,7 +49,7 @@ public async Task AnimateAsync(bool forceUpdateAnimatedElements = false) /// /// Reverse animation, morphs from target control to source control. /// - /// Indicates whether to force the update of the child element list before the animation starts. + /// Indicates whether to force the update of child elements before the animation starts. /// A that completes when all animations have completed. public async Task ReverseAsync(bool forceUpdateAnimatedElements = false) { @@ -66,9 +66,10 @@ public async Task ReverseAsync(bool forceUpdateAnimatedElements = false) /// /// Reset to initial state. /// - public void Reset() + /// Indicates whether to force the reset of child elements. + public void Reset(bool forceRestoreChildElements = false) { - var needRestoreChildElements = this.IsTargetState; + var needRestoreChildElements = forceRestoreChildElements || this.IsTargetState; if (this._animateCancellationTokenSource is not null) { needRestoreChildElements = true; From bedbbfcf8ab688ecb1f5f8f265ad6b0ac28849ed Mon Sep 17 00:00:00 2001 From: hhchaos Date: Wed, 23 Mar 2022 14:37:09 +0800 Subject: [PATCH 30/94] Fix reset bug --- .../Helpers/TransitionHelper.Logic.cs | 27 +++++++++---------- .../Helpers/TransitionHelper.cs | 2 ++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 054b67eaa0f..94843d18a3b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -189,12 +189,11 @@ private void RestoreUIElements(IEnumerable animatedElements) { foreach (var animatedElement in animatedElements) { - var visual = ElementCompositionPreview.GetElementVisual(animatedElement); - visual.Opacity = 1; - visual.Scale = Vector3.One; - var transformMatrix = visual.TransformMatrix; - transformMatrix.Translation = Vector3.Zero; - visual.TransformMatrix = transformMatrix; + AnimationBuilder.Create() + .Translation(to: Vector3.Zero, duration: almostZeroDuration) + .Opacity(to: 1, duration: almostZeroDuration) + .Scale(to: Vector3.One, duration: almostZeroDuration) + .Start(animatedElement); } } @@ -339,7 +338,7 @@ private void AnimateUIElementsTranslation(AnimationBuilder sourceBuilder, Animat { if (this._isInterruptedAnimation) { - _ = sourceBuilder.Translation(to: Vector3.Zero, duration: TimeSpan.FromMilliseconds(1)); + _ = sourceBuilder.Translation(to: Vector3.Zero, duration: almostZeroDuration); _ = targetBuilder.Translation(to: Vector3.Zero, duration: duration); return; } @@ -347,13 +346,13 @@ private void AnimateUIElementsTranslation(AnimationBuilder sourceBuilder, Animat var diff = target.TransformToVisual(source).TransformPoint(default); _ = sourceBuilder.Translation().TimedKeyFrames( build: b => b - .KeyFrame(duration - TimeSpan.FromMilliseconds(1), new Vector3((float)diff.X, (float)diff.Y, 0)) + .KeyFrame(duration - almostZeroDuration, new Vector3((float)diff.X, (float)diff.Y, 0)) .KeyFrame(duration, Vector3.Zero)); _ = targetBuilder.Translation().TimedKeyFrames( delayBehavior: AnimationDelayBehavior.SetInitialValueBeforeDelay, build: b => b .KeyFrame(TimeSpan.Zero, new Vector3((float)-diff.X, (float)-diff.Y, 0)) - .KeyFrame(duration - TimeSpan.FromMilliseconds(1), Vector3.Zero) + .KeyFrame(duration - almostZeroDuration, Vector3.Zero) .KeyFrame(duration, Vector3.Zero)); } @@ -383,20 +382,20 @@ private void AnimateUIElementsScale(AnimationBuilder sourceBuilder, AnimationBui { if (this._isInterruptedAnimation) { - _ = sourceBuilder.Scale(to: Vector3.One, duration: TimeSpan.FromMilliseconds(1)); + _ = sourceBuilder.Scale(to: Vector3.One, duration: almostZeroDuration); _ = targetBuilder.Scale(to: Vector3.One, duration: duration); return; } _ = sourceBuilder.Scale().TimedKeyFrames( build: b => b - .KeyFrame(duration - TimeSpan.FromMilliseconds(1), targetScale) + .KeyFrame(duration - almostZeroDuration, targetScale) .KeyFrame(duration, Vector3.One)); _ = targetBuilder.Scale().TimedKeyFrames( delayBehavior: AnimationDelayBehavior.SetInitialValueBeforeDelay, build: b => b .KeyFrame(TimeSpan.Zero, new Vector3(1 / targetScale.X, 1 / targetScale.Y, 1)) - .KeyFrame(duration - TimeSpan.FromMilliseconds(1), Vector3.One) + .KeyFrame(duration - almostZeroDuration, Vector3.One) .KeyFrame(duration, Vector3.One)); } @@ -404,8 +403,8 @@ private void AnimateUIElementsOpacity(AnimationBuilder sourceBuilder, AnimationB { if (this._isInterruptedAnimation) { - _ = sourceBuilder.Opacity(to: 0, duration: TimeSpan.FromMilliseconds(1)); - _ = targetBuilder.Opacity(to: 1, duration: TimeSpan.FromMilliseconds(1)); + _ = sourceBuilder.Opacity(to: 0, duration: almostZeroDuration); + _ = targetBuilder.Opacity(to: 1, duration: almostZeroDuration); return; } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 3461d84037b..9e78dcb5595 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -20,6 +21,7 @@ public sealed partial class TransitionHelper private readonly Dictionary sourceAnimatedElements = new(); private readonly Dictionary targetAnimatedElements = new(); private readonly double interruptedAnimationReverseDurationRatio = 0.7; + private readonly TimeSpan almostZeroDuration = TimeSpan.FromMilliseconds(1); private CancellationTokenSource _animateCancellationTokenSource; private CancellationTokenSource _reverseCancellationTokenSource; From 300a10ee9f5501cd3809f2551a9cd51bfedb91b5 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Thu, 14 Apr 2022 14:07:43 +0800 Subject: [PATCH 31/94] update RestoreUIElements --- .../Helpers/TransitionHelper.Logic.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 94843d18a3b..7ac6a116deb 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -189,11 +189,11 @@ private void RestoreUIElements(IEnumerable animatedElements) { foreach (var animatedElement in animatedElements) { - AnimationBuilder.Create() - .Translation(to: Vector3.Zero, duration: almostZeroDuration) - .Opacity(to: 1, duration: almostZeroDuration) - .Scale(to: Vector3.One, duration: almostZeroDuration) - .Start(animatedElement); + var visual = ElementCompositionPreview.GetElementVisual(animatedElement); + visual.Opacity = 1; + visual.Scale = Vector3.One; + ElementCompositionPreview.SetIsTranslationEnabled(animatedElement, true); + visual.Properties.InsertVector3("Translation", Vector3.Zero); } } From 2d4d589c18b1f97835100ed4f101d8046df743e4 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Thu, 14 Apr 2022 17:31:26 +0800 Subject: [PATCH 32/94] Add IsHitTestVisibleWhenAnimating --- .../Helpers/TransitionHelper.Logic.cs | 4 ++-- .../Helpers/TransitionHelper.Properties.cs | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 7ac6a116deb..94c471c94d3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -189,10 +189,10 @@ private void RestoreUIElements(IEnumerable animatedElements) { foreach (var animatedElement in animatedElements) { + ElementCompositionPreview.SetIsTranslationEnabled(animatedElement, true); var visual = ElementCompositionPreview.GetElementVisual(animatedElement); visual.Opacity = 1; visual.Scale = Vector3.One; - ElementCompositionPreview.SetIsTranslationEnabled(animatedElement, true); visual.Properties.InsertVector3("Translation", Vector3.Zero); } } @@ -221,7 +221,7 @@ private Task InitControlStateAsync(FrameworkElement target, bool needUpdateLayou return updateLayoutTask; } - target.IsHitTestVisible = false; + target.IsHitTestVisible = IsHitTestVisibleWhenAnimating; if (target.Visibility == Visibility.Collapsed) { diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 656f36670a9..b89caa19b82 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -99,6 +99,11 @@ public List AnimationConfigs /// public bool IsTargetState { get; private set; } = false; + /// + /// Gets or sets a value indicating whether the contained area of the source or target control can return true values for hit testing when animating. + /// + public bool IsHitTestVisibleWhenAnimating { get; set; } = false; + /// /// Gets or sets the method of changing the visibility of the source control. /// From b30645459f864b24a7e4b9824fe1c18e87fe3926 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Thu, 14 Apr 2022 19:14:35 +0800 Subject: [PATCH 33/94] Add DefaultAnimationConfig and update logic --- .../TransitionHelperXaml.bind | 4 +- .../TransitionHelper.AttachedProperty.cs | 37 ++++---- .../Helpers/TransitionHelper.Logic.cs | 94 +++++++++---------- .../Helpers/TransitionHelper.Properties.cs | 43 ++++----- .../Helpers/TransitionHelper.cs | 21 +++-- 5 files changed, 97 insertions(+), 102 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index 20b03a638d9..0d2cc7d9372 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -16,10 +16,8 @@ ScaleMode="Scale" /> - - @@ -178,7 +176,7 @@ + ani:TransitionHelper.IsIndependent="True"> public sealed partial class TransitionHelper { - private class AnimatedElementIdComparer : IEqualityComparer + private class AnimatedElementComparer : IEqualityComparer { public bool Equals(DependencyObject x, DependencyObject y) { + if (GetIsIndependent(x) || GetIsIndependent(y)) + { + return false; + } + return GetId(x) is string xId && GetId(y) is string yId && xId == yId; } @@ -51,40 +56,34 @@ public static void SetId(DependencyObject obj, string value) DependencyProperty.RegisterAttached("Id", typeof(string), typeof(TransitionHelper), null); /// - /// Get the value indicating whether the UI element needs to be connected by animation. + /// Get the value indicating whether the UI element is animated independently. /// /// A bool value indicating whether the UI element needs to be connected by animation. - public static bool GetIsIgnored(DependencyObject obj) + public static bool GetIsIndependent(DependencyObject obj) { - return (bool)obj.GetValue(IsIgnoredProperty); + return (bool)obj.GetValue(IsIndependentProperty); } /// - /// Set the value indicating whether the UI element needs to be connected by animation. + /// Set the value indicating whether the UI element is animated independently. /// - public static void SetIsIgnored(DependencyObject obj, bool value) + public static void SetIsIndependent(DependencyObject obj, bool value) { - obj.SetValue(IsIgnoredProperty, value); + obj.SetValue(IsIndependentProperty, value); } /// - /// IsIgnored is used to mark controls that do not need to be connected by animation, it will disappear/show independently. + /// IsIndependent is used to mark controls that do not need to be connected by animation, it will disappear/show independently. /// - public static readonly DependencyProperty IsIgnoredProperty = - DependencyProperty.RegisterAttached("IsIgnored", typeof(bool), typeof(TransitionHelper), new PropertyMetadata(false)); + public static readonly DependencyProperty IsIndependentProperty = + DependencyProperty.RegisterAttached("IsIndependent", typeof(bool), typeof(TransitionHelper), new PropertyMetadata(false)); - private static IEnumerable GetAnimatedElements(UIElement targetElement, IEnumerable filters) + private static IEnumerable GetAnimatedElements(UIElement targetElement) { return targetElement?.FindDescendantsOrSelf() - .Where(element => GetId(element) is string id && (filters is null || filters.Contains(id))) - .Distinct(new AnimatedElementIdComparer()) + .Where(element => GetId(element) is not null || GetIsIndependent(element)) + .Distinct(new AnimatedElementComparer()) .OfType(); } - - private static IEnumerable GetIgnoredElements(UIElement targetElement) - { - return targetElement?.FindDescendantsOrSelf() - .Where(element => GetIsIgnored(element)).OfType(); - } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 94c471c94d3..0d38b882155 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -26,28 +26,39 @@ private void UpdateSourceAnimatedElements() return; } - this.sourceAnimatedElements.Clear(); - var filters = this.AnimationConfigs.Select(config => config.Id); + UpdateAnimatedElements(this.Source, this.sourceConnectedAnimatedElements, this.sourceIndependentAnimatedElements); + } - foreach (var item in GetAnimatedElements(this.Source, filters)) + private void UpdateTargetAnimatedElements() + { + if (this.Target is null) { - this.sourceAnimatedElements[GetId(item)] = item; + return; } + + UpdateAnimatedElements(this.Target, this.targetConnectedAnimatedElements, this.targetIndependentAnimatedElements); } - private void UpdateTargetAnimatedElements() + private void UpdateAnimatedElements(UIElement parent, IDictionary connectedAnimatedElements, IList independentAnimatedElements) { - if (this.Target is null) + if (this.Source is null) { return; } - this.targetAnimatedElements.Clear(); - var filters = this.AnimationConfigs.Select(config => config.Id); + connectedAnimatedElements.Clear(); + independentAnimatedElements.Clear(); - foreach (var item in GetAnimatedElements(this.Target, filters)) + foreach (var item in GetAnimatedElements(parent)) { - this.targetAnimatedElements[GetId(item)] = item; + if (GetId(item) is string id) + { + connectedAnimatedElements[id] = item; + } + else + { + independentAnimatedElements.Add(item); + } } } @@ -74,12 +85,6 @@ private void ToggleVisualState(UIElement target, VisualStateToggleMethod method, target.IsHitTestVisible = isVisible; } - private (UIElement, UIElement) GetPairElements(AnimationConfig config) - { - return (this.sourceAnimatedElements.ContainsKey(config.Id) ? this.sourceAnimatedElements[config.Id] : null, - this.targetAnimatedElements.ContainsKey(config.Id) ? this.targetAnimatedElements[config.Id] : null); - } - private Task AnimateControlsAsync(bool reversed, CancellationToken token) { var duration = this.AnimationDuration; @@ -89,37 +94,33 @@ private Task AnimateControlsAsync(bool reversed, CancellationToken token) } var animationTasks = new List(); - var sourceUnpairedElements = new List(); - var targetUnpairedElements = new List(); - foreach (var item in this.AnimationConfigs) + var sourceUnpairedElements = this.sourceConnectedAnimatedElements + .Where(item => !this.targetConnectedAnimatedElements.ContainsKey(item.Key)) + .Select(item => item.Value); + var targetUnpairedElements = this.targetConnectedAnimatedElements + .Where(item => !this.sourceConnectedAnimatedElements.ContainsKey(item.Key)) + .Select(item => item.Value); + + var pairedElementKeys = this.sourceConnectedAnimatedElements + .Where(item => this.targetConnectedAnimatedElements.ContainsKey(item.Key)) + .Select(item => item.Key); + foreach (var key in pairedElementKeys) { - var (source, target) = this.GetPairElements(item); - if (source is null || target is null) - { - if (source is not null) - { - sourceUnpairedElements.Add(source); - } - - if (target is not null) - { - targetUnpairedElements.Add(target); - } - - continue; - } + var source = this.sourceConnectedAnimatedElements[key]; + var target = this.targetConnectedAnimatedElements[key]; + var animationConfig = this.AnimationConfigs.FirstOrDefault(config => config.Id == key) ?? this.DefaultAnimationConfig; animationTasks.Add( this.AnimateElementsAsync( reversed ? target : source, reversed ? source : target, duration, - item, + animationConfig, token)); } - animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Source, sourceUnpairedElements, reversed, token)); - animationTasks.Add(this.AnimateIgnoredOrUnpairedElementsAsync(this.Target, targetUnpairedElements, !reversed, token)); + animationTasks.Add(this.AnimateIndependentElementsAsync(this.sourceIndependentAnimatedElements.Concat(sourceUnpairedElements), reversed, token)); + animationTasks.Add(this.AnimateIndependentElementsAsync(this.targetIndependentAnimatedElements.Concat(targetUnpairedElements), !reversed, token)); return Task.WhenAll(animationTasks); } @@ -291,30 +292,29 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d return Task.WhenAll(sourceBuilder.StartAsync(source, token), targetBuilder.StartAsync(target, token)); } - private Task AnimateIgnoredOrUnpairedElementsAsync(UIElement parent, IEnumerable unpairedElements, bool isShow, CancellationToken token) + private Task AnimateIndependentElementsAsync(IEnumerable independentElements, bool isShow, CancellationToken token) { - if (parent is null) + if (!independentElements.Any()) { return Task.CompletedTask; } var animationTasks = new List(); - var ignoredElements = GetIgnoredElements(parent); - var duration = isShow ? this.IgnoredOrUnpairedElementShowDuration : this.IgnoredOrUnpairedElementHideDuration; - var delay = isShow ? this.IgnoredOrUnpairedElementShowDelayDuration : TimeSpan.Zero; + var duration = isShow ? this.IndependentElementShowDuration : this.IndependentElementHideDuration; + var delay = isShow ? this.IndependentElementShowDelayDuration : TimeSpan.Zero; if (this._isInterruptedAnimation) { duration *= this.interruptedAnimationReverseDurationRatio; delay *= this.interruptedAnimationReverseDurationRatio; } - foreach (var item in ignoredElements.Concat(unpairedElements)) + foreach (var item in independentElements) { - if (this.IgnoredOrUnpairedElementHideTranslation != Vector3.Zero) + if (this.IndependentElementHideTranslation != Vector3.Zero) { animationTasks.Add(AnimationBuilder.Create().Translation( - from: this._isInterruptedAnimation ? null : (isShow ? this.IgnoredOrUnpairedElementHideTranslation : Vector3.Zero), - to: isShow ? Vector3.Zero : this.IgnoredOrUnpairedElementHideTranslation, + from: this._isInterruptedAnimation ? null : (isShow ? this.IndependentElementHideTranslation : Vector3.Zero), + to: isShow ? Vector3.Zero : this.IndependentElementHideTranslation, duration: duration, delay: delay).StartAsync(item, token)); } @@ -327,7 +327,7 @@ private Task AnimateIgnoredOrUnpairedElementsAsync(UIElement parent, IEnumerable if (isShow) { - delay += this.IgnoredOrUnpairedElementShowStepDuration; + delay += this.IndependentElementShowStepDuration; } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index b89caa19b82..18d78c0117b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -17,7 +17,6 @@ public sealed partial class TransitionHelper { private FrameworkElement _source; private FrameworkElement _target; - private List _animationConfigs = new(); /// /// Gets or sets the source control. @@ -38,7 +37,7 @@ public FrameworkElement Source if(this._source is not null) { - this.RestoreUIElements(this.sourceAnimatedElements.Values.Concat(GetIgnoredElements(this._source))); + this.RestoreUIElements(this.SourceAnimatedElements); } this._source = value; @@ -66,7 +65,7 @@ public FrameworkElement Target if (this._target is not null) { - this.RestoreUIElements(this.targetAnimatedElements.Values.Concat(GetIgnoredElements(this._target))); + this.RestoreUIElements(this.TargetAnimatedElements); } this._target = value; @@ -79,20 +78,12 @@ public FrameworkElement Target /// /// Gets or sets the collection of animation configurations of UI elements that need to be connected by animation. /// - public List AnimationConfigs - { - get - { - return this._animationConfigs; - } + public List AnimationConfigs { get; set; } = new(); - set - { - this._animationConfigs = value; - this.UpdateSourceAnimatedElements(); - this.UpdateTargetAnimatedElements(); - } - } + /// + /// Gets or sets the default animation configuration. + /// + public AnimationConfig DefaultAnimationConfig { get; set; } = new (); /// /// Gets a value indicating whether the source control has been morphed to the target control. @@ -120,28 +111,28 @@ public List AnimationConfigs public TimeSpan AnimationDuration { get; set; } = TimeSpan.FromMilliseconds(600); /// - /// Gets or sets the duration of the show animation for ignored or unpaired UI elements. + /// Gets or sets the duration of the show animation for independent or unpaired UI elements. /// - public TimeSpan IgnoredOrUnpairedElementShowDuration { get; set; } = TimeSpan.FromMilliseconds(200); + public TimeSpan IndependentElementShowDuration { get; set; } = TimeSpan.FromMilliseconds(200); /// - /// Gets or sets the delay of the show animation for ignored or unpaired UI elements. + /// Gets or sets the delay of the show animation for independent or unpaired UI elements. /// - public TimeSpan IgnoredOrUnpairedElementShowDelayDuration { get; set; } = TimeSpan.FromMilliseconds(300); + public TimeSpan IndependentElementShowDelayDuration { get; set; } = TimeSpan.FromMilliseconds(300); /// - /// Gets or sets the duration of the interval between the show animations for ignored or unpaired UI elements. + /// Gets or sets the duration of the interval between the show animations for independent or unpaired UI elements. /// - public TimeSpan IgnoredOrUnpairedElementShowStepDuration { get; set; } = TimeSpan.FromMilliseconds(50); + public TimeSpan IndependentElementShowStepDuration { get; set; } = TimeSpan.FromMilliseconds(50); /// - /// Gets or sets the duration of the hide animation for ignored or unpaired UI elements. + /// Gets or sets the duration of the hide animation for independent or unpaired UI elements. /// - public TimeSpan IgnoredOrUnpairedElementHideDuration { get; set; } = TimeSpan.FromMilliseconds(100); + public TimeSpan IndependentElementHideDuration { get; set; } = TimeSpan.FromMilliseconds(100); /// - /// Gets or sets the translation of the hide animation for ignored or unpaired UI elements. + /// Gets or sets the translation of the hide animation for independent or unpaired UI elements. /// - public Vector3 IgnoredOrUnpairedElementHideTranslation { get; set; } = new Vector3(0, 20, 0); + public Vector3 IndependentElementHideTranslation { get; set; } = new Vector3(0, 20, 0); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 9e78dcb5595..ff3863254fe 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -18,8 +18,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations [ContentProperty(Name = nameof(AnimationConfigs))] public sealed partial class TransitionHelper { - private readonly Dictionary sourceAnimatedElements = new(); - private readonly Dictionary targetAnimatedElements = new(); + private readonly Dictionary sourceConnectedAnimatedElements = new(); + private readonly Dictionary targetConnectedAnimatedElements = new(); + private readonly List sourceIndependentAnimatedElements = new(); + private readonly List targetIndependentAnimatedElements = new(); private readonly double interruptedAnimationReverseDurationRatio = 0.7; private readonly TimeSpan almostZeroDuration = TimeSpan.FromMilliseconds(1); @@ -31,6 +33,10 @@ public sealed partial class TransitionHelper private bool _needUpdateTargetLayout = false; private bool _isInterruptedAnimation = false; + private IEnumerable SourceAnimatedElements => this.sourceConnectedAnimatedElements.Values.Concat(this.sourceIndependentAnimatedElements); + + private IEnumerable TargetAnimatedElements => this.targetConnectedAnimatedElements.Values.Concat(this.targetIndependentAnimatedElements); + /// /// Morphs from source control to target control. /// @@ -66,10 +72,11 @@ public async Task ReverseAsync(bool forceUpdateAnimatedElements = false) } /// - /// Reset to initial state. + /// Reset to initial or target state. /// + /// Indicates whether to reset to initial state. default value is True, if it is False, it will be reset to target state. /// Indicates whether to force the reset of child elements. - public void Reset(bool forceRestoreChildElements = false) + public void Reset(bool toInitialState = true, bool forceRestoreChildElements = false) { var needRestoreChildElements = forceRestoreChildElements || this.IsTargetState; if (this._animateCancellationTokenSource is not null) @@ -87,12 +94,12 @@ public void Reset(bool forceRestoreChildElements = false) } this._isInterruptedAnimation = false; - this.RestoreState(false); + this.RestoreState(!toInitialState); if (needRestoreChildElements) { - this.RestoreUIElements(this.sourceAnimatedElements.Values.Concat(GetIgnoredElements(Source))); - this.RestoreUIElements(this.targetAnimatedElements.Values.Concat(GetIgnoredElements(Target))); + this.RestoreUIElements(this.SourceAnimatedElements); + this.RestoreUIElements(this.TargetAnimatedElements); } } } From 034298e350a42fa8304741bcfbd943622d1a81ca Mon Sep 17 00:00:00 2001 From: hhchaos Date: Thu, 14 Apr 2022 21:47:16 +0800 Subject: [PATCH 34/94] Add NormalizedCenterPoint for AnimationConfig --- .../Actions/StartTransitionActionXaml.bind | 1 - .../Helpers/AnimationConfig.cs | 10 ++++++++++ .../Helpers/TransitionHelper.Logic.cs | 17 ++++++++++------- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind index 959ce8a17ce..dfa590f829b 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind @@ -13,7 +13,6 @@ ScaleMode="Scale" /> - @@ -20,6 +22,14 @@ public class AnimationConfig /// public ScaleMode ScaleMode { get; set; } = ScaleMode.None; + /// + /// Gets or sets the center point used to calculate the element's translation or scale when animating. + /// Value is normalized with respect to the size of the animated element. + /// For example, a value of (0.0, 0.5) means that this point is at the leftmost point of the element horizontally and the center of the element vertically. + /// The default value is . + /// + public Vector2 NormalizedCenterPoint { get; set; } = Vector2.Zero; + /// /// Gets or sets the strategy for animating the opacity of a UI element. /// The default value is . diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 0d38b882155..3a3d13e1f9c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -100,7 +100,6 @@ private Task AnimateControlsAsync(bool reversed, CancellationToken token) var targetUnpairedElements = this.targetConnectedAnimatedElements .Where(item => !this.sourceConnectedAnimatedElements.ContainsKey(item.Key)) .Select(item => item.Value); - var pairedElementKeys = this.sourceConnectedAnimatedElements .Where(item => this.targetConnectedAnimatedElements.ContainsKey(item.Key)) .Select(item => item.Key); @@ -109,7 +108,6 @@ private Task AnimateControlsAsync(bool reversed, CancellationToken token) var source = this.sourceConnectedAnimatedElements[key]; var target = this.targetConnectedAnimatedElements[key]; var animationConfig = this.AnimationConfigs.FirstOrDefault(config => config.Id == key) ?? this.DefaultAnimationConfig; - animationTasks.Add( this.AnimateElementsAsync( reversed ? target : source, @@ -265,7 +263,10 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d { var sourceBuilder = AnimationBuilder.Create(); var targetBuilder = AnimationBuilder.Create(); - this.AnimateUIElementsTranslation(sourceBuilder, targetBuilder, source, target, duration); + + ElementCompositionPreview.GetElementVisual(source).CenterPoint = new Vector3(source.ActualSize * config.NormalizedCenterPoint, 0); + ElementCompositionPreview.GetElementVisual(target).CenterPoint = new Vector3(target.ActualSize * config.NormalizedCenterPoint, 0); + this.AnimateUIElementsTranslation(sourceBuilder, targetBuilder, source, target, config.NormalizedCenterPoint, duration); TimeSpan opacityAnimationDuration = config.OpacityMode switch { OpacityAnimationMode.Normal => duration, @@ -334,7 +335,7 @@ private Task AnimateIndependentElementsAsync(IEnumerable independentE return Task.WhenAll(animationTasks); } - private void AnimateUIElementsTranslation(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, UIElement target, TimeSpan duration) + private void AnimateUIElementsTranslation(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, UIElement target, Vector2 normalizedCenterPoint, TimeSpan duration) { if (this._isInterruptedAnimation) { @@ -343,15 +344,17 @@ private void AnimateUIElementsTranslation(AnimationBuilder sourceBuilder, Animat return; } - var diff = target.TransformToVisual(source).TransformPoint(default); + var sourceNormalizedCenterPoint = source.ActualSize * normalizedCenterPoint; + var targetNormalizedCenterPoint = target.ActualSize * normalizedCenterPoint; + var diff = target.TransformToVisual(source).TransformPoint(default).ToVector2() - sourceNormalizedCenterPoint + targetNormalizedCenterPoint; _ = sourceBuilder.Translation().TimedKeyFrames( build: b => b - .KeyFrame(duration - almostZeroDuration, new Vector3((float)diff.X, (float)diff.Y, 0)) + .KeyFrame(duration - almostZeroDuration, new Vector3(diff, 0)) .KeyFrame(duration, Vector3.Zero)); _ = targetBuilder.Translation().TimedKeyFrames( delayBehavior: AnimationDelayBehavior.SetInitialValueBeforeDelay, build: b => b - .KeyFrame(TimeSpan.Zero, new Vector3((float)-diff.X, (float)-diff.Y, 0)) + .KeyFrame(TimeSpan.Zero, new Vector3(-diff, 0)) .KeyFrame(duration - almostZeroDuration, Vector3.Zero) .KeyFrame(duration, Vector3.Zero)); } From bb0c7b8e0f1dad1862bf8b9bcbcdec6804c3f6ee Mon Sep 17 00:00:00 2001 From: hhchaos Date: Fri, 15 Apr 2022 11:35:21 +0800 Subject: [PATCH 35/94] Add EasingType and EasingMode for AnimationConfig --- .../Helpers/AnimationConfig.cs | 13 ++ .../Helpers/TransitionHelper.Logic.cs | 146 +++++++++++++++--- .../Helpers/TransitionHelper.Properties.cs | 27 +++- 3 files changed, 159 insertions(+), 27 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs index 8d8f644a0a8..30aa5825990 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Numerics; +using Windows.UI.Xaml.Media.Animation; namespace Microsoft.Toolkit.Uwp.UI.Animations { @@ -35,5 +36,17 @@ public class AnimationConfig /// The default value is . /// public OpacityAnimationMode OpacityMode { get; set; } = OpacityAnimationMode.Faster; + + /// + /// Gets or sets the easing function type for animation. + /// The default value is . + /// + public EasingType EasingType { get; set; } = EasingType.Default; + + /// + /// Gets or sets the easing function mode for animation. + /// The default value is . + /// + public EasingMode EasingMode { get; set; } = EasingMode.EaseInOut; } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 3a3d13e1f9c..60d4962d5d6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -11,6 +11,7 @@ using Windows.UI.Composition; using Windows.UI.Xaml; using Windows.UI.Xaml.Hosting; +using Windows.UI.Xaml.Media.Animation; namespace Microsoft.Toolkit.Uwp.UI.Animations { @@ -117,8 +118,20 @@ private Task AnimateControlsAsync(bool reversed, CancellationToken token) token)); } - animationTasks.Add(this.AnimateIndependentElementsAsync(this.sourceIndependentAnimatedElements.Concat(sourceUnpairedElements), reversed, token)); - animationTasks.Add(this.AnimateIndependentElementsAsync(this.targetIndependentAnimatedElements.Concat(targetUnpairedElements), !reversed, token)); + animationTasks.Add( + this.AnimateIndependentElementsAsync( + this.sourceIndependentAnimatedElements.Concat(sourceUnpairedElements), + reversed, + token, + IndependentElementEasingType, + IndependentElementEasingMode)); + animationTasks.Add( + this.AnimateIndependentElementsAsync( + this.targetIndependentAnimatedElements.Concat(targetUnpairedElements), + !reversed, + token, + IndependentElementEasingType, + IndependentElementEasingMode)); return Task.WhenAll(animationTasks); } @@ -266,24 +279,58 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d ElementCompositionPreview.GetElementVisual(source).CenterPoint = new Vector3(source.ActualSize * config.NormalizedCenterPoint, 0); ElementCompositionPreview.GetElementVisual(target).CenterPoint = new Vector3(target.ActualSize * config.NormalizedCenterPoint, 0); - this.AnimateUIElementsTranslation(sourceBuilder, targetBuilder, source, target, config.NormalizedCenterPoint, duration); + this.AnimateUIElementsTranslation( + sourceBuilder, + targetBuilder, + source, + target, + config.NormalizedCenterPoint, + duration, + config.EasingType, + config.EasingMode); TimeSpan opacityAnimationDuration = config.OpacityMode switch { OpacityAnimationMode.Normal => duration, OpacityAnimationMode.Faster => duration * 1 / 3, _ => duration }; - this.AnimateUIElementsOpacity(sourceBuilder, targetBuilder, opacityAnimationDuration); + this.AnimateUIElementsOpacity( + sourceBuilder, + targetBuilder, + opacityAnimationDuration, + config.EasingType, + config.EasingMode); switch (config.ScaleMode) { case ScaleMode.Scale: - this.AnimateUIElementsScale(sourceBuilder, targetBuilder, source, target, duration); + this.AnimateUIElementsScale( + sourceBuilder, + targetBuilder, + source, + target, + duration, + config.EasingType, + config.EasingMode); break; case ScaleMode.ScaleX: - this.AnimateUIElementsScaleX(sourceBuilder, targetBuilder, source, target, duration); + this.AnimateUIElementsScaleX( + sourceBuilder, + targetBuilder, + source, + target, + duration, + config.EasingType, + config.EasingMode); break; case ScaleMode.ScaleY: - this.AnimateUIElementsScaleY(sourceBuilder, targetBuilder, source, target, duration); + this.AnimateUIElementsScaleY( + sourceBuilder, + targetBuilder, + source, + target, + duration, + config.EasingType, + config.EasingMode); break; case ScaleMode.None: default: @@ -293,7 +340,12 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d return Task.WhenAll(sourceBuilder.StartAsync(source, token), targetBuilder.StartAsync(target, token)); } - private Task AnimateIndependentElementsAsync(IEnumerable independentElements, bool isShow, CancellationToken token) + private Task AnimateIndependentElementsAsync( + IEnumerable independentElements, + bool isShow, + CancellationToken token, + EasingType easingType, + EasingMode easingMode) { if (!independentElements.Any()) { @@ -317,6 +369,8 @@ private Task AnimateIndependentElementsAsync(IEnumerable independentE from: this._isInterruptedAnimation ? null : (isShow ? this.IndependentElementHideTranslation : Vector3.Zero), to: isShow ? Vector3.Zero : this.IndependentElementHideTranslation, duration: duration, + easingType: easingType, + easingMode: easingMode, delay: delay).StartAsync(item, token)); } @@ -324,6 +378,8 @@ private Task AnimateIndependentElementsAsync(IEnumerable independentE from: this._isInterruptedAnimation ? null : (isShow ? 0 : 1), to: isShow ? 1 : 0, duration: duration, + easingType: easingType, + easingMode: easingMode, delay: delay).StartAsync(item, token)); if (isShow) @@ -335,12 +391,20 @@ private Task AnimateIndependentElementsAsync(IEnumerable independentE return Task.WhenAll(animationTasks); } - private void AnimateUIElementsTranslation(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, UIElement target, Vector2 normalizedCenterPoint, TimeSpan duration) + private void AnimateUIElementsTranslation( + AnimationBuilder sourceBuilder, + AnimationBuilder targetBuilder, + UIElement source, + UIElement target, + Vector2 normalizedCenterPoint, + TimeSpan duration, + EasingType easingType, + EasingMode easingMode) { if (this._isInterruptedAnimation) { _ = sourceBuilder.Translation(to: Vector3.Zero, duration: almostZeroDuration); - _ = targetBuilder.Translation(to: Vector3.Zero, duration: duration); + _ = targetBuilder.Translation(to: Vector3.Zero, duration: duration, easingType: easingType, easingMode: easingMode); return; } @@ -349,60 +413,92 @@ private void AnimateUIElementsTranslation(AnimationBuilder sourceBuilder, Animat var diff = target.TransformToVisual(source).TransformPoint(default).ToVector2() - sourceNormalizedCenterPoint + targetNormalizedCenterPoint; _ = sourceBuilder.Translation().TimedKeyFrames( build: b => b - .KeyFrame(duration - almostZeroDuration, new Vector3(diff, 0)) + .KeyFrame(duration - almostZeroDuration, new Vector3(diff, 0), easingType, easingMode) .KeyFrame(duration, Vector3.Zero)); _ = targetBuilder.Translation().TimedKeyFrames( delayBehavior: AnimationDelayBehavior.SetInitialValueBeforeDelay, build: b => b .KeyFrame(TimeSpan.Zero, new Vector3(-diff, 0)) - .KeyFrame(duration - almostZeroDuration, Vector3.Zero) + .KeyFrame(duration - almostZeroDuration, Vector3.Zero, easingType, easingMode) .KeyFrame(duration, Vector3.Zero)); } - private void AnimateUIElementsScale(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, UIElement target, TimeSpan duration) + private void AnimateUIElementsScale( + AnimationBuilder sourceBuilder, + AnimationBuilder targetBuilder, + UIElement source, + UIElement target, + TimeSpan duration, + EasingType easingType, + EasingMode easingMode) { var scaleX = target.ActualSize.X / source.ActualSize.X; var scaleY = target.ActualSize.Y / source.ActualSize.Y; var scale = new Vector3((float)scaleX, (float)scaleY, 1); - this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration); + this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } - private void AnimateUIElementsScaleX(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, UIElement target, TimeSpan duration) + private void AnimateUIElementsScaleX( + AnimationBuilder sourceBuilder, + AnimationBuilder targetBuilder, + UIElement source, + UIElement target, + TimeSpan duration, + EasingType easingType, + EasingMode easingMode) { var scaleX = target.ActualSize.X / source.ActualSize.X; var scale = new Vector3((float)scaleX, (float)scaleX, 1); - this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration); + this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } - private void AnimateUIElementsScaleY(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, UIElement target, TimeSpan duration) + private void AnimateUIElementsScaleY( + AnimationBuilder sourceBuilder, + AnimationBuilder targetBuilder, + UIElement source, + UIElement target, + TimeSpan duration, + EasingType easingType, + EasingMode easingMode) { var scaleY = target.ActualSize.Y / source.ActualSize.Y; var scale = new Vector3((float)scaleY, (float)scaleY, 1); - this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration); + this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } - private void AnimateUIElementsScale(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, Vector3 targetScale, TimeSpan duration) + private void AnimateUIElementsScale( + AnimationBuilder sourceBuilder, + AnimationBuilder targetBuilder, + Vector3 targetScale, + TimeSpan duration, + EasingType easingType, + EasingMode easingMode) { if (this._isInterruptedAnimation) { _ = sourceBuilder.Scale(to: Vector3.One, duration: almostZeroDuration); - _ = targetBuilder.Scale(to: Vector3.One, duration: duration); + _ = targetBuilder.Scale(to: Vector3.One, duration: duration, easingType: easingType, easingMode: easingMode); return; } _ = sourceBuilder.Scale().TimedKeyFrames( build: b => b - .KeyFrame(duration - almostZeroDuration, targetScale) + .KeyFrame(duration - almostZeroDuration, targetScale, easingType, easingMode) .KeyFrame(duration, Vector3.One)); _ = targetBuilder.Scale().TimedKeyFrames( delayBehavior: AnimationDelayBehavior.SetInitialValueBeforeDelay, build: b => b .KeyFrame(TimeSpan.Zero, new Vector3(1 / targetScale.X, 1 / targetScale.Y, 1)) - .KeyFrame(duration - almostZeroDuration, Vector3.One) + .KeyFrame(duration - almostZeroDuration, Vector3.One, easingType, easingMode) .KeyFrame(duration, Vector3.One)); } - private void AnimateUIElementsOpacity(AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, TimeSpan duration) + private void AnimateUIElementsOpacity( + AnimationBuilder sourceBuilder, + AnimationBuilder targetBuilder, + TimeSpan duration, + EasingType easingType, + EasingMode easingMode) { if (this._isInterruptedAnimation) { @@ -411,8 +507,8 @@ private void AnimateUIElementsOpacity(AnimationBuilder sourceBuilder, AnimationB return; } - _ = sourceBuilder.Opacity(to: 0, duration: duration); - _ = targetBuilder.Opacity(to: 1, duration: duration); + _ = sourceBuilder.Opacity(to: 0, duration: duration, easingType: easingType, easingMode: easingMode); + _ = targetBuilder.Opacity(to: 1, duration: duration, easingType: easingType, easingMode: easingMode); } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 18d78c0117b..f5b8bb79b81 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Numerics; using Windows.UI.Xaml; +using Windows.UI.Xaml.Media.Animation; namespace Microsoft.Toolkit.Uwp.UI.Animations { @@ -35,7 +36,7 @@ public FrameworkElement Source return; } - if(this._source is not null) + if (this._source is not null) { this.RestoreUIElements(this.SourceAnimatedElements); } @@ -83,56 +84,78 @@ public FrameworkElement Target /// /// Gets or sets the default animation configuration. /// - public AnimationConfig DefaultAnimationConfig { get; set; } = new (); + public AnimationConfig DefaultAnimationConfig { get; set; } = new(); /// /// Gets a value indicating whether the source control has been morphed to the target control. + /// The default value is false. /// public bool IsTargetState { get; private set; } = false; /// /// Gets or sets a value indicating whether the contained area of the source or target control can return true values for hit testing when animating. + /// The default value is false. /// public bool IsHitTestVisibleWhenAnimating { get; set; } = false; /// /// Gets or sets the method of changing the visibility of the source control. + /// The default value is . /// public VisualStateToggleMethod SourceToggleMethod { get; set; } = VisualStateToggleMethod.ByVisibility; /// /// Gets or sets the method of changing the visibility of the target control. + /// The default value is . /// public VisualStateToggleMethod TargetToggleMethod { get; set; } = VisualStateToggleMethod.ByVisibility; /// /// Gets or sets the duration of the connected animation between two UI elements. + /// The default value is 600ms. /// public TimeSpan AnimationDuration { get; set; } = TimeSpan.FromMilliseconds(600); /// /// Gets or sets the duration of the show animation for independent or unpaired UI elements. + /// The default value is 200ms. /// public TimeSpan IndependentElementShowDuration { get; set; } = TimeSpan.FromMilliseconds(200); /// /// Gets or sets the delay of the show animation for independent or unpaired UI elements. + /// The default value is 300ms. /// public TimeSpan IndependentElementShowDelayDuration { get; set; } = TimeSpan.FromMilliseconds(300); /// /// Gets or sets the duration of the interval between the show animations for independent or unpaired UI elements. + /// The default value is 50ms. /// public TimeSpan IndependentElementShowStepDuration { get; set; } = TimeSpan.FromMilliseconds(50); /// /// Gets or sets the duration of the hide animation for independent or unpaired UI elements. + /// The default value is 100ms. /// public TimeSpan IndependentElementHideDuration { get; set; } = TimeSpan.FromMilliseconds(100); /// /// Gets or sets the translation of the hide animation for independent or unpaired UI elements. + /// The default value is Vector3(0, 20, 0). /// public Vector3 IndependentElementHideTranslation { get; set; } = new Vector3(0, 20, 0); + + /// + /// Gets or sets the easing function type for animation of independent or unpaired UI elements. + /// The default value is . + /// + public EasingType IndependentElementEasingType { get; set; } = EasingType.Default; + + /// + /// Gets or sets the easing function mode for animation of independent or unpaired UI elements. + /// The default value is . + /// + public EasingMode IndependentElementEasingMode { get; set; } = EasingMode.EaseInOut; } } From e5ed6207f0ab9bd61e711d5827c9b42c4f9ff5f4 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Fri, 15 Apr 2022 14:35:40 +0800 Subject: [PATCH 36/94] Change Vector2/Vector3 to Point for xaml --- .../Helpers/AnimationConfig.cs | 5 +- .../Helpers/TransitionHelper.Logic.cs | 46 +++++++++++-------- .../Helpers/TransitionHelper.Properties.cs | 5 +- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs index 30aa5825990..148af33579c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Numerics; +using Windows.Foundation; using Windows.UI.Xaml.Media.Animation; namespace Microsoft.Toolkit.Uwp.UI.Animations @@ -27,9 +28,9 @@ public class AnimationConfig /// Gets or sets the center point used to calculate the element's translation or scale when animating. /// Value is normalized with respect to the size of the animated element. /// For example, a value of (0.0, 0.5) means that this point is at the leftmost point of the element horizontally and the center of the element vertically. - /// The default value is . + /// The default value is (0, 0). /// - public Vector2 NormalizedCenterPoint { get; set; } = Vector2.Zero; + public Point NormalizedCenterPoint { get; set; } = default; /// /// Gets or sets the strategy for animating the opacity of a UI element. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 60d4962d5d6..fe2890fa17a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -277,14 +277,14 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d var sourceBuilder = AnimationBuilder.Create(); var targetBuilder = AnimationBuilder.Create(); - ElementCompositionPreview.GetElementVisual(source).CenterPoint = new Vector3(source.ActualSize * config.NormalizedCenterPoint, 0); - ElementCompositionPreview.GetElementVisual(target).CenterPoint = new Vector3(target.ActualSize * config.NormalizedCenterPoint, 0); + ElementCompositionPreview.GetElementVisual(source).CenterPoint = new Vector3(source.ActualSize * config.NormalizedCenterPoint.ToVector2(), 0); + ElementCompositionPreview.GetElementVisual(target).CenterPoint = new Vector3(target.ActualSize * config.NormalizedCenterPoint.ToVector2(), 0); this.AnimateUIElementsTranslation( sourceBuilder, targetBuilder, source, target, - config.NormalizedCenterPoint, + config.NormalizedCenterPoint.ToVector2(), duration, config.EasingType, config.EasingMode); @@ -363,24 +363,32 @@ private Task AnimateIndependentElementsAsync( foreach (var item in independentElements) { - if (this.IndependentElementHideTranslation != Vector3.Zero) + if (this.IndependentElementHideTranslation != default) { - animationTasks.Add(AnimationBuilder.Create().Translation( - from: this._isInterruptedAnimation ? null : (isShow ? this.IndependentElementHideTranslation : Vector3.Zero), - to: isShow ? Vector3.Zero : this.IndependentElementHideTranslation, + animationTasks.Add( + AnimationBuilder + .Create() + .Translation( + from: this._isInterruptedAnimation ? null : (isShow ? this.IndependentElementHideTranslation.ToVector3() : Vector3.Zero), + to: isShow ? Vector3.Zero : this.IndependentElementHideTranslation.ToVector3(), + duration: duration, + easingType: easingType, + easingMode: easingMode, + delay: delay) + .StartAsync(item, token)); + } + + animationTasks.Add( + AnimationBuilder + .Create() + .Opacity( + from: this._isInterruptedAnimation ? null : (isShow ? 0 : 1), + to: isShow ? 1 : 0, duration: duration, easingType: easingType, easingMode: easingMode, - delay: delay).StartAsync(item, token)); - } - - animationTasks.Add(AnimationBuilder.Create().Opacity( - from: this._isInterruptedAnimation ? null : (isShow ? 0 : 1), - to: isShow ? 1 : 0, - duration: duration, - easingType: easingType, - easingMode: easingMode, - delay: delay).StartAsync(item, token)); + delay: delay) + .StartAsync(item, token)); if (isShow) { @@ -507,8 +515,8 @@ private void AnimateUIElementsOpacity( return; } - _ = sourceBuilder.Opacity(to: 0, duration: duration, easingType: easingType, easingMode: easingMode); - _ = targetBuilder.Opacity(to: 1, duration: duration, easingType: easingType, easingMode: easingMode); + _ = sourceBuilder.Opacity(from: 1, to: 0, duration: duration, easingType: easingType, easingMode: easingMode); + _ = targetBuilder.Opacity(from: 0, to: 1, duration: duration, easingType: easingType, easingMode: easingMode); } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index f5b8bb79b81..75579d863b6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Media.Animation; @@ -142,9 +143,9 @@ public FrameworkElement Target /// /// Gets or sets the translation of the hide animation for independent or unpaired UI elements. - /// The default value is Vector3(0, 20, 0). + /// The default value is Point(0, 20). /// - public Vector3 IndependentElementHideTranslation { get; set; } = new Vector3(0, 20, 0); + public Point IndependentElementHideTranslation { get; set; } = new (0, 20); /// /// Gets or sets the easing function type for animation of independent or unpaired UI elements. From 7dde461cbcbd5649b7178bd22bdcc1577adec0fb Mon Sep 17 00:00:00 2001 From: hhchaos Date: Fri, 15 Apr 2022 14:39:30 +0800 Subject: [PATCH 37/94] remove useless using --- .../Helpers/AnimationConfig.cs | 1 - .../Helpers/TransitionHelper.Properties.cs | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs index 148af33579c..a62fa801e1f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Numerics; using Windows.Foundation; using Windows.UI.Xaml.Media.Animation; diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 75579d863b6..27363ed8867 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Numerics; using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Media.Animation; @@ -143,7 +141,7 @@ public FrameworkElement Target /// /// Gets or sets the translation of the hide animation for independent or unpaired UI elements. - /// The default value is Point(0, 20). + /// The default value is (0, 20). /// public Point IndependentElementHideTranslation { get; set; } = new (0, 20); From 4852783084d57356899bfd03b829f1b00fbbf2fb Mon Sep 17 00:00:00 2001 From: hhchaos Date: Fri, 15 Apr 2022 16:16:31 +0800 Subject: [PATCH 38/94] Update --- .../Helpers/TransitionHelper.Logic.cs | 32 +++---- .../Helpers/TransitionHelper.cs | 93 +++++++++++++++---- .../Animations/StartTransitionAction.cs | 2 +- 3 files changed, 89 insertions(+), 38 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index fe2890fa17a..3513219b025 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -138,25 +138,6 @@ private Task AnimateControlsAsync(bool reversed, CancellationToken token) private async Task StartInterruptibleAnimationsAsync(bool reversed, CancellationToken token, bool forceUpdateAnimatedElements) { - var reversedCancellationTokenSource = - reversed ? this._animateCancellationTokenSource : this._reverseCancellationTokenSource; - var reversedTaskSource = - reversed ? this._animateTaskSource : this._reverseTaskSource; - - if (reversedCancellationTokenSource is not null) - { - if (this._isInterruptedAnimation) - { - this._isInterruptedAnimation = false; - await reversedTaskSource.Task; - } - else - { - reversedCancellationTokenSource?.Cancel(); - this._isInterruptedAnimation = true; - } - } - if (!this._isInterruptedAnimation && IsTargetState != reversed) { return; @@ -184,17 +165,26 @@ private async Task StartInterruptibleAnimationsAsync(bool reversed, Cancellation return; } - this.RestoreState(!reversed); + this.RestoreState(!reversed, false); _ = currentTaskSource.TrySetResult(null); this._isInterruptedAnimation = false; } - private void RestoreState(bool isTargetState) + private void RestoreState(bool isTargetState, bool restoreAllChildElements) { this.IsTargetState = isTargetState; this.ToggleVisualState(this.Source, this.SourceToggleMethod, !isTargetState); this.ToggleVisualState(this.Target, this.TargetToggleMethod, isTargetState); + if (restoreAllChildElements) + { + this.RestoreUIElements(this.SourceAnimatedElements); + this.RestoreUIElements(this.TargetAnimatedElements); + } + else + { + this.RestoreUIElements(isTargetState ? this.SourceAnimatedElements : this.TargetAnimatedElements); + } } private void RestoreUIElements(IEnumerable animatedElements) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index ff3863254fe..0f340d0a1c6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -37,37 +37,108 @@ public sealed partial class TransitionHelper private IEnumerable TargetAnimatedElements => this.targetConnectedAnimatedElements.Values.Concat(this.targetIndependentAnimatedElements); + /// + /// Morphs from source control to target control. + /// + /// A that completes when all animations have completed. + public Task StartAsync() + { + return StartAsync(CancellationToken.None, false); + } + /// /// Morphs from source control to target control. /// /// Indicates whether to force the update of the child element list before the animation starts. /// A that completes when all animations have completed. - public async Task AnimateAsync(bool forceUpdateAnimatedElements = false) + public Task StartAsync(bool forceUpdateAnimatedElements) + { + return StartAsync(CancellationToken.None, forceUpdateAnimatedElements); + } + + /// + /// Morphs from source control to target control. + /// + /// The cancellation token to stop animations while they're running. + /// Indicates whether to force the update of the child element list before the animation starts. + /// A that completes when all animations have completed. + public async Task StartAsync(CancellationToken token, bool forceUpdateAnimatedElements) { if (this._animateCancellationTokenSource is not null) { return; } + if (this._reverseCancellationTokenSource is not null) + { + if (this._isInterruptedAnimation) + { + this._isInterruptedAnimation = false; + await this._reverseTaskSource.Task; + } + else + { + this._reverseCancellationTokenSource.Cancel(); + this._isInterruptedAnimation = true; + } + } + this._animateCancellationTokenSource = new CancellationTokenSource(); - await StartInterruptibleAnimationsAsync(false, _animateCancellationTokenSource.Token, forceUpdateAnimatedElements); + var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, _animateCancellationTokenSource.Token); + await StartInterruptibleAnimationsAsync(false, linkedTokenSource.Token, forceUpdateAnimatedElements); this._animateCancellationTokenSource = null; } + /// + /// Reverse animation, morphs from target control to source control. + /// + /// A that completes when all animations have completed. + public Task ReverseAsync() + { + return ReverseAsync(CancellationToken.None, false); + } + /// /// Reverse animation, morphs from target control to source control. /// /// Indicates whether to force the update of child elements before the animation starts. /// A that completes when all animations have completed. - public async Task ReverseAsync(bool forceUpdateAnimatedElements = false) + public Task ReverseAsync(bool forceUpdateAnimatedElements) + { + return ReverseAsync(CancellationToken.None, forceUpdateAnimatedElements); + } + + /// + /// Reverse animation, morphs from target control to source control. + /// + /// The cancellation token to stop animations while they're running. + /// Indicates whether to force the update of child elements before the animation starts. + /// A that completes when all animations have completed. + public async Task ReverseAsync(CancellationToken token, bool forceUpdateAnimatedElements) { if (this._reverseCancellationTokenSource is not null) { return; } + if (this._animateCancellationTokenSource is not null) + { + if (this._isInterruptedAnimation) + { + this._isInterruptedAnimation = false; + await this._animateTaskSource.Task; + } + else + { + this._animateCancellationTokenSource.Cancel(); + this._isInterruptedAnimation = true; + } + } + + this._reverseCancellationTokenSource = new CancellationTokenSource(); + var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, _reverseCancellationTokenSource.Token); this._reverseCancellationTokenSource = new CancellationTokenSource(); - await StartInterruptibleAnimationsAsync(true, _reverseCancellationTokenSource.Token, forceUpdateAnimatedElements); + await StartInterruptibleAnimationsAsync(true, linkedTokenSource.Token, forceUpdateAnimatedElements); this._reverseCancellationTokenSource = null; } @@ -75,32 +146,22 @@ public async Task ReverseAsync(bool forceUpdateAnimatedElements = false) /// Reset to initial or target state. /// /// Indicates whether to reset to initial state. default value is True, if it is False, it will be reset to target state. - /// Indicates whether to force the reset of child elements. - public void Reset(bool toInitialState = true, bool forceRestoreChildElements = false) + public void Reset(bool toInitialState = true) { - var needRestoreChildElements = forceRestoreChildElements || this.IsTargetState; if (this._animateCancellationTokenSource is not null) { - needRestoreChildElements = true; this._animateCancellationTokenSource.Cancel(); this._animateCancellationTokenSource = null; } if (_reverseCancellationTokenSource is not null) { - needRestoreChildElements = true; this._reverseCancellationTokenSource.Cancel(); this._reverseCancellationTokenSource = null; } this._isInterruptedAnimation = false; - this.RestoreState(!toInitialState); - - if (needRestoreChildElements) - { - this.RestoreUIElements(this.SourceAnimatedElements); - this.RestoreUIElements(this.TargetAnimatedElements); - } + this.RestoreState(!toInitialState, true); } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs index 3530cf17d5d..4e7bffe29c7 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartTransitionAction.cs @@ -109,7 +109,7 @@ public object Execute(object sender, object parameter) this.Transition.Source = this.Source; this.Transition.Target = this.Target; - _ = this.Transition.AnimateAsync(); + _ = this.Transition.StartAsync(); return null; } From 487e0a225e380d91879716ed6424179f20c3da75 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Fri, 15 Apr 2022 17:09:32 +0800 Subject: [PATCH 39/94] Fix a bug --- .../Helpers/TransitionHelper.Logic.cs | 15 +++++++++++---- .../Helpers/TransitionHelper.cs | 5 ++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 3513219b025..b8a5aafc10d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -148,25 +148,32 @@ private async Task StartInterruptibleAnimationsAsync(bool reversed, Cancellation return; } - TaskCompletionSource currentTaskSource; + TaskCompletionSource currentTaskSource; if (reversed) { - currentTaskSource = this._reverseTaskSource = new TaskCompletionSource(); + currentTaskSource = this._reverseTaskSource = new(); } else { - currentTaskSource = this._animateTaskSource = new TaskCompletionSource(); + currentTaskSource = this._animateTaskSource = new(); } await this.InitControlsStateAsync(forceUpdateAnimatedElements); + if (token.IsCancellationRequested) + { + _ = currentTaskSource.TrySetResult(false); + return; + } + await this.AnimateControlsAsync(reversed, token); if (token.IsCancellationRequested) { + _ = currentTaskSource.TrySetResult(false); return; } this.RestoreState(!reversed, false); - _ = currentTaskSource.TrySetResult(null); + _ = currentTaskSource.TrySetResult(true); this._isInterruptedAnimation = false; } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 0f340d0a1c6..6ea8636454f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -27,8 +27,8 @@ public sealed partial class TransitionHelper private CancellationTokenSource _animateCancellationTokenSource; private CancellationTokenSource _reverseCancellationTokenSource; - private TaskCompletionSource _animateTaskSource; - private TaskCompletionSource _reverseTaskSource; + private TaskCompletionSource _animateTaskSource; + private TaskCompletionSource _reverseTaskSource; private bool _needUpdateSourceLayout = false; private bool _needUpdateTargetLayout = false; private bool _isInterruptedAnimation = false; @@ -137,7 +137,6 @@ public async Task ReverseAsync(CancellationToken token, bool forceUpdateAnimated this._reverseCancellationTokenSource = new CancellationTokenSource(); var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, _reverseCancellationTokenSource.Token); - this._reverseCancellationTokenSource = new CancellationTokenSource(); await StartInterruptibleAnimationsAsync(true, linkedTokenSource.Token, forceUpdateAnimatedElements); this._reverseCancellationTokenSource = null; } From ab8fc7c0295274d28405cfa1f6d0d853e0efb22a Mon Sep 17 00:00:00 2001 From: hhchaos Date: Mon, 23 May 2022 17:07:23 +0800 Subject: [PATCH 40/94] update transition strategy. --- .../TransitionHelperXaml.bind | 34 +++----------- ...cityAnimationMode.cs => TransitionMode.cs} | 10 ++-- .../Helpers/AnimationConfig.cs | 6 +-- .../Helpers/TransitionHelper.Logic.cs | 47 ++++++++++++------- .../Helpers/TransitionHelper.Properties.cs | 10 ++-- 5 files changed, 51 insertions(+), 56 deletions(-) rename Microsoft.Toolkit.Uwp.UI.Animations/Enums/{OpacityAnimationMode.cs => TransitionMode.cs} (60%) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index 0d2cc7d9372..386db237629 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -6,15 +6,13 @@ xmlns:interactions="using:Microsoft.Xaml.Interactions.Core" xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:media="using:Microsoft.Toolkit.Uwp.UI.Media" - xmlns:ui="using:Microsoft.Toolkit.Uwp.UI" mc:Ignorable="d"> @@ -30,14 +28,8 @@ - - - - + Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" + CornerRadius="4"/> - - - - + Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" + CornerRadius="8"/> @@ -145,14 +131,8 @@ VerticalAlignment="Center" Visibility="Collapsed"> - - - - + Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" + CornerRadius="8"/> diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/OpacityAnimationMode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/TransitionMode.cs similarity index 60% rename from Microsoft.Toolkit.Uwp.UI.Animations/Enums/OpacityAnimationMode.cs rename to Microsoft.Toolkit.Uwp.UI.Animations/Enums/TransitionMode.cs index de3af3ec9d1..eae5c102f84 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/OpacityAnimationMode.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/TransitionMode.cs @@ -5,18 +5,18 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations { /// - /// Indicates the strategy for animating the opacity of a UI element. + /// Indicates the transition strategy between controls. /// - public enum OpacityAnimationMode + public enum TransitionMode { /// - /// Animating the opacity of a UI element using default speed. + /// The default transition strategy. /// Normal, /// - /// Animating the opacity of a UI element using faster speed. + /// The transition strategy for image or other UI elements that require smoother transitions. /// - Faster, + Image, } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs index a62fa801e1f..7c91739f84a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs @@ -32,10 +32,10 @@ public class AnimationConfig public Point NormalizedCenterPoint { get; set; } = default; /// - /// Gets or sets the strategy for animating the opacity of a UI element. - /// The default value is . + /// Gets or sets the transition strategy. + /// The default value is . /// - public OpacityAnimationMode OpacityMode { get; set; } = OpacityAnimationMode.Faster; + public TransitionMode TransitionMode { get; set; } = TransitionMode.Normal; /// /// Gets or sets the easing function type for animation. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index b8a5aafc10d..0384a98282b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -88,7 +88,7 @@ private void ToggleVisualState(UIElement target, VisualStateToggleMethod method, private Task AnimateControlsAsync(bool reversed, CancellationToken token) { - var duration = this.AnimationDuration; + var duration = this.Duration; if (this._isInterruptedAnimation) { duration *= this.interruptedAnimationReverseDurationRatio; @@ -285,18 +285,11 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d duration, config.EasingType, config.EasingMode); - TimeSpan opacityAnimationDuration = config.OpacityMode switch - { - OpacityAnimationMode.Normal => duration, - OpacityAnimationMode.Faster => duration * 1 / 3, - _ => duration - }; this.AnimateUIElementsOpacity( sourceBuilder, targetBuilder, - opacityAnimationDuration, - config.EasingType, - config.EasingMode); + duration, + config.TransitionMode); switch (config.ScaleMode) { case ScaleMode.Scale: @@ -351,7 +344,7 @@ private Task AnimateIndependentElementsAsync( var animationTasks = new List(); var duration = isShow ? this.IndependentElementShowDuration : this.IndependentElementHideDuration; - var delay = isShow ? this.IndependentElementShowDelayDuration : TimeSpan.Zero; + var delay = isShow ? this.IndependentElementShowDelay : TimeSpan.Zero; if (this._isInterruptedAnimation) { duration *= this.interruptedAnimationReverseDurationRatio; @@ -389,7 +382,7 @@ private Task AnimateIndependentElementsAsync( if (isShow) { - delay += this.IndependentElementShowStepDuration; + delay += this.IndependentElementShowInterval; } } @@ -502,8 +495,7 @@ private void AnimateUIElementsOpacity( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, TimeSpan duration, - EasingType easingType, - EasingMode easingMode) + TransitionMode transitionMode) { if (this._isInterruptedAnimation) { @@ -512,8 +504,31 @@ private void AnimateUIElementsOpacity( return; } - _ = sourceBuilder.Opacity(from: 1, to: 0, duration: duration, easingType: easingType, easingMode: easingMode); - _ = targetBuilder.Opacity(from: 0, to: 1, duration: duration, easingType: easingType, easingMode: easingMode); + switch (transitionMode) + { + case TransitionMode.Normal: + _ = sourceBuilder.Opacity().TimedKeyFrames( + build: b => b + .KeyFrame(TimeSpan.Zero, 1) + .KeyFrame(duration / 3, 0, easingType: EasingType.Linear) + .KeyFrame(duration, 0)); + break; + case TransitionMode.Image: + _ = sourceBuilder.Opacity().TimedKeyFrames( + build: b => b + .KeyFrame(TimeSpan.Zero, 1) + .KeyFrame(duration / 3, 1) + .KeyFrame(duration, 0, easingType: EasingType.Linear)); + break; + default: + break; + } + + _ = targetBuilder.Opacity().TimedKeyFrames( + build: b => b + .KeyFrame(TimeSpan.Zero, 0) + .KeyFrame(duration / 3, 1, easingType: EasingType.Linear) + .KeyFrame(duration, 1)); } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 27363ed8867..8102d17be2b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -113,7 +113,7 @@ public FrameworkElement Target /// Gets or sets the duration of the connected animation between two UI elements. /// The default value is 600ms. /// - public TimeSpan AnimationDuration { get; set; } = TimeSpan.FromMilliseconds(600); + public TimeSpan Duration { get; set; } = TimeSpan.FromMilliseconds(600); /// /// Gets or sets the duration of the show animation for independent or unpaired UI elements. @@ -125,13 +125,13 @@ public FrameworkElement Target /// Gets or sets the delay of the show animation for independent or unpaired UI elements. /// The default value is 300ms. /// - public TimeSpan IndependentElementShowDelayDuration { get; set; } = TimeSpan.FromMilliseconds(300); + public TimeSpan IndependentElementShowDelay { get; set; } = TimeSpan.FromMilliseconds(300); /// - /// Gets or sets the duration of the interval between the show animations for independent or unpaired UI elements. + /// Gets or sets the interval between the show animations for independent or unpaired UI elements. /// The default value is 50ms. /// - public TimeSpan IndependentElementShowStepDuration { get; set; } = TimeSpan.FromMilliseconds(50); + public TimeSpan IndependentElementShowInterval { get; set; } = TimeSpan.FromMilliseconds(50); /// /// Gets or sets the duration of the hide animation for independent or unpaired UI elements. @@ -143,7 +143,7 @@ public FrameworkElement Target /// Gets or sets the translation of the hide animation for independent or unpaired UI elements. /// The default value is (0, 20). /// - public Point IndependentElementHideTranslation { get; set; } = new (0, 20); + public Point IndependentElementHideTranslation { get; set; } = new(0, 20); /// /// Gets or sets the easing function type for animation of independent or unpaired UI elements. From 7b9fe59ad314f6bff0c60cd5b466b8311ed479bb Mon Sep 17 00:00:00 2001 From: hhchaos Date: Tue, 24 May 2022 10:43:31 +0800 Subject: [PATCH 41/94] format some code --- .../TransitionHelper.AttachedProperty.cs | 4 +- .../Helpers/TransitionHelper.Logic.cs | 129 +++++++++--------- .../Helpers/TransitionHelper.Properties.cs | 4 +- .../Helpers/TransitionHelper.cs | 8 +- 4 files changed, 71 insertions(+), 74 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs index 23986213a93..e6b4ddfe629 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs @@ -22,7 +22,7 @@ public bool Equals(DependencyObject x, DependencyObject y) return false; } - return GetId(x) is string xId && GetId(y) is string yId && xId == yId; + return GetId(x) is { } xId && GetId(y) is { } yId && xId.Equals(yId); } public int GetHashCode(DependencyObject obj) @@ -78,7 +78,7 @@ public static void SetIsIndependent(DependencyObject obj, bool value) public static readonly DependencyProperty IsIndependentProperty = DependencyProperty.RegisterAttached("IsIndependent", typeof(bool), typeof(TransitionHelper), new PropertyMetadata(false)); - private static IEnumerable GetAnimatedElements(UIElement targetElement) + private static IEnumerable GetAnimatedElements(DependencyObject targetElement) { return targetElement?.FindDescendantsOrSelf() .Where(element => GetId(element) is not null || GetIsIndependent(element)) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 0384a98282b..5587d02fc25 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -8,7 +8,6 @@ using System.Numerics; using System.Threading; using System.Threading.Tasks; -using Windows.UI.Composition; using Windows.UI.Xaml; using Windows.UI.Xaml.Hosting; using Windows.UI.Xaml.Media.Animation; @@ -40,7 +39,7 @@ private void UpdateTargetAnimatedElements() UpdateAnimatedElements(this.Target, this.targetConnectedAnimatedElements, this.targetIndependentAnimatedElements); } - private void UpdateAnimatedElements(UIElement parent, IDictionary connectedAnimatedElements, IList independentAnimatedElements) + private void UpdateAnimatedElements(DependencyObject parent, IDictionary connectedAnimatedElements, ICollection independentAnimatedElements) { if (this.Source is null) { @@ -52,7 +51,7 @@ private void UpdateAnimatedElements(UIElement parent, IDictionary(); @@ -108,7 +104,8 @@ private Task AnimateControlsAsync(bool reversed, CancellationToken token) { var source = this.sourceConnectedAnimatedElements[key]; var target = this.targetConnectedAnimatedElements[key]; - var animationConfig = this.AnimationConfigs.FirstOrDefault(config => config.Id == key) ?? this.DefaultAnimationConfig; + var animationConfig = this.AnimationConfigs.FirstOrDefault(config => config.Id == key) ?? + this.DefaultAnimationConfig; animationTasks.Add( this.AnimateElementsAsync( reversed ? target : source, @@ -181,25 +178,25 @@ private async Task StartInterruptibleAnimationsAsync(bool reversed, Cancellation private void RestoreState(bool isTargetState, bool restoreAllChildElements) { this.IsTargetState = isTargetState; - this.ToggleVisualState(this.Source, this.SourceToggleMethod, !isTargetState); - this.ToggleVisualState(this.Target, this.TargetToggleMethod, isTargetState); + ToggleVisualState(this.Source, this.SourceToggleMethod, !isTargetState); + ToggleVisualState(this.Target, this.TargetToggleMethod, isTargetState); if (restoreAllChildElements) { - this.RestoreUIElements(this.SourceAnimatedElements); - this.RestoreUIElements(this.TargetAnimatedElements); + RestoreUIElements(this.SourceAnimatedElements); + RestoreUIElements(this.TargetAnimatedElements); } else { - this.RestoreUIElements(isTargetState ? this.SourceAnimatedElements : this.TargetAnimatedElements); + RestoreUIElements(isTargetState ? this.SourceAnimatedElements : this.TargetAnimatedElements); } } - private void RestoreUIElements(IEnumerable animatedElements) + private static void RestoreUIElements(IEnumerable animatedElements) { foreach (var animatedElement in animatedElements) { ElementCompositionPreview.SetIsTranslationEnabled(animatedElement, true); - var visual = ElementCompositionPreview.GetElementVisual(animatedElement); + var visual = animatedElement.GetVisual(); visual.Opacity = 1; visual.Scale = Vector3.One; visual.Properties.InsertVector3("Translation", Vector3.Zero); @@ -246,7 +243,7 @@ private Task InitControlStateAsync(FrameworkElement target, bool needUpdateLayou target.Opacity = 1; } - var targetVisual = ElementCompositionPreview.GetElementVisual(target); + var targetVisual = target.GetVisual(); if (!targetVisual.IsVisible) { targetVisual.IsVisible = true; @@ -274,8 +271,10 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d var sourceBuilder = AnimationBuilder.Create(); var targetBuilder = AnimationBuilder.Create(); - ElementCompositionPreview.GetElementVisual(source).CenterPoint = new Vector3(source.ActualSize * config.NormalizedCenterPoint.ToVector2(), 0); - ElementCompositionPreview.GetElementVisual(target).CenterPoint = new Vector3(target.ActualSize * config.NormalizedCenterPoint.ToVector2(), 0); + source.GetVisual().CenterPoint = + new Vector3(source.ActualSize * config.NormalizedCenterPoint.ToVector2(), 0); + target.GetVisual().CenterPoint = + new Vector3(target.ActualSize * config.NormalizedCenterPoint.ToVector2(), 0); this.AnimateUIElementsTranslation( sourceBuilder, targetBuilder, @@ -337,7 +336,8 @@ private Task AnimateIndependentElementsAsync( EasingType easingType, EasingMode easingMode) { - if (!independentElements.Any()) + var uiElements = independentElements as UIElement[] ?? independentElements.ToArray(); + if (!uiElements.Any()) { return Task.CompletedTask; } @@ -347,38 +347,40 @@ private Task AnimateIndependentElementsAsync( var delay = isShow ? this.IndependentElementShowDelay : TimeSpan.Zero; if (this._isInterruptedAnimation) { - duration *= this.interruptedAnimationReverseDurationRatio; - delay *= this.interruptedAnimationReverseDurationRatio; + duration *= InterruptedAnimationReverseDurationRatio; + delay *= InterruptedAnimationReverseDurationRatio; } - foreach (var item in independentElements) + foreach (var item in uiElements) { if (this.IndependentElementHideTranslation != default) { animationTasks.Add( AnimationBuilder + .Create() + .Translation( + from: this._isInterruptedAnimation + ? null + : (isShow ? this.IndependentElementHideTranslation.ToVector3() : Vector3.Zero), + to: isShow ? Vector3.Zero : this.IndependentElementHideTranslation.ToVector3(), + duration: duration, + easingType: easingType, + easingMode: easingMode, + delay: delay) + .StartAsync(item, token)); + } + + animationTasks.Add( + AnimationBuilder .Create() - .Translation( - from: this._isInterruptedAnimation ? null : (isShow ? this.IndependentElementHideTranslation.ToVector3() : Vector3.Zero), - to: isShow ? Vector3.Zero : this.IndependentElementHideTranslation.ToVector3(), + .Opacity( + from: this._isInterruptedAnimation ? null : (isShow ? 0 : 1), + to: isShow ? 1 : 0, duration: duration, easingType: easingType, easingMode: easingMode, delay: delay) .StartAsync(item, token)); - } - - animationTasks.Add( - AnimationBuilder - .Create() - .Opacity( - from: this._isInterruptedAnimation ? null : (isShow ? 0 : 1), - to: isShow ? 1 : 0, - duration: duration, - easingType: easingType, - easingMode: easingMode, - delay: delay) - .StartAsync(item, token)); if (isShow) { @@ -401,21 +403,21 @@ private void AnimateUIElementsTranslation( { if (this._isInterruptedAnimation) { - _ = sourceBuilder.Translation(to: Vector3.Zero, duration: almostZeroDuration); - _ = targetBuilder.Translation(to: Vector3.Zero, duration: duration, easingType: easingType, easingMode: easingMode); + _ = sourceBuilder.Translation(Vector3.Zero, duration: almostZeroDuration); + _ = targetBuilder.Translation(Vector3.Zero, duration: duration, easingType: easingType, easingMode: easingMode); return; } var sourceNormalizedCenterPoint = source.ActualSize * normalizedCenterPoint; var targetNormalizedCenterPoint = target.ActualSize * normalizedCenterPoint; - var diff = target.TransformToVisual(source).TransformPoint(default).ToVector2() - sourceNormalizedCenterPoint + targetNormalizedCenterPoint; + var diff = target.TransformToVisual(source).TransformPoint(default).ToVector2() - + sourceNormalizedCenterPoint + targetNormalizedCenterPoint; _ = sourceBuilder.Translation().TimedKeyFrames( - build: b => b + b => b .KeyFrame(duration - almostZeroDuration, new Vector3(diff, 0), easingType, easingMode) .KeyFrame(duration, Vector3.Zero)); _ = targetBuilder.Translation().TimedKeyFrames( - delayBehavior: AnimationDelayBehavior.SetInitialValueBeforeDelay, - build: b => b + b => b .KeyFrame(TimeSpan.Zero, new Vector3(-diff, 0)) .KeyFrame(duration - almostZeroDuration, Vector3.Zero, easingType, easingMode) .KeyFrame(duration, Vector3.Zero)); @@ -432,7 +434,7 @@ private void AnimateUIElementsScale( { var scaleX = target.ActualSize.X / source.ActualSize.X; var scaleY = target.ActualSize.Y / source.ActualSize.Y; - var scale = new Vector3((float)scaleX, (float)scaleY, 1); + var scale = new Vector3(scaleX, scaleY, 1); this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } @@ -446,7 +448,7 @@ private void AnimateUIElementsScaleX( EasingMode easingMode) { var scaleX = target.ActualSize.X / source.ActualSize.X; - var scale = new Vector3((float)scaleX, (float)scaleX, 1); + var scale = new Vector3(scaleX, scaleX, 1); this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } @@ -460,7 +462,7 @@ private void AnimateUIElementsScaleY( EasingMode easingMode) { var scaleY = target.ActualSize.Y / source.ActualSize.Y; - var scale = new Vector3((float)scaleY, (float)scaleY, 1); + var scale = new Vector3(scaleY, scaleY, 1); this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } @@ -474,18 +476,17 @@ private void AnimateUIElementsScale( { if (this._isInterruptedAnimation) { - _ = sourceBuilder.Scale(to: Vector3.One, duration: almostZeroDuration); - _ = targetBuilder.Scale(to: Vector3.One, duration: duration, easingType: easingType, easingMode: easingMode); + _ = sourceBuilder.Scale(Vector3.One, duration: almostZeroDuration); + _ = targetBuilder.Scale(Vector3.One, duration: duration, easingType: easingType, easingMode: easingMode); return; } _ = sourceBuilder.Scale().TimedKeyFrames( - build: b => b + b => b .KeyFrame(duration - almostZeroDuration, targetScale, easingType, easingMode) .KeyFrame(duration, Vector3.One)); _ = targetBuilder.Scale().TimedKeyFrames( - delayBehavior: AnimationDelayBehavior.SetInitialValueBeforeDelay, - build: b => b + b => b .KeyFrame(TimeSpan.Zero, new Vector3(1 / targetScale.X, 1 / targetScale.Y, 1)) .KeyFrame(duration - almostZeroDuration, Vector3.One, easingType, easingMode) .KeyFrame(duration, Vector3.One)); @@ -499,8 +500,8 @@ private void AnimateUIElementsOpacity( { if (this._isInterruptedAnimation) { - _ = sourceBuilder.Opacity(to: 0, duration: almostZeroDuration); - _ = targetBuilder.Opacity(to: 1, duration: almostZeroDuration); + _ = sourceBuilder.Opacity(0, duration: almostZeroDuration); + _ = targetBuilder.Opacity(1, duration: almostZeroDuration); return; } @@ -508,27 +509,23 @@ private void AnimateUIElementsOpacity( { case TransitionMode.Normal: _ = sourceBuilder.Opacity().TimedKeyFrames( - build: b => b + b => b .KeyFrame(TimeSpan.Zero, 1) - .KeyFrame(duration / 3, 0, easingType: EasingType.Linear) - .KeyFrame(duration, 0)); + .KeyFrame(duration / 3, 0, easingType: EasingType.Linear)); break; case TransitionMode.Image: _ = sourceBuilder.Opacity().TimedKeyFrames( - build: b => b + b => b .KeyFrame(TimeSpan.Zero, 1) .KeyFrame(duration / 3, 1) - .KeyFrame(duration, 0, easingType: EasingType.Linear)); - break; - default: + .KeyFrame(duration * 2 / 3, 0, easingType: EasingType.Linear)); break; } _ = targetBuilder.Opacity().TimedKeyFrames( - build: b => b + b => b .KeyFrame(TimeSpan.Zero, 0) - .KeyFrame(duration / 3, 1, easingType: EasingType.Linear) - .KeyFrame(duration, 1)); + .KeyFrame(duration / 3, 1, easingType: EasingType.Linear)); } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 8102d17be2b..74d07dfe89e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -37,7 +37,7 @@ public FrameworkElement Source if (this._source is not null) { - this.RestoreUIElements(this.SourceAnimatedElements); + RestoreUIElements(this.SourceAnimatedElements); } this._source = value; @@ -65,7 +65,7 @@ public FrameworkElement Target if (this._target is not null) { - this.RestoreUIElements(this.TargetAnimatedElements); + RestoreUIElements(this.TargetAnimatedElements); } this._target = value; diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 6ea8636454f..53ce0b2cd67 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -18,20 +18,20 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations [ContentProperty(Name = nameof(AnimationConfigs))] public sealed partial class TransitionHelper { + private const double InterruptedAnimationReverseDurationRatio = 0.7; private readonly Dictionary sourceConnectedAnimatedElements = new(); private readonly Dictionary targetConnectedAnimatedElements = new(); private readonly List sourceIndependentAnimatedElements = new(); private readonly List targetIndependentAnimatedElements = new(); - private readonly double interruptedAnimationReverseDurationRatio = 0.7; private readonly TimeSpan almostZeroDuration = TimeSpan.FromMilliseconds(1); private CancellationTokenSource _animateCancellationTokenSource; private CancellationTokenSource _reverseCancellationTokenSource; private TaskCompletionSource _animateTaskSource; private TaskCompletionSource _reverseTaskSource; - private bool _needUpdateSourceLayout = false; - private bool _needUpdateTargetLayout = false; - private bool _isInterruptedAnimation = false; + private bool _needUpdateSourceLayout; + private bool _needUpdateTargetLayout; + private bool _isInterruptedAnimation; private IEnumerable SourceAnimatedElements => this.sourceConnectedAnimatedElements.Values.Concat(this.sourceIndependentAnimatedElements); From a0e779ddfece3458f53c483dd12c55c7d6d1e4c9 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Tue, 24 May 2022 13:39:07 +0800 Subject: [PATCH 42/94] update --- .../Actions/StartTransitionActionXaml.bind | 8 ++++---- .../TransitionHelper/TransitionHelperCode.bind | 12 ++++++------ .../TransitionHelper/TransitionHelperXaml.bind | 14 +++++++------- .../{AnimationConfig.cs => TransitionConfig.cs} | 12 ++++++------ .../Helpers/TransitionHelper.Logic.cs | 12 ++++++------ .../Helpers/TransitionHelper.Properties.cs | 8 ++++---- .../Helpers/TransitionHelper.cs | 2 +- 7 files changed, 34 insertions(+), 34 deletions(-) rename Microsoft.Toolkit.Uwp.UI.Animations/Helpers/{AnimationConfig.cs => TransitionConfig.cs} (82%) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind index dfa590f829b..d5e09514cb5 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind @@ -9,10 +9,10 @@ mc:Ignorable="d"> - - + + +transitionHelper.Configs = new List { - new AnimationConfig + new TransitionConfig { Id = "background", ScaleMode = ScaleMode.Scale }, - new AnimationConfig + new TransitionConfig { Id = "image", ScaleMode = ScaleMode.Scale }, - new AnimationConfig + new TransitionConfig { Id = "guide", }, - new AnimationConfig + new TransitionConfig { Id = "name", ScaleMode = ScaleMode.ScaleY }, - new AnimationConfig + new TransitionConfig { Id = "desc", }, diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index 386db237629..bdf27ad4a3b 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -9,13 +9,13 @@ mc:Ignorable="d"> - - - + + + diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs similarity index 82% rename from Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs rename to Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs index 7c91739f84a..b6df07fbdf3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/AnimationConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs @@ -8,17 +8,17 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations { /// - /// Configuration used for UI element animation. + /// Configuration used for the transition between UI elements. /// - public class AnimationConfig + public class TransitionConfig { /// - /// Gets or sets id of a UI element. + /// Gets or sets an id to indicate the target UI elements. /// public string Id { get; set; } /// - /// Gets or sets the scale strategy of a UI element. + /// Gets or sets the scale strategy of the transition. /// The default value is . /// public ScaleMode ScaleMode { get; set; } = ScaleMode.None; @@ -38,13 +38,13 @@ public class AnimationConfig public TransitionMode TransitionMode { get; set; } = TransitionMode.Normal; /// - /// Gets or sets the easing function type for animation. + /// Gets or sets the easing function type for the transition. /// The default value is . /// public EasingType EasingType { get; set; } = EasingType.Default; /// - /// Gets or sets the easing function mode for animation. + /// Gets or sets the easing function mode for the transition. /// The default value is . /// public EasingMode EasingMode { get; set; } = EasingMode.EaseInOut; diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 5587d02fc25..8b7264ff744 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -104,8 +104,8 @@ private Task AnimateControlsAsync(bool reversed, CancellationToken token) { var source = this.sourceConnectedAnimatedElements[key]; var target = this.targetConnectedAnimatedElements[key]; - var animationConfig = this.AnimationConfigs.FirstOrDefault(config => config.Id == key) ?? - this.DefaultAnimationConfig; + var animationConfig = this.Configs.FirstOrDefault(config => config.Id == key) ?? + this.DefaultConfig; animationTasks.Add( this.AnimateElementsAsync( reversed ? target : source, @@ -266,7 +266,7 @@ void OnTargetLayoutUpdated(object sender, object e) return updateTargetLayoutTaskSource.Task; } - private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan duration, AnimationConfig config, CancellationToken token) + private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan duration, TransitionConfig config, CancellationToken token) { var sourceBuilder = AnimationBuilder.Create(); var targetBuilder = AnimationBuilder.Create(); @@ -511,21 +511,21 @@ private void AnimateUIElementsOpacity( _ = sourceBuilder.Opacity().TimedKeyFrames( b => b .KeyFrame(TimeSpan.Zero, 1) - .KeyFrame(duration / 3, 0, easingType: EasingType.Linear)); + .KeyFrame(duration / 3, 0, EasingType.Linear)); break; case TransitionMode.Image: _ = sourceBuilder.Opacity().TimedKeyFrames( b => b .KeyFrame(TimeSpan.Zero, 1) .KeyFrame(duration / 3, 1) - .KeyFrame(duration * 2 / 3, 0, easingType: EasingType.Linear)); + .KeyFrame(duration * 2 / 3, 0, EasingType.Linear)); break; } _ = targetBuilder.Opacity().TimedKeyFrames( b => b .KeyFrame(TimeSpan.Zero, 0) - .KeyFrame(duration / 3, 1, easingType: EasingType.Linear)); + .KeyFrame(duration / 3, 1, EasingType.Linear)); } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 74d07dfe89e..68069b1dca4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -76,14 +76,14 @@ public FrameworkElement Target } /// - /// Gets or sets the collection of animation configurations of UI elements that need to be connected by animation. + /// Gets or sets transition configurations of UI elements that need to be connected by animation. /// - public List AnimationConfigs { get; set; } = new(); + public List Configs { get; set; } = new(); /// - /// Gets or sets the default animation configuration. + /// Gets or sets the default transition configuration. /// - public AnimationConfig DefaultAnimationConfig { get; set; } = new(); + public TransitionConfig DefaultConfig { get; set; } = new(); /// /// Gets a value indicating whether the source control has been morphed to the target control. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 53ce0b2cd67..3ae33910924 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -15,7 +15,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// /// A animation helper that morphs between two controls. /// - [ContentProperty(Name = nameof(AnimationConfigs))] + [ContentProperty(Name = nameof(Configs))] public sealed partial class TransitionHelper { private const double InterruptedAnimationReverseDurationRatio = 0.7; From 6e2712b9a5e58715382ef833bf315ecc692bb800 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Wed, 25 May 2022 14:39:11 +0800 Subject: [PATCH 43/94] Update code file structure --- .../TransitionHelper.AttachedProperty.cs | 28 ------- .../Helpers/TransitionHelper.Helpers.cs | 76 +++++++++++++++++++ .../Helpers/TransitionHelper.Logic.cs | 33 -------- 3 files changed, 76 insertions(+), 61 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs index e6b4ddfe629..3074d913a7e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; -using System.Linq; using Windows.UI.Xaml; namespace Microsoft.Toolkit.Uwp.UI.Animations @@ -13,24 +11,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// public sealed partial class TransitionHelper { - private class AnimatedElementComparer : IEqualityComparer - { - public bool Equals(DependencyObject x, DependencyObject y) - { - if (GetIsIndependent(x) || GetIsIndependent(y)) - { - return false; - } - - return GetId(x) is { } xId && GetId(y) is { } yId && xId.Equals(yId); - } - - public int GetHashCode(DependencyObject obj) - { - return 0; - } - } - /// /// Get the animation id of the UI element. /// @@ -77,13 +57,5 @@ public static void SetIsIndependent(DependencyObject obj, bool value) /// public static readonly DependencyProperty IsIndependentProperty = DependencyProperty.RegisterAttached("IsIndependent", typeof(bool), typeof(TransitionHelper), new PropertyMetadata(false)); - - private static IEnumerable GetAnimatedElements(DependencyObject targetElement) - { - return targetElement?.FindDescendantsOrSelf() - .Where(element => GetId(element) is not null || GetIsIndependent(element)) - .Distinct(new AnimatedElementComparer()) - .OfType(); - } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs new file mode 100644 index 00000000000..044ed328f2d --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Hosting; + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// A animation helper that morphs between two controls. + /// + public sealed partial class TransitionHelper + { + private class AnimatedElementComparer : IEqualityComparer + { + public bool Equals(DependencyObject x, DependencyObject y) + { + if (GetIsIndependent(x) || GetIsIndependent(y)) + { + return false; + } + + return GetId(x) is { } xId && GetId(y) is { } yId && xId.Equals(yId); + } + + public int GetHashCode(DependencyObject obj) + { + return 0; + } + } + + private static IEnumerable GetAnimatedElements(DependencyObject targetElement) + { + return targetElement?.FindDescendantsOrSelf() + .Where(element => GetId(element) is not null || GetIsIndependent(element)) + .Distinct(new AnimatedElementComparer()) + .OfType(); + } + + private static void ToggleVisualState(UIElement target, VisualStateToggleMethod method, bool isVisible) + { + if (target is null) + { + return; + } + + switch (method) + { + case VisualStateToggleMethod.ByVisibility: + target.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed; + break; + case VisualStateToggleMethod.ByIsVisible: + target.GetVisual().IsVisible = isVisible; + break; + } + + target.IsHitTestVisible = isVisible; + } + + private static void RestoreUIElements(IEnumerable animatedElements) + { + foreach (var animatedElement in animatedElements) + { + ElementCompositionPreview.SetIsTranslationEnabled(animatedElement, true); + var visual = animatedElement.GetVisual(); + visual.Opacity = 1; + visual.Scale = Vector3.One; + visual.Properties.InsertVector3("Translation", Vector3.Zero); + } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 8b7264ff744..abf3e38fc4b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Windows.UI.Xaml; -using Windows.UI.Xaml.Hosting; using Windows.UI.Xaml.Media.Animation; namespace Microsoft.Toolkit.Uwp.UI.Animations @@ -62,26 +61,6 @@ private void UpdateAnimatedElements(DependencyObject parent, IDictionary animatedElements) - { - foreach (var animatedElement in animatedElements) - { - ElementCompositionPreview.SetIsTranslationEnabled(animatedElement, true); - var visual = animatedElement.GetVisual(); - visual.Opacity = 1; - visual.Scale = Vector3.One; - visual.Properties.InsertVector3("Translation", Vector3.Zero); - } - } - private async Task InitControlsStateAsync(bool forceUpdateAnimatedElements = false) { await Task.WhenAll( From 1fd61cfe296ca674921b351489d600c646be46df Mon Sep 17 00:00:00 2001 From: hhchaos Date: Tue, 28 Jun 2022 17:21:07 +0800 Subject: [PATCH 44/94] Remove TransitionMode --- .../TransitionHelperCode.bind | 14 ++------ .../TransitionHelperXaml.bind | 1 - .../Enums/TransitionMode.cs | 22 ------------ .../Helpers/TransitionConfig.cs | 6 ---- .../Helpers/TransitionHelper.Logic.cs | 36 ++++++++----------- .../Helpers/TransitionHelper.Properties.cs | 5 +++ 6 files changed, 22 insertions(+), 62 deletions(-) delete mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Enums/TransitionMode.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind index 5b559039cb2..588cc6c1af5 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind @@ -1,7 +1,7 @@ // Create a TransitionHelper. var transitionHelper = new TransitionHelper(); -// Configure TransitionHelper. +// Configure TransitionHelper.Items that use the default configuration do not need to be listed here. transitionHelper.Configs = new List { new TransitionConfig @@ -15,24 +15,16 @@ transitionHelper.Configs = new List ScaleMode = ScaleMode.Scale }, new TransitionConfig - { - Id = "guide", - }, - new TransitionConfig { Id = "name", ScaleMode = ScaleMode.ScaleY - }, - new TransitionConfig - { - Id = "desc", - }, + } }; transitionHelper.Source = FirstControl; transitionHelper.Target = SecondControl; // Animate. -await transitionHelper.AnimateAsync() +await transitionHelper.AnimateAsync(); // Reverse. await transitionHelper.ReverseAsync(); \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index bdf27ad4a3b..41b2512464b 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -12,7 +12,6 @@ diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/TransitionMode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/TransitionMode.cs deleted file mode 100644 index eae5c102f84..00000000000 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/TransitionMode.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Toolkit.Uwp.UI.Animations -{ - /// - /// Indicates the transition strategy between controls. - /// - public enum TransitionMode - { - /// - /// The default transition strategy. - /// - Normal, - - /// - /// The transition strategy for image or other UI elements that require smoother transitions. - /// - Image, - } -} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs index b6df07fbdf3..39c635cf607 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs @@ -31,12 +31,6 @@ public class TransitionConfig /// public Point NormalizedCenterPoint { get; set; } = default; - /// - /// Gets or sets the transition strategy. - /// The default value is . - /// - public TransitionMode TransitionMode { get; set; } = TransitionMode.Normal; - /// /// Gets or sets the easing function type for the transition. /// The default value is . diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index abf3e38fc4b..601538e4026 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Animation; namespace Microsoft.Toolkit.Uwp.UI.Animations @@ -157,6 +158,8 @@ private async Task StartInterruptibleAnimationsAsync(bool reversed, Cancellation private void RestoreState(bool isTargetState, bool restoreAllChildElements) { this.IsTargetState = isTargetState; + Canvas.SetZIndex(this.Source, _sourceZIndex); + Canvas.SetZIndex(this.Target, _targetZIndex); ToggleVisualState(this.Source, this.SourceToggleMethod, !isTargetState); ToggleVisualState(this.Target, this.TargetToggleMethod, isTargetState); if (restoreAllChildElements) @@ -172,6 +175,9 @@ private void RestoreState(bool isTargetState, bool restoreAllChildElements) private async Task InitControlsStateAsync(bool forceUpdateAnimatedElements = false) { + var maxZIndex = Math.Max(_sourceZIndex, _targetZIndex) + 1; + Canvas.SetZIndex(this.IsTargetState ? this.Source : this.Target, maxZIndex); + await Task.WhenAll( this.InitControlStateAsync(this.Source, this._needUpdateSourceLayout), this.InitControlStateAsync(this.Target, this._needUpdateTargetLayout)); @@ -254,8 +260,7 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d this.AnimateUIElementsOpacity( sourceBuilder, targetBuilder, - duration, - config.TransitionMode); + duration); switch (config.ScaleMode) { case ScaleMode.Scale: @@ -462,8 +467,7 @@ private void AnimateUIElementsScale( private void AnimateUIElementsOpacity( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, - TimeSpan duration, - TransitionMode transitionMode) + TimeSpan duration) { if (this._isInterruptedAnimation) { @@ -472,27 +476,15 @@ private void AnimateUIElementsOpacity( return; } - switch (transitionMode) - { - case TransitionMode.Normal: - _ = sourceBuilder.Opacity().TimedKeyFrames( - b => b - .KeyFrame(TimeSpan.Zero, 1) - .KeyFrame(duration / 3, 0, EasingType.Linear)); - break; - case TransitionMode.Image: - _ = sourceBuilder.Opacity().TimedKeyFrames( - b => b - .KeyFrame(TimeSpan.Zero, 1) - .KeyFrame(duration / 3, 1) - .KeyFrame(duration * 2 / 3, 0, EasingType.Linear)); - break; - } - + var useDuration = TimeSpan.FromMilliseconds(Math.Min(200, duration.TotalMilliseconds / 3)); + _ = sourceBuilder.Opacity().TimedKeyFrames( + b => b + .KeyFrame(TimeSpan.Zero, 1) + .KeyFrame(useDuration, 0, EasingType.Cubic, EasingMode.EaseIn)); _ = targetBuilder.Opacity().TimedKeyFrames( b => b .KeyFrame(TimeSpan.Zero, 0) - .KeyFrame(duration / 3, 1, EasingType.Linear)); + .KeyFrame(useDuration, 1, EasingType.Cubic, EasingMode.EaseOut)); } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 68069b1dca4..3644e97c1c5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using Windows.Foundation; using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Animation; namespace Microsoft.Toolkit.Uwp.UI.Animations @@ -16,7 +17,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations public sealed partial class TransitionHelper { private FrameworkElement _source; + private int _sourceZIndex = -1; private FrameworkElement _target; + private int _targetZIndex = -1; /// /// Gets or sets the source control. @@ -41,6 +44,7 @@ public FrameworkElement Source } this._source = value; + this._sourceZIndex = value is null ? -1 : Canvas.GetZIndex(value); this._needUpdateSourceLayout = true; this.UpdateSourceAnimatedElements(); } @@ -69,6 +73,7 @@ public FrameworkElement Target } this._target = value; + this._targetZIndex = value is null ? -1 : Canvas.GetZIndex(value); this._needUpdateTargetLayout = true; this.IsTargetState = false; this.UpdateTargetAnimatedElements(); From 1e1347a5a7fc27deb95c09238640da7950ca44c6 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Tue, 28 Jun 2022 17:27:29 +0800 Subject: [PATCH 45/94] Update some function names --- .../Helpers/TransitionHelper.Helpers.cs | 2 +- .../Helpers/TransitionHelper.Logic.cs | 58 +++++++++---------- .../Helpers/TransitionHelper.Properties.cs | 4 +- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index 044ed328f2d..60dd0a657db 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -61,7 +61,7 @@ private static void ToggleVisualState(UIElement target, VisualStateToggleMethod target.IsHitTestVisible = isVisible; } - private static void RestoreUIElements(IEnumerable animatedElements) + private static void RestoreElements(IEnumerable animatedElements) { foreach (var animatedElement in animatedElements) { diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 601538e4026..f8799d579da 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -62,7 +62,7 @@ private void UpdateAnimatedElements(DependencyObject parent, IDictionary config.Id == key) ?? this.DefaultConfig; animationTasks.Add( - this.AnimateElementsAsync( + this.AnimateElements( reversed ? target : source, reversed ? source : target, duration, @@ -96,14 +96,14 @@ private Task AnimateControlsAsync(bool reversed, CancellationToken token) } animationTasks.Add( - this.AnimateIndependentElementsAsync( + this.AnimateIndependentElements( this.sourceIndependentAnimatedElements.Concat(sourceUnpairedElements), reversed, token, IndependentElementEasingType, IndependentElementEasingMode)); animationTasks.Add( - this.AnimateIndependentElementsAsync( + this.AnimateIndependentElements( this.targetIndependentAnimatedElements.Concat(targetUnpairedElements), !reversed, token, @@ -142,7 +142,7 @@ private async Task StartInterruptibleAnimationsAsync(bool reversed, Cancellation return; } - await this.AnimateControlsAsync(reversed, token); + await this.AnimateControls(reversed, token); if (token.IsCancellationRequested) { _ = currentTaskSource.TrySetResult(false); @@ -164,12 +164,12 @@ private void RestoreState(bool isTargetState, bool restoreAllChildElements) ToggleVisualState(this.Target, this.TargetToggleMethod, isTargetState); if (restoreAllChildElements) { - RestoreUIElements(this.SourceAnimatedElements); - RestoreUIElements(this.TargetAnimatedElements); + RestoreElements(this.SourceAnimatedElements); + RestoreElements(this.TargetAnimatedElements); } else { - RestoreUIElements(isTargetState ? this.SourceAnimatedElements : this.TargetAnimatedElements); + RestoreElements(isTargetState ? this.SourceAnimatedElements : this.TargetAnimatedElements); } } @@ -179,8 +179,8 @@ private async Task InitControlsStateAsync(bool forceUpdateAnimatedElements = fal Canvas.SetZIndex(this.IsTargetState ? this.Source : this.Target, maxZIndex); await Task.WhenAll( - this.InitControlStateAsync(this.Source, this._needUpdateSourceLayout), - this.InitControlStateAsync(this.Target, this._needUpdateTargetLayout)); + this.InitControlState(this.Source, this._needUpdateSourceLayout), + this.InitControlState(this.Target, this._needUpdateTargetLayout)); this._needUpdateSourceLayout = false; this._needUpdateTargetLayout = false; @@ -192,7 +192,7 @@ await Task.WhenAll( } } - private Task InitControlStateAsync(FrameworkElement target, bool needUpdateLayout) + private Task InitControlState(FrameworkElement target, bool needUpdateLayout) { var updateLayoutTask = Task.CompletedTask; if (target is null) @@ -207,7 +207,7 @@ private Task InitControlStateAsync(FrameworkElement target, bool needUpdateLayou target.Visibility = Visibility.Visible; if (needUpdateLayout) { - updateLayoutTask = this.UpdateControlLayoutAsync(target); + updateLayoutTask = this.UpdateControlLayout(target); } } @@ -225,7 +225,7 @@ private Task InitControlStateAsync(FrameworkElement target, bool needUpdateLayou return updateLayoutTask; } - private Task UpdateControlLayoutAsync(FrameworkElement target) + private Task UpdateControlLayout(FrameworkElement target) { var updateTargetLayoutTaskSource = new TaskCompletionSource(); void OnTargetLayoutUpdated(object sender, object e) @@ -239,7 +239,7 @@ void OnTargetLayoutUpdated(object sender, object e) return updateTargetLayoutTaskSource.Task; } - private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan duration, TransitionConfig config, CancellationToken token) + private Task AnimateElements(UIElement source, UIElement target, TimeSpan duration, TransitionConfig config, CancellationToken token) { var sourceBuilder = AnimationBuilder.Create(); var targetBuilder = AnimationBuilder.Create(); @@ -248,7 +248,7 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d new Vector3(source.ActualSize * config.NormalizedCenterPoint.ToVector2(), 0); target.GetVisual().CenterPoint = new Vector3(target.ActualSize * config.NormalizedCenterPoint.ToVector2(), 0); - this.AnimateUIElementsTranslation( + this.AnimateTranslation( sourceBuilder, targetBuilder, source, @@ -257,14 +257,14 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d duration, config.EasingType, config.EasingMode); - this.AnimateUIElementsOpacity( + this.AnimateOpacity( sourceBuilder, targetBuilder, duration); switch (config.ScaleMode) { case ScaleMode.Scale: - this.AnimateUIElementsScale( + this.AnimateScale( sourceBuilder, targetBuilder, source, @@ -274,7 +274,7 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d config.EasingMode); break; case ScaleMode.ScaleX: - this.AnimateUIElementsScaleX( + this.AnimateScaleX( sourceBuilder, targetBuilder, source, @@ -284,7 +284,7 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d config.EasingMode); break; case ScaleMode.ScaleY: - this.AnimateUIElementsScaleY( + this.AnimateScaleY( sourceBuilder, targetBuilder, source, @@ -301,7 +301,7 @@ private Task AnimateElementsAsync(UIElement source, UIElement target, TimeSpan d return Task.WhenAll(sourceBuilder.StartAsync(source, token), targetBuilder.StartAsync(target, token)); } - private Task AnimateIndependentElementsAsync( + private Task AnimateIndependentElements( IEnumerable independentElements, bool isShow, CancellationToken token, @@ -363,7 +363,7 @@ private Task AnimateIndependentElementsAsync( return Task.WhenAll(animationTasks); } - private void AnimateUIElementsTranslation( + private void AnimateTranslation( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, @@ -395,7 +395,7 @@ private void AnimateUIElementsTranslation( .KeyFrame(duration, Vector3.Zero)); } - private void AnimateUIElementsScale( + private void AnimateScale( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, @@ -407,10 +407,10 @@ private void AnimateUIElementsScale( var scaleX = target.ActualSize.X / source.ActualSize.X; var scaleY = target.ActualSize.Y / source.ActualSize.Y; var scale = new Vector3(scaleX, scaleY, 1); - this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); + this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } - private void AnimateUIElementsScaleX( + private void AnimateScaleX( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, @@ -421,10 +421,10 @@ private void AnimateUIElementsScaleX( { var scaleX = target.ActualSize.X / source.ActualSize.X; var scale = new Vector3(scaleX, scaleX, 1); - this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); + this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } - private void AnimateUIElementsScaleY( + private void AnimateScaleY( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, UIElement source, @@ -435,10 +435,10 @@ private void AnimateUIElementsScaleY( { var scaleY = target.ActualSize.Y / source.ActualSize.Y; var scale = new Vector3(scaleY, scaleY, 1); - this.AnimateUIElementsScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); + this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } - private void AnimateUIElementsScale( + private void AnimateScale( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, Vector3 targetScale, @@ -464,7 +464,7 @@ private void AnimateUIElementsScale( .KeyFrame(duration, Vector3.One)); } - private void AnimateUIElementsOpacity( + private void AnimateOpacity( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, TimeSpan duration) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 3644e97c1c5..f16feccd587 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -40,7 +40,7 @@ public FrameworkElement Source if (this._source is not null) { - RestoreUIElements(this.SourceAnimatedElements); + RestoreElements(this.SourceAnimatedElements); } this._source = value; @@ -69,7 +69,7 @@ public FrameworkElement Target if (this._target is not null) { - RestoreUIElements(this.TargetAnimatedElements); + RestoreElements(this.TargetAnimatedElements); } this._target = value; From 05cb8088660649f70344f00cef7239f52bb72af7 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Sun, 10 Jul 2022 21:46:56 +0800 Subject: [PATCH 46/94] update independent elements animation --- .../Helpers/TransitionHelper.Logic.cs | 52 +++++++++---------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index f8799d579da..4443d5918a9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -308,8 +308,7 @@ private Task AnimateIndependentElements( EasingType easingType, EasingMode easingMode) { - var uiElements = independentElements as UIElement[] ?? independentElements.ToArray(); - if (!uiElements.Any()) + if (independentElements?.ToArray() is not { Length: > 0 } elements) { return Task.CompletedTask; } @@ -317,43 +316,40 @@ private Task AnimateIndependentElements( var animationTasks = new List(); var duration = isShow ? this.IndependentElementShowDuration : this.IndependentElementHideDuration; var delay = isShow ? this.IndependentElementShowDelay : TimeSpan.Zero; + var translationFrom = isShow ? this.IndependentElementHideTranslation.ToVector3() : Vector3.Zero; + var translationTo = isShow ? Vector3.Zero : this.IndependentElementHideTranslation.ToVector3(); + var opacityFrom = isShow ? 0 : 1; + var opacityTo = isShow ? 1 : 0; if (this._isInterruptedAnimation) { duration *= InterruptedAnimationReverseDurationRatio; delay *= InterruptedAnimationReverseDurationRatio; } - foreach (var item in uiElements) + foreach (var item in elements) { - if (this.IndependentElementHideTranslation != default) + var animationBuilder = AnimationBuilder.Create(); + if (Math.Abs(this.IndependentElementHideTranslation.X) > 0.01 || + Math.Abs(this.IndependentElementHideTranslation.Y) > 0.01) { - animationTasks.Add( - AnimationBuilder - .Create() - .Translation( - from: this._isInterruptedAnimation - ? null - : (isShow ? this.IndependentElementHideTranslation.ToVector3() : Vector3.Zero), - to: isShow ? Vector3.Zero : this.IndependentElementHideTranslation.ToVector3(), - duration: duration, - easingType: easingType, - easingMode: easingMode, - delay: delay) - .StartAsync(item, token)); + animationBuilder.Translation( + translationTo, + this._isInterruptedAnimation ? null : translationFrom, + delay, + duration: duration, + easingType: easingType, + easingMode: easingMode); } - animationTasks.Add( - AnimationBuilder - .Create() - .Opacity( - from: this._isInterruptedAnimation ? null : (isShow ? 0 : 1), - to: isShow ? 1 : 0, - duration: duration, - easingType: easingType, - easingMode: easingMode, - delay: delay) - .StartAsync(item, token)); + animationBuilder.Opacity( + opacityTo, + this._isInterruptedAnimation ? null : opacityFrom, + delay, + duration, + easingType: easingType, + easingMode: easingMode); + animationTasks.Add(animationBuilder.StartAsync(item, token)); if (isShow) { delay += this.IndependentElementShowInterval; From 3d3d2fa0f53a00e8cc0ca51102fad2283a179de8 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Wed, 13 Jul 2022 20:17:39 +0800 Subject: [PATCH 47/94] Add clip animation --- .../Helpers/TransitionConfig.cs | 5 + .../Helpers/TransitionHelper.Helpers.cs | 1 + .../Helpers/TransitionHelper.Logic.cs | 138 +++++++++++++----- .../Helpers/TransitionHelper.cs | 2 +- 4 files changed, 108 insertions(+), 38 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs index 39c635cf607..be13569bf48 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs @@ -23,6 +23,11 @@ public class TransitionConfig /// public ScaleMode ScaleMode { get; set; } = ScaleMode.None; + /// + /// Gets or sets a value indicating whether clip animations are enabled for the target UI elements. + /// + public bool EnableClipAnimation { get; set; } + /// /// Gets or sets the center point used to calculate the element's translation or scale when animating. /// Value is normalized with respect to the size of the animated element. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index 60dd0a657db..2f057c0b551 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -69,6 +69,7 @@ private static void RestoreElements(IEnumerable animatedElements) var visual = animatedElement.GetVisual(); visual.Opacity = 1; visual.Scale = Vector3.One; + visual.Clip = null; visual.Properties.InsertVector3("Translation", Vector3.Zero); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 4443d5918a9..9352e11b5f9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -149,28 +149,20 @@ private async Task StartInterruptibleAnimationsAsync(bool reversed, Cancellation return; } - this.RestoreState(!reversed, false); + this.RestoreState(!reversed); _ = currentTaskSource.TrySetResult(true); this._isInterruptedAnimation = false; } - private void RestoreState(bool isTargetState, bool restoreAllChildElements) + private void RestoreState(bool isTargetState) { this.IsTargetState = isTargetState; Canvas.SetZIndex(this.Source, _sourceZIndex); Canvas.SetZIndex(this.Target, _targetZIndex); ToggleVisualState(this.Source, this.SourceToggleMethod, !isTargetState); ToggleVisualState(this.Target, this.TargetToggleMethod, isTargetState); - if (restoreAllChildElements) - { - RestoreElements(this.SourceAnimatedElements); - RestoreElements(this.TargetAnimatedElements); - } - else - { - RestoreElements(isTargetState ? this.SourceAnimatedElements : this.TargetAnimatedElements); - } + RestoreElements(this.SourceAnimatedElements.Concat(this.TargetAnimatedElements)); } private async Task InitControlsStateAsync(bool forceUpdateAnimatedElements = false) @@ -244,16 +236,20 @@ private Task AnimateElements(UIElement source, UIElement target, TimeSpan durati var sourceBuilder = AnimationBuilder.Create(); var targetBuilder = AnimationBuilder.Create(); - source.GetVisual().CenterPoint = - new Vector3(source.ActualSize * config.NormalizedCenterPoint.ToVector2(), 0); - target.GetVisual().CenterPoint = - new Vector3(target.ActualSize * config.NormalizedCenterPoint.ToVector2(), 0); + var sourceActualSize = source is FrameworkElement sourceElement ? new Vector2((float)sourceElement.ActualWidth, (float)sourceElement.ActualHeight) : source.ActualSize; + var targetActualSize = target is FrameworkElement targetElement ? new Vector2((float)targetElement.ActualWidth, (float)targetElement.ActualHeight) : target.ActualSize; + var sourceCenterPoint = sourceActualSize * config.NormalizedCenterPoint.ToVector2(); + var targetCenterPoint = targetActualSize * config.NormalizedCenterPoint.ToVector2(); + + source.GetVisual().CenterPoint = new Vector3(sourceCenterPoint, 0); + target.GetVisual().CenterPoint = new Vector3(targetCenterPoint, 0); this.AnimateTranslation( sourceBuilder, targetBuilder, source, target, - config.NormalizedCenterPoint.ToVector2(), + sourceCenterPoint, + targetCenterPoint, duration, config.EasingType, config.EasingMode); @@ -261,14 +257,36 @@ private Task AnimateElements(UIElement source, UIElement target, TimeSpan durati sourceBuilder, targetBuilder, duration); + if (config is { EnableClipAnimation: true, ScaleMode: not ScaleMode.Scale }) + { + Axis? axis = config.ScaleMode switch + { + ScaleMode.Scale => null, + ScaleMode.ScaleX => Axis.Y, + ScaleMode.ScaleY => Axis.X, + _ => null, + }; + this.AnimateClip( + sourceBuilder, + targetBuilder, + sourceActualSize, + targetActualSize, + sourceCenterPoint, + targetCenterPoint, + duration, + config.EasingType, + config.EasingMode, + axis); + } + switch (config.ScaleMode) { case ScaleMode.Scale: this.AnimateScale( sourceBuilder, targetBuilder, - source, - target, + sourceActualSize, + targetActualSize, duration, config.EasingType, config.EasingMode); @@ -277,8 +295,8 @@ private Task AnimateElements(UIElement source, UIElement target, TimeSpan durati this.AnimateScaleX( sourceBuilder, targetBuilder, - source, - target, + sourceActualSize, + targetActualSize, duration, config.EasingType, config.EasingMode); @@ -287,8 +305,8 @@ private Task AnimateElements(UIElement source, UIElement target, TimeSpan durati this.AnimateScaleY( sourceBuilder, targetBuilder, - source, - target, + sourceActualSize, + targetActualSize, duration, config.EasingType, config.EasingMode); @@ -364,7 +382,8 @@ private void AnimateTranslation( AnimationBuilder targetBuilder, UIElement source, UIElement target, - Vector2 normalizedCenterPoint, + Vector2 sourceCenterPoint, + Vector2 targetCenterPoint, TimeSpan duration, EasingType easingType, EasingMode easingMode) @@ -376,10 +395,7 @@ private void AnimateTranslation( return; } - var sourceNormalizedCenterPoint = source.ActualSize * normalizedCenterPoint; - var targetNormalizedCenterPoint = target.ActualSize * normalizedCenterPoint; - var diff = target.TransformToVisual(source).TransformPoint(default).ToVector2() - - sourceNormalizedCenterPoint + targetNormalizedCenterPoint; + var diff = target.TransformToVisual(source).TransformPoint(default).ToVector2() - sourceCenterPoint + targetCenterPoint; _ = sourceBuilder.Translation().TimedKeyFrames( b => b .KeyFrame(duration - almostZeroDuration, new Vector3(diff, 0), easingType, easingMode) @@ -394,14 +410,14 @@ private void AnimateTranslation( private void AnimateScale( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, - UIElement source, - UIElement target, + Vector2 sourceActualSize, + Vector2 targetActualSize, TimeSpan duration, EasingType easingType, EasingMode easingMode) { - var scaleX = target.ActualSize.X / source.ActualSize.X; - var scaleY = target.ActualSize.Y / source.ActualSize.Y; + var scaleX = targetActualSize.X / sourceActualSize.X; + var scaleY = targetActualSize.Y / sourceActualSize.Y; var scale = new Vector3(scaleX, scaleY, 1); this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } @@ -409,13 +425,13 @@ private void AnimateScale( private void AnimateScaleX( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, - UIElement source, - UIElement target, + Vector2 sourceActualSize, + Vector2 targetActualSize, TimeSpan duration, EasingType easingType, EasingMode easingMode) { - var scaleX = target.ActualSize.X / source.ActualSize.X; + var scaleX = targetActualSize.X / sourceActualSize.X; var scale = new Vector3(scaleX, scaleX, 1); this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } @@ -423,13 +439,13 @@ private void AnimateScaleX( private void AnimateScaleY( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, - UIElement source, - UIElement target, + Vector2 sourceActualSize, + Vector2 targetActualSize, TimeSpan duration, EasingType easingType, EasingMode easingMode) { - var scaleY = target.ActualSize.Y / source.ActualSize.Y; + var scaleY = targetActualSize.Y / sourceActualSize.Y; var scale = new Vector3(scaleY, scaleY, 1); this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } @@ -482,5 +498,53 @@ private void AnimateOpacity( .KeyFrame(TimeSpan.Zero, 0) .KeyFrame(useDuration, 1, EasingType.Cubic, EasingMode.EaseOut)); } + + private void AnimateClip( + AnimationBuilder sourceBuilder, + AnimationBuilder targetBuilder, + Vector2 sourceActualSize, + Vector2 targetActualSize, + Vector2 sourceCenterPoint, + Vector2 targetCenterPoint, + TimeSpan duration, + EasingType easingType, + EasingMode easingMode, + Axis? axis) + { + // -4 is used to prevent shadows from being cropped. + var defaultValue = -4; + var defaultThickness = new Thickness(defaultValue); + if (this._isInterruptedAnimation) + { + _ = sourceBuilder.Clip(defaultThickness, duration: almostZeroDuration); + _ = targetBuilder.Clip(defaultThickness, duration: duration, easingType: easingType, easingMode: easingMode); + return; + } + + var left = axis == Axis.X + ? Math.Max(defaultValue, targetCenterPoint.X - sourceCenterPoint.X) + : defaultValue; + var top = axis == Axis.Y + ? Math.Max(defaultValue, targetCenterPoint.Y - sourceCenterPoint.Y) + : defaultValue; + var right = axis == Axis.X + ? Math.Max(defaultValue, targetActualSize.X - sourceActualSize.X - left) + : defaultValue; + var bottom = axis == Axis.Y + ? Math.Max(defaultValue, targetActualSize.Y - sourceActualSize.Y - top) + : defaultValue; + + _ = sourceBuilder.Clip( + new Thickness(left, top, right, bottom), + duration: duration, + easingType: easingType, + easingMode: easingMode); + _ = targetBuilder.Clip( + defaultThickness, + new Thickness(left, top, right, bottom), + duration: duration, + easingType: easingType, + easingMode: easingMode); + } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 3ae33910924..e6b656e079d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -160,7 +160,7 @@ public void Reset(bool toInitialState = true) } this._isInterruptedAnimation = false; - this.RestoreState(!toInitialState, true); + this.RestoreState(!toInitialState); } } } From 8e8f54b12c7172c76271243ad7d575c2923bab68 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Wed, 13 Jul 2022 20:27:23 +0800 Subject: [PATCH 48/94] update --- .../Helpers/TransitionHelper.Logic.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 9352e11b5f9..6f246ffcc19 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -261,6 +261,7 @@ private Task AnimateElements(UIElement source, UIElement target, TimeSpan durati { Axis? axis = config.ScaleMode switch { + ScaleMode.None => null, ScaleMode.Scale => null, ScaleMode.ScaleX => Axis.Y, ScaleMode.ScaleY => Axis.X, From da5b10354b5c4a046e7720346ffeeb377e5e5ac8 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Thu, 14 Jul 2022 21:16:41 +0800 Subject: [PATCH 49/94] Fix clip animations --- .../Helpers/TransitionHelper.Helpers.cs | 11 ++ .../Helpers/TransitionHelper.Logic.cs | 166 +++++++++--------- 2 files changed, 93 insertions(+), 84 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index 2f057c0b551..9d03cf15283 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -73,5 +73,16 @@ private static void RestoreElements(IEnumerable animatedElements) visual.Properties.InsertVector3("Translation", Vector3.Zero); } } + + private static Vector2 GetInverseScale(Vector2 scale) => new Vector2(1 / scale.X, 1 / scale.Y); + + private static Thickness GetFixedThickness(Thickness thickness, double defaultValue) + { + var left = thickness.Left < 0.1 ? defaultValue : thickness.Left; + var top = thickness.Top < 0.1 ? defaultValue : thickness.Top; + var right = thickness.Right < 0.1 ? defaultValue : thickness.Right; + var bottom = thickness.Bottom < 0.1 ? defaultValue : thickness.Bottom; + return new Thickness(left, top, right, bottom); + } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 6f246ffcc19..c24d55d5ff4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -257,6 +257,36 @@ private Task AnimateElements(UIElement source, UIElement target, TimeSpan durati sourceBuilder, targetBuilder, duration); + var targetScale = config.ScaleMode switch + { + ScaleMode.None => Vector2.One, + ScaleMode.Scale => this.AnimateScale( + sourceBuilder, + targetBuilder, + sourceActualSize, + targetActualSize, + duration, + config.EasingType, + config.EasingMode), + ScaleMode.ScaleX => this.AnimateScaleX( + sourceBuilder, + targetBuilder, + sourceActualSize, + targetActualSize, + duration, + config.EasingType, + config.EasingMode), + ScaleMode.ScaleY => this.AnimateScaleY( + sourceBuilder, + targetBuilder, + sourceActualSize, + targetActualSize, + duration, + config.EasingType, + config.EasingMode), + _ => Vector2.One, + }; + if (config is { EnableClipAnimation: true, ScaleMode: not ScaleMode.Scale }) { Axis? axis = config.ScaleMode switch @@ -274,48 +304,14 @@ private Task AnimateElements(UIElement source, UIElement target, TimeSpan durati targetActualSize, sourceCenterPoint, targetCenterPoint, + targetScale, duration, config.EasingType, config.EasingMode, axis); } - switch (config.ScaleMode) - { - case ScaleMode.Scale: - this.AnimateScale( - sourceBuilder, - targetBuilder, - sourceActualSize, - targetActualSize, - duration, - config.EasingType, - config.EasingMode); - break; - case ScaleMode.ScaleX: - this.AnimateScaleX( - sourceBuilder, - targetBuilder, - sourceActualSize, - targetActualSize, - duration, - config.EasingType, - config.EasingMode); - break; - case ScaleMode.ScaleY: - this.AnimateScaleY( - sourceBuilder, - targetBuilder, - sourceActualSize, - targetActualSize, - duration, - config.EasingType, - config.EasingMode); - break; - case ScaleMode.None: - default: - break; - } + return Task.WhenAll(sourceBuilder.StartAsync(source, token), targetBuilder.StartAsync(target, token)); } @@ -391,24 +387,17 @@ private void AnimateTranslation( { if (this._isInterruptedAnimation) { - _ = sourceBuilder.Translation(Vector3.Zero, duration: almostZeroDuration); - _ = targetBuilder.Translation(Vector3.Zero, duration: duration, easingType: easingType, easingMode: easingMode); + _ = sourceBuilder.Translation(Vector2.Zero, duration: almostZeroDuration); + _ = targetBuilder.Translation(Vector2.Zero, duration: duration, easingType: easingType, easingMode: easingMode); return; } var diff = target.TransformToVisual(source).TransformPoint(default).ToVector2() - sourceCenterPoint + targetCenterPoint; - _ = sourceBuilder.Translation().TimedKeyFrames( - b => b - .KeyFrame(duration - almostZeroDuration, new Vector3(diff, 0), easingType, easingMode) - .KeyFrame(duration, Vector3.Zero)); - _ = targetBuilder.Translation().TimedKeyFrames( - b => b - .KeyFrame(TimeSpan.Zero, new Vector3(-diff, 0)) - .KeyFrame(duration - almostZeroDuration, Vector3.Zero, easingType, easingMode) - .KeyFrame(duration, Vector3.Zero)); + _ = sourceBuilder.Translation(diff, duration: duration, easingType: easingType, easingMode: easingMode); + _ = targetBuilder.Translation(Vector2.Zero, -diff, duration: duration, easingType: easingType, easingMode: easingMode); } - private void AnimateScale( + private Vector2 AnimateScale( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, Vector2 sourceActualSize, @@ -419,11 +408,11 @@ private void AnimateScale( { var scaleX = targetActualSize.X / sourceActualSize.X; var scaleY = targetActualSize.Y / sourceActualSize.Y; - var scale = new Vector3(scaleX, scaleY, 1); - this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); + var scale = new Vector2(scaleX, scaleY); + return this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } - private void AnimateScaleX( + private Vector2 AnimateScaleX( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, Vector2 sourceActualSize, @@ -433,11 +422,11 @@ private void AnimateScaleX( EasingMode easingMode) { var scaleX = targetActualSize.X / sourceActualSize.X; - var scale = new Vector3(scaleX, scaleX, 1); - this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); + var scale = new Vector2(scaleX, scaleX); + return this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } - private void AnimateScaleY( + private Vector2 AnimateScaleY( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, Vector2 sourceActualSize, @@ -447,34 +436,27 @@ private void AnimateScaleY( EasingMode easingMode) { var scaleY = targetActualSize.Y / sourceActualSize.Y; - var scale = new Vector3(scaleY, scaleY, 1); - this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); + var scale = new Vector2(scaleY, scaleY); + return this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); } - private void AnimateScale( + private Vector2 AnimateScale( AnimationBuilder sourceBuilder, AnimationBuilder targetBuilder, - Vector3 targetScale, + Vector2 targetScale, TimeSpan duration, EasingType easingType, EasingMode easingMode) { if (this._isInterruptedAnimation) { - _ = sourceBuilder.Scale(Vector3.One, duration: almostZeroDuration); - _ = targetBuilder.Scale(Vector3.One, duration: duration, easingType: easingType, easingMode: easingMode); - return; + _ = sourceBuilder.Scale(Vector2.One, duration: almostZeroDuration); + _ = targetBuilder.Scale(Vector2.One, duration: duration, easingType: easingType, easingMode: easingMode); } - _ = sourceBuilder.Scale().TimedKeyFrames( - b => b - .KeyFrame(duration - almostZeroDuration, targetScale, easingType, easingMode) - .KeyFrame(duration, Vector3.One)); - _ = targetBuilder.Scale().TimedKeyFrames( - b => b - .KeyFrame(TimeSpan.Zero, new Vector3(1 / targetScale.X, 1 / targetScale.Y, 1)) - .KeyFrame(duration - almostZeroDuration, Vector3.One, easingType, easingMode) - .KeyFrame(duration, Vector3.One)); + _ = sourceBuilder.Scale(targetScale, duration: duration, easingType: easingType, easingMode: easingMode); + _ = targetBuilder.Scale(Vector2.One, GetInverseScale(targetScale), duration: duration, easingType: easingType, easingMode: easingMode); + return targetScale; } private void AnimateOpacity( @@ -507,6 +489,7 @@ private void AnimateClip( Vector2 targetActualSize, Vector2 sourceCenterPoint, Vector2 targetCenterPoint, + Vector2 targetScale, TimeSpan duration, EasingType easingType, EasingMode easingMode, @@ -522,27 +505,42 @@ private void AnimateClip( return; } - var left = axis == Axis.X - ? Math.Max(defaultValue, targetCenterPoint.X - sourceCenterPoint.X) - : defaultValue; - var top = axis == Axis.Y - ? Math.Max(defaultValue, targetCenterPoint.Y - sourceCenterPoint.Y) - : defaultValue; - var right = axis == Axis.X - ? Math.Max(defaultValue, targetActualSize.X - sourceActualSize.X - left) - : defaultValue; - var bottom = axis == Axis.Y - ? Math.Max(defaultValue, targetActualSize.Y - sourceActualSize.Y - top) - : defaultValue; + var inverseScale = GetInverseScale(targetScale); + + var sourceLeft = axis is Axis.Y ? 0 : sourceCenterPoint.X - targetCenterPoint.X; + var sourceTop = axis is Axis.X ? 0 : sourceCenterPoint.Y - targetCenterPoint.Y; + var targetLeft = axis is Axis.Y ? 0 : targetCenterPoint.X - sourceCenterPoint.X; + var targetTop = axis is Axis.X ? 0 : targetCenterPoint.Y - sourceCenterPoint.Y; + var sourceEndViewportWidth = axis is Axis.Y ? sourceActualSize.X * targetScale.X : targetActualSize.X; + var sourceEndViewportHeight = axis is Axis.X ? sourceActualSize.Y * targetScale.Y : targetActualSize.Y; + var targetBeginViewportWidth = axis is Axis.Y ? targetActualSize.X * inverseScale.X : sourceActualSize.X; + var targetBeginViewportHeight = axis is Axis.X ? targetActualSize.Y * inverseScale.Y : sourceActualSize.Y; + + var scaleMatrix = Matrix3x2.CreateScale(targetScale, sourceCenterPoint); + var inverseScaleMatrix = Matrix3x2.CreateScale(inverseScale, targetCenterPoint); + + Matrix3x2.Invert(scaleMatrix, out Matrix3x2 scaleMatrixInvert); + Matrix3x2.Invert(inverseScaleMatrix, out Matrix3x2 inverseScaleMatrixInvert); + + var sourceLeftTop = Vector2.Transform(new Vector2(sourceLeft, sourceTop), scaleMatrixInvert); + var sourceRightBottom = Vector2.Transform(new Vector2(sourceLeft + sourceEndViewportWidth, sourceTop + sourceEndViewportHeight), scaleMatrixInvert); + var sourceRight = sourceActualSize.X - sourceRightBottom.X; + var sourceBottom = sourceActualSize.Y - sourceRightBottom.Y; + + var targetLeftTop = Vector2.Transform(new Vector2(targetLeft, targetTop), inverseScaleMatrixInvert); + var targetRightBottom = Vector2.Transform(new Vector2(targetLeft + targetBeginViewportWidth, targetTop + targetBeginViewportHeight), inverseScaleMatrixInvert); + var targetRight = targetActualSize.X - targetRightBottom.X; + var targetBottom = targetActualSize.Y - targetRightBottom.Y; _ = sourceBuilder.Clip( - new Thickness(left, top, right, bottom), + GetFixedThickness(new Thickness(sourceLeftTop.X, sourceLeftTop.Y, sourceRight, sourceBottom), defaultValue), + from: defaultThickness, duration: duration, easingType: easingType, easingMode: easingMode); _ = targetBuilder.Clip( defaultThickness, - new Thickness(left, top, right, bottom), + GetFixedThickness(new Thickness(targetLeftTop.X, targetLeftTop.Y, targetRight, targetBottom), defaultValue), duration: duration, easingType: easingType, easingMode: easingMode); From acd6ac39952dd5cc4576a1bf2c60176a5792f1d6 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 15 Jul 2022 14:50:10 +0800 Subject: [PATCH 50/94] fix clip animation --- .../Helpers/TransitionHelper.Logic.cs | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index c24d55d5ff4..12c9c381f87 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -311,8 +311,6 @@ private Task AnimateElements(UIElement source, UIElement target, TimeSpan durati axis); } - - return Task.WhenAll(sourceBuilder.StartAsync(source, token), targetBuilder.StartAsync(target, token)); } @@ -507,28 +505,23 @@ private void AnimateClip( var inverseScale = GetInverseScale(targetScale); - var sourceLeft = axis is Axis.Y ? 0 : sourceCenterPoint.X - targetCenterPoint.X; - var sourceTop = axis is Axis.X ? 0 : sourceCenterPoint.Y - targetCenterPoint.Y; - var targetLeft = axis is Axis.Y ? 0 : targetCenterPoint.X - sourceCenterPoint.X; - var targetTop = axis is Axis.X ? 0 : targetCenterPoint.Y - sourceCenterPoint.Y; - var sourceEndViewportWidth = axis is Axis.Y ? sourceActualSize.X * targetScale.X : targetActualSize.X; - var sourceEndViewportHeight = axis is Axis.X ? sourceActualSize.Y * targetScale.Y : targetActualSize.Y; - var targetBeginViewportWidth = axis is Axis.Y ? targetActualSize.X * inverseScale.X : sourceActualSize.X; - var targetBeginViewportHeight = axis is Axis.X ? targetActualSize.Y * inverseScale.Y : sourceActualSize.Y; - - var scaleMatrix = Matrix3x2.CreateScale(targetScale, sourceCenterPoint); - var inverseScaleMatrix = Matrix3x2.CreateScale(inverseScale, targetCenterPoint); + var sourceEndViewportLeft = (axis is Axis.Y ? -sourceCenterPoint.X * targetScale.X : -targetCenterPoint.X) + (sourceCenterPoint.X * targetScale.X); + var sourceEndViewportTop = (axis is Axis.X ? -sourceCenterPoint.Y * targetScale.Y : -targetCenterPoint.Y) + (sourceCenterPoint.Y * targetScale.Y); + var sourceEndViewportRight = (axis is Axis.Y ? (sourceActualSize.X - sourceCenterPoint.X) * targetScale.X : targetActualSize.X - targetCenterPoint.X) + (sourceCenterPoint.X * targetScale.X); + var sourceEndViewportBottom = (axis is Axis.X ? (sourceActualSize.Y - sourceCenterPoint.Y) * targetScale.Y : targetActualSize.Y - targetCenterPoint.Y) + (sourceCenterPoint.Y * targetScale.Y); - Matrix3x2.Invert(scaleMatrix, out Matrix3x2 scaleMatrixInvert); - Matrix3x2.Invert(inverseScaleMatrix, out Matrix3x2 inverseScaleMatrixInvert); + var targetBeginViewportLeft = (axis is Axis.Y ? -targetCenterPoint.X * inverseScale.X : -sourceCenterPoint.X) + (targetCenterPoint.X * inverseScale.X); + var targetBeginViewportTop = (axis is Axis.X ? -targetCenterPoint.Y * inverseScale.Y : -sourceCenterPoint.Y) + (targetCenterPoint.Y * inverseScale.Y); + var targetBeginViewportRight = (axis is Axis.Y ? (targetActualSize.X - targetCenterPoint.X) * inverseScale.X : sourceActualSize.X - sourceCenterPoint.X) + (targetCenterPoint.X * inverseScale.X); + var targetBeginViewportBottom = (axis is Axis.X ? (targetActualSize.Y - targetCenterPoint.Y) * inverseScale.Y : sourceActualSize.Y - sourceCenterPoint.Y) + (targetCenterPoint.Y * inverseScale.Y); - var sourceLeftTop = Vector2.Transform(new Vector2(sourceLeft, sourceTop), scaleMatrixInvert); - var sourceRightBottom = Vector2.Transform(new Vector2(sourceLeft + sourceEndViewportWidth, sourceTop + sourceEndViewportHeight), scaleMatrixInvert); + var sourceLeftTop = new Vector2(sourceEndViewportLeft, sourceEndViewportTop) * inverseScale; + var sourceRightBottom = new Vector2(sourceEndViewportRight, sourceEndViewportBottom) * inverseScale; var sourceRight = sourceActualSize.X - sourceRightBottom.X; var sourceBottom = sourceActualSize.Y - sourceRightBottom.Y; - var targetLeftTop = Vector2.Transform(new Vector2(targetLeft, targetTop), inverseScaleMatrixInvert); - var targetRightBottom = Vector2.Transform(new Vector2(targetLeft + targetBeginViewportWidth, targetTop + targetBeginViewportHeight), inverseScaleMatrixInvert); + var targetLeftTop = new Vector2(targetBeginViewportLeft, targetBeginViewportTop) * targetScale; + var targetRightBottom = new Vector2(targetBeginViewportRight, targetBeginViewportBottom) * targetScale; var targetRight = targetActualSize.X - targetRightBottom.X; var targetBottom = targetActualSize.Y - targetRightBottom.Y; From a2c41166f91b6e30e4c752b96042c73c6ca06d46 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Thu, 21 Jul 2022 23:00:07 +0800 Subject: [PATCH 51/94] Add KeyFrameAnimationGroupController --- .../Helpers/TransitionHelper.Animation.cs | 370 +++++++++++++++++ .../Helpers/TransitionHelper.Helpers.cs | 21 +- .../Helpers/TransitionHelper.Logic.cs | 391 +++++++++--------- .../Helpers/TransitionHelper.Properties.cs | 6 + .../Helpers/TransitionHelper.cs | 78 ++-- 5 files changed, 627 insertions(+), 239 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs new file mode 100644 index 00000000000..e8d274d0111 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -0,0 +1,370 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Windows.UI.Composition; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Hosting; +using Windows.UI.Xaml.Media.Animation; + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// A animation helper that morphs between two controls. + /// + public sealed partial class TransitionHelper + { + private interface IKeyFrameCompositionAnimationFactory + { + KeyFrameAnimation GetAnimation(CompositionObject targetHint, out CompositionObject target); + } + + private interface IKeyFrameAnimationGroupController + { + Task StartAsync(CancellationToken token, TimeSpan? duration, float? startProgress); + + Task ReverseAsync(CancellationToken token, TimeSpan? duration, float? startProgress); + + void AddAnimationFor(UIElement target, IKeyFrameCompositionAnimationFactory factory); + + void AddAnimationGroupFor(UIElement target, IKeyFrameCompositionAnimationFactory[] factories); + } + + private sealed record KeyFrameAnimationFactory( + string Property, + T To, + T? From, + TimeSpan? Delay, + TimeSpan? Duration, + EasingType? EasingType, + EasingMode? EasingMode, + Dictionary NormalizedKeyFrames) + : IKeyFrameCompositionAnimationFactory + where T : unmanaged + { + public KeyFrameAnimation GetAnimation(CompositionObject targetHint, out CompositionObject target) + { + target = null; + + if (typeof(T) == typeof(float)) + { + var scalarAnimation = targetHint.Compositor.CreateScalarKeyFrameAnimation( + Property, + CastTo(To), + CastToNullable(From), + Delay, + Duration, + GetEasingFunction(targetHint.Compositor, EasingType, EasingMode), + iterationBehavior: AnimationIterationBehavior.Count, + iterationCount: 1); + if (NormalizedKeyFrames?.Count > 0) + { + foreach (var item in NormalizedKeyFrames) + { + var (value, easingType, easingMode) = item.Value; + scalarAnimation.InsertKeyFrame(item.Key, CastTo(value), GetEasingFunction(targetHint.Compositor, easingType, easingMode)); + } + } + + return scalarAnimation; + } + + if (typeof(T) == typeof(Vector2)) + { + var vector2Animation = targetHint.Compositor.CreateVector2KeyFrameAnimation( + Property, + CastTo(To), + CastToNullable(From), + Delay, + Duration, + GetEasingFunction(targetHint.Compositor, EasingType, EasingMode), + iterationBehavior: AnimationIterationBehavior.Count, + iterationCount: 1); + if (NormalizedKeyFrames?.Count > 0) + { + foreach (var item in NormalizedKeyFrames) + { + var (value, easingType, easingMode) = item.Value; + vector2Animation.InsertKeyFrame(item.Key, CastTo(value), GetEasingFunction(targetHint.Compositor, easingType, easingMode)); + } + } + + return vector2Animation; + } + + throw new InvalidOperationException("Invalid animation type"); + } + + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private TValue CastTo(T value) + where TValue : unmanaged + { + return (TValue)(object)value; + } + + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private TValue? CastToNullable(T? value) + where TValue : unmanaged + { + if (value is null) + { + return null; + } + + T validValue = value.GetValueOrDefault(); + + return (TValue)(object)validValue; + } + } + + private sealed record ClipScalarAnimationFactory( + string Property, + float To, + float? From, + TimeSpan? Delay, + TimeSpan? Duration, + EasingType? EasingType, + EasingMode? EasingMode) + : IKeyFrameCompositionAnimationFactory + { + public KeyFrameAnimation GetAnimation(CompositionObject targetHint, out CompositionObject target) + { + Visual visual = (Visual)targetHint; + InsetClip clip = visual.Clip as InsetClip ?? (InsetClip)(visual.Clip = visual.Compositor.CreateInsetClip()); + CompositionEasingFunction easingFunction = GetEasingFunction(clip.Compositor, EasingType, EasingMode); + ScalarKeyFrameAnimation animation = clip.Compositor.CreateScalarKeyFrameAnimation( + Property, + To, + From, + Delay, + Duration, + easingFunction, + iterationBehavior: AnimationIterationBehavior.Count, + iterationCount: 1); + + target = clip; + + return animation; + } + } + + private static CompositionEasingFunction GetEasingFunction(Compositor compositor, EasingType? easingType, EasingMode? easingMode) + { + CompositionEasingFunction easingFunction = null; + if (easingType.HasValue && easingMode.HasValue) + { + easingFunction = compositor.TryCreateEasingFunction(easingType.Value, easingMode.Value); + } + + return easingFunction; + } + + private IKeyFrameCompositionAnimationFactory[] Clip( + Thickness to, + Thickness? from = null, + TimeSpan? delay = null, + TimeSpan? duration = null, + EasingType easingType = EasingType.Default, + EasingMode easingMode = EasingMode.EaseInOut) + { + return new[] + { + new ClipScalarAnimationFactory( + nameof(InsetClip.LeftInset), + (float)to.Left, + (float?)from?.Left, + delay, + duration, + easingType, + easingMode), + new ClipScalarAnimationFactory( + nameof(InsetClip.TopInset), + (float)to.Top, + (float?)from?.Top, + delay, + duration, + easingType, + easingMode), + new ClipScalarAnimationFactory( + nameof(InsetClip.RightInset), + (float)to.Right, + (float?)from?.Right, + delay, + duration, + easingType, + easingMode), + new ClipScalarAnimationFactory( + nameof(InsetClip.BottomInset), + (float)to.Bottom, + (float?)from?.Bottom, + delay, + duration, + easingType, + easingMode) + }; + } + + private IKeyFrameCompositionAnimationFactory Translation( + Vector2 to, + Vector2? from = null, + TimeSpan? delay = null, + TimeSpan? duration = null, + EasingType? easingType = null, + EasingMode? easingMode = null, + Dictionary normalizedKeyFrames = null) + { + return new KeyFrameAnimationFactory("Translation.XY", to, from, delay, duration, easingType, easingMode, normalizedKeyFrames); + } + + private IKeyFrameCompositionAnimationFactory Opacity( + double to, + double? from = null, + TimeSpan? delay = null, + TimeSpan? duration = null, + EasingType? easingType = null, + EasingMode? easingMode = null, + Dictionary normalizedKeyFrames = null) + { + return new KeyFrameAnimationFactory(nameof(Visual.Opacity), (float)to, (float?)from, delay, duration, easingType, easingMode, normalizedKeyFrames); + } + + private IKeyFrameCompositionAnimationFactory Scale( + Vector2 to, + Vector2? from = null, + TimeSpan? delay = null, + TimeSpan? duration = null, + EasingType? easingType = null, + EasingMode? easingMode = null, + Dictionary normalizedKeyFrames = null) + { + return new KeyFrameAnimationFactory("Scale.XY", to, from, delay, duration, easingType, easingMode, normalizedKeyFrames); + } + + private sealed class KeyFrameAnimationGroupController : IKeyFrameAnimationGroupController + { + private readonly Dictionary> animationFactories = new(); + + public void AddAnimationFor(UIElement target, IKeyFrameCompositionAnimationFactory factory) + { + if(factory is null) + { + return; + } + + if (animationFactories.ContainsKey(target)) + { + animationFactories[target].Add(factory); + } + else + { + animationFactories.Add(target, new List() { factory }); + } + } + + public void AddAnimationGroupFor(UIElement target, IKeyFrameCompositionAnimationFactory[] factories) + { + var validFactories = factories.Where(factory => factory is not null); + if (validFactories.Any() is false) + { + return; + } + + if (animationFactories.ContainsKey(target)) + { + animationFactories[target].AddRange(validFactories); + } + else + { + animationFactories.Add(target, new List(validFactories)); + } + } + + public Task StartAsync(CancellationToken token, TimeSpan? duration, float? startProgress) + { + return AnimateAsync(false, token, duration, startProgress); + } + + public Task ReverseAsync(CancellationToken token, TimeSpan? duration, float? startProgress) + { + return AnimateAsync(true, token, duration, startProgress); + } + + private Task AnimateAsync(bool reversed, CancellationToken token, TimeSpan? duration, float? startProgress) + { + List tasks = null; + List<(CompositionObject Target, string Path)> compositionAnimations = null; + if (this.animationFactories.Count > 0) + { + tasks = new List(this.animationFactories.Count); + compositionAnimations = new List<(CompositionObject Target, string Path)>(); + foreach (var item in this.animationFactories) + { + tasks.Add(StartFor(item.Key, reversed, duration, startProgress, compositionAnimations)); + } + } + + static void Stop(object state) + { + if (state is List<(CompositionObject Target, string Path)> animations) + { + foreach (var (target, path) in animations) + { + target.StopAnimation(path); + } + } + } + + token.Register(static obj => Stop(obj), compositionAnimations); + return Task.WhenAll(tasks); + } + + private Task StartFor(UIElement element, bool reversed, TimeSpan? duration, float? startProgress, List<(CompositionObject Target, string Path)> animations) + { + if (!this.animationFactories.TryGetValue(element, out var factories) || factories.Count > 0 is false) + { + return Task.CompletedTask; + } + + ElementCompositionPreview.SetIsTranslationEnabled(element, true); + var visual = element.GetVisual(); + CompositionScopedBatch batch = visual.Compositor.CreateScopedBatch(CompositionBatchTypes.Animation); + TaskCompletionSource taskCompletionSource = new(); + batch.Completed += (_, _) => taskCompletionSource.SetResult(null); + foreach (var factory in factories) + { + var animation = factory.GetAnimation(visual, out var target); + if (duration.HasValue) + { + animation.Duration = duration.Value; + } + + if (reversed) + { + animation.Direction = AnimationDirection.Reverse; + } + + (target ?? visual).StartAnimation(animation.Target, animation); + if (startProgress.HasValue) + { + var controller = (target ?? visual).TryGetAnimationController(animation.Target); + if (controller is not null) + { + controller.Progress = startProgress.Value; + } + } + + animations.Add((target ?? visual, animation.Target)); + } + + batch.End(); + return taskCompletionSource.Task; + } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index 9d03cf15283..a3704c19692 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -65,16 +65,23 @@ private static void RestoreElements(IEnumerable animatedElements) { foreach (var animatedElement in animatedElements) { - ElementCompositionPreview.SetIsTranslationEnabled(animatedElement, true); - var visual = animatedElement.GetVisual(); - visual.Opacity = 1; - visual.Scale = Vector3.One; - visual.Clip = null; - visual.Properties.InsertVector3("Translation", Vector3.Zero); + RestoreElement(animatedElement); } } - private static Vector2 GetInverseScale(Vector2 scale) => new Vector2(1 / scale.X, 1 / scale.Y); + private static void RestoreElement(UIElement animatedElement) + { + ElementCompositionPreview.SetIsTranslationEnabled(animatedElement, true); + var visual = animatedElement.GetVisual(); + visual.Opacity = 1; + visual.Scale = Vector3.One; + visual.Clip = null; + visual.Properties.InsertVector3("Translation", Vector3.Zero); + } + + private static Vector2 GetInverseScale(Vector2 scale) => new(1 / scale.X, 1 / scale.Y); + + private static Vector2 GetXY(Vector3 value) => new(value.X, value.Y); private static Thickness GetFixedThickness(Thickness thickness, double defaultValue) { diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 12c9c381f87..10e9ec54648 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -62,96 +62,21 @@ private void UpdateAnimatedElements(DependencyObject parent, IDictionary(); - var sourceUnpairedElements = this.sourceConnectedAnimatedElements - .Where(item => !this.targetConnectedAnimatedElements.ContainsKey(item.Key)) - .Select(item => item.Value); - var targetUnpairedElements = this.targetConnectedAnimatedElements - .Where(item => !this.sourceConnectedAnimatedElements.ContainsKey(item.Key)) - .Select(item => item.Value); - var pairedElementKeys = this.sourceConnectedAnimatedElements - .Where(item => this.targetConnectedAnimatedElements.ContainsKey(item.Key)) - .Select(item => item.Key); - foreach (var key in pairedElementKeys) - { - var source = this.sourceConnectedAnimatedElements[key]; - var target = this.targetConnectedAnimatedElements[key]; - var animationConfig = this.Configs.FirstOrDefault(config => config.Id == key) ?? - this.DefaultConfig; - animationTasks.Add( - this.AnimateElements( - reversed ? target : source, - reversed ? source : target, - duration, - animationConfig, - token)); - } - - animationTasks.Add( - this.AnimateIndependentElements( - this.sourceIndependentAnimatedElements.Concat(sourceUnpairedElements), - reversed, - token, - IndependentElementEasingType, - IndependentElementEasingMode)); - animationTasks.Add( - this.AnimateIndependentElements( - this.targetIndependentAnimatedElements.Concat(targetUnpairedElements), - !reversed, - token, - IndependentElementEasingType, - IndependentElementEasingMode)); - - return Task.WhenAll(animationTasks); - } + this._lastAnimationDuration = totalDuration * (1 - startProgress); + _lastAnimationStartTime = DateTime.Now; - private async Task StartInterruptibleAnimationsAsync(bool reversed, CancellationToken token, bool forceUpdateAnimatedElements) - { - if (!this._isInterruptedAnimation && IsTargetState != reversed) - { - return; - } - - if (token.IsCancellationRequested) - { - return; - } - - TaskCompletionSource currentTaskSource; - if (reversed) - { - currentTaskSource = this._reverseTaskSource = new(); - } - else - { - currentTaskSource = this._animateTaskSource = new(); - } - - await this.InitControlsStateAsync(forceUpdateAnimatedElements); + await this.AnimateControls(totalDuration, startProgress, reversed, token); if (token.IsCancellationRequested) { - _ = currentTaskSource.TrySetResult(false); - return; - } - - await this.AnimateControls(reversed, token); - if (token.IsCancellationRequested) - { - _ = currentTaskSource.TrySetResult(false); return; } this.RestoreState(!reversed); - _ = currentTaskSource.TrySetResult(true); - + this._lastAnimationStartTime = null; + this._lastAnimationDuration = null; + this._lastStartProgress = null; this._isInterruptedAnimation = false; } @@ -231,62 +156,128 @@ void OnTargetLayoutUpdated(object sender, object e) return updateTargetLayoutTaskSource.Task; } - private Task AnimateElements(UIElement source, UIElement target, TimeSpan duration, TransitionConfig config, CancellationToken token) + private Task AnimateControls(TimeSpan duration, float? startProgress, bool reversed, CancellationToken token) { - var sourceBuilder = AnimationBuilder.Create(); - var targetBuilder = AnimationBuilder.Create(); + var animationTasks = new List(3); + var sourceUnpairedElements = this.sourceConnectedAnimatedElements + .Where(item => !this.targetConnectedAnimatedElements.ContainsKey(item.Key)) + .Select(item => item.Value); + var targetUnpairedElements = this.targetConnectedAnimatedElements + .Where(item => !this.sourceConnectedAnimatedElements.ContainsKey(item.Key)) + .Select(item => item.Value); + var pairedElementKeys = this.sourceConnectedAnimatedElements + .Where(item => this.targetConnectedAnimatedElements.ContainsKey(item.Key)) + .Select(item => item.Key); + if (_currentAnimationGroupController is null) + { + _currentAnimationGroupController = new KeyFrameAnimationGroupController(); + foreach (var key in pairedElementKeys) + { + var source = this.sourceConnectedAnimatedElements[key]; + var target = this.targetConnectedAnimatedElements[key]; + var animationConfig = this.Configs.FirstOrDefault(config => config.Id == key) ?? + this.DefaultConfig; + this.BuildConnectedAnimationController( + _currentAnimationGroupController, + source, + target, + duration, + animationConfig); + } + } + animationTasks.Add(reversed + ? _currentAnimationGroupController.ReverseAsync(token, duration, startProgress) + : _currentAnimationGroupController.StartAsync(token, duration, startProgress)); + + animationTasks.Add( + this.AnimateIndependentElements( + this.sourceIndependentAnimatedElements.Concat(sourceUnpairedElements), + reversed, + token, + duration, + startProgress, + IndependentElementEasingType, + IndependentElementEasingMode)); + animationTasks.Add( + this.AnimateIndependentElements( + this.targetIndependentAnimatedElements.Concat(targetUnpairedElements), + !reversed, + token, + duration, + startProgress, + IndependentElementEasingType, + IndependentElementEasingMode)); + + return Task.WhenAll(animationTasks); + } + + private void BuildConnectedAnimationController(KeyFrameAnimationGroupController controller, UIElement source, UIElement target, TimeSpan duration, TransitionConfig config) + { var sourceActualSize = source is FrameworkElement sourceElement ? new Vector2((float)sourceElement.ActualWidth, (float)sourceElement.ActualHeight) : source.ActualSize; var targetActualSize = target is FrameworkElement targetElement ? new Vector2((float)targetElement.ActualWidth, (float)targetElement.ActualHeight) : target.ActualSize; var sourceCenterPoint = sourceActualSize * config.NormalizedCenterPoint.ToVector2(); var targetCenterPoint = targetActualSize * config.NormalizedCenterPoint.ToVector2(); + var currentSourceScale = GetXY(source.GetVisual().Scale); + source.GetVisual().CenterPoint = new Vector3(sourceCenterPoint, 0); target.GetVisual().CenterPoint = new Vector3(targetCenterPoint, 0); - this.AnimateTranslation( - sourceBuilder, - targetBuilder, + var (sourceTranslationAnimation, targetTranslationAnimation) = this.AnimateTranslation( source, target, sourceCenterPoint, targetCenterPoint, + currentSourceScale, duration, config.EasingType, config.EasingMode); - this.AnimateOpacity( - sourceBuilder, - targetBuilder, - duration); - var targetScale = config.ScaleMode switch + var (sourceOpacityAnimation, targetOpacityAnimation) = this.AnimateOpacity(duration); + var (sourceScaleAnimation, targetScaleAnimation, targetScale) = config.ScaleMode switch { - ScaleMode.None => Vector2.One, + ScaleMode.None => (null, null, Vector2.One), ScaleMode.Scale => this.AnimateScale( - sourceBuilder, - targetBuilder, sourceActualSize, targetActualSize, + currentSourceScale, duration, config.EasingType, config.EasingMode), ScaleMode.ScaleX => this.AnimateScaleX( - sourceBuilder, - targetBuilder, sourceActualSize, targetActualSize, + currentSourceScale, duration, config.EasingType, config.EasingMode), ScaleMode.ScaleY => this.AnimateScaleY( - sourceBuilder, - targetBuilder, sourceActualSize, targetActualSize, + currentSourceScale, duration, config.EasingType, config.EasingMode), - _ => Vector2.One, + _ => (null, null, Vector2.One), }; + controller.AddAnimationGroupFor( + source, + new[] + { + sourceTranslationAnimation, + sourceOpacityAnimation, + sourceScaleAnimation + }); + + controller.AddAnimationGroupFor( + target, + new[] + { + targetTranslationAnimation, + targetOpacityAnimation, + targetScaleAnimation + }); + if (config is { EnableClipAnimation: true, ScaleMode: not ScaleMode.Scale }) { Axis? axis = config.ScaleMode switch @@ -297,9 +288,7 @@ private Task AnimateElements(UIElement source, UIElement target, TimeSpan durati ScaleMode.ScaleY => Axis.X, _ => null, }; - this.AnimateClip( - sourceBuilder, - targetBuilder, + var (sourceClipAnimationGroup, targetClipAnimationGroup) = this.AnimateClip( sourceActualSize, targetActualSize, sourceCenterPoint, @@ -309,15 +298,17 @@ private Task AnimateElements(UIElement source, UIElement target, TimeSpan durati config.EasingType, config.EasingMode, axis); + controller.AddAnimationGroupFor(source, sourceClipAnimationGroup); + controller.AddAnimationGroupFor(target, targetClipAnimationGroup); } - - return Task.WhenAll(sourceBuilder.StartAsync(source, token), targetBuilder.StartAsync(target, token)); } private Task AnimateIndependentElements( IEnumerable independentElements, bool isShow, CancellationToken token, + TimeSpan totalDuration, + float? startProgress, EasingType easingType, EasingMode easingMode) { @@ -326,11 +317,18 @@ private Task AnimateIndependentElements( return Task.CompletedTask; } - var animationTasks = new List(); + var startTime = TimeSpan.Zero; + if (startProgress.HasValue) + { + startTime = totalDuration * startProgress.Value; + } + + var controller = new KeyFrameAnimationGroupController(); + var duration = isShow ? this.IndependentElementShowDuration : this.IndependentElementHideDuration; var delay = isShow ? this.IndependentElementShowDelay : TimeSpan.Zero; - var translationFrom = isShow ? this.IndependentElementHideTranslation.ToVector3() : Vector3.Zero; - var translationTo = isShow ? Vector3.Zero : this.IndependentElementHideTranslation.ToVector3(); + var translationFrom = isShow ? this.IndependentElementHideTranslation.ToVector2() : Vector2.Zero; + var translationTo = isShow ? Vector2.Zero : this.IndependentElementHideTranslation.ToVector2(); var opacityFrom = isShow ? 0 : 1; var opacityTo = isShow ? 1 : 0; if (this._isInterruptedAnimation) @@ -341,65 +339,69 @@ private Task AnimateIndependentElements( foreach (var item in elements) { - var animationBuilder = AnimationBuilder.Create(); - if (Math.Abs(this.IndependentElementHideTranslation.X) > 0.01 || - Math.Abs(this.IndependentElementHideTranslation.Y) > 0.01) + if (delay < startTime) { - animationBuilder.Translation( - translationTo, - this._isInterruptedAnimation ? null : translationFrom, + if (isShow) + { + RestoreElement(item); + } + else + { + item.GetVisual().Opacity = 0; + } + } + else + { + var useDelay = delay - startTime; + if (Math.Abs(this.IndependentElementHideTranslation.X) > 0.01 || + Math.Abs(this.IndependentElementHideTranslation.Y) > 0.01) + { + controller.AddAnimationFor(item, this.Translation( + translationTo, + this._isInterruptedAnimation ? null : translationFrom, + useDelay, + duration: duration, + easingType: easingType, + easingMode: easingMode)); + } + + controller.AddAnimationFor(item, this.Opacity( + opacityTo, + this._isInterruptedAnimation ? null : opacityFrom, delay, - duration: duration, + duration, easingType: easingType, - easingMode: easingMode); + easingMode: easingMode)); } - animationBuilder.Opacity( - opacityTo, - this._isInterruptedAnimation ? null : opacityFrom, - delay, - duration, - easingType: easingType, - easingMode: easingMode); - - animationTasks.Add(animationBuilder.StartAsync(item, token)); if (isShow) { delay += this.IndependentElementShowInterval; } } - return Task.WhenAll(animationTasks); + return controller.StartAsync(token, null, null); } - private void AnimateTranslation( - AnimationBuilder sourceBuilder, - AnimationBuilder targetBuilder, + private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory) AnimateTranslation( UIElement source, UIElement target, Vector2 sourceCenterPoint, Vector2 targetCenterPoint, + Vector2 initialScale, TimeSpan duration, EasingType easingType, EasingMode easingMode) { - if (this._isInterruptedAnimation) - { - _ = sourceBuilder.Translation(Vector2.Zero, duration: almostZeroDuration); - _ = targetBuilder.Translation(Vector2.Zero, duration: duration, easingType: easingType, easingMode: easingMode); - return; - } - - var diff = target.TransformToVisual(source).TransformPoint(default).ToVector2() - sourceCenterPoint + targetCenterPoint; - _ = sourceBuilder.Translation(diff, duration: duration, easingType: easingType, easingMode: easingMode); - _ = targetBuilder.Translation(Vector2.Zero, -diff, duration: duration, easingType: easingType, easingMode: easingMode); + var diff = ((target.TransformToVisual(source).TransformPoint(default).ToVector2() - sourceCenterPoint) * initialScale) + targetCenterPoint; + return (this.Translation(diff, Vector2.Zero, duration: duration, easingType: easingType, easingMode: easingMode), + this.Translation(Vector2.Zero, -diff, duration: duration, easingType: easingType, easingMode: easingMode)); } - private Vector2 AnimateScale( - AnimationBuilder sourceBuilder, - AnimationBuilder targetBuilder, + private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory, Vector2) AnimateScale( Vector2 sourceActualSize, Vector2 targetActualSize, + Vector2 initialScale, TimeSpan duration, EasingType easingType, EasingMode easingMode) @@ -407,82 +409,66 @@ private Vector2 AnimateScale( var scaleX = targetActualSize.X / sourceActualSize.X; var scaleY = targetActualSize.Y / sourceActualSize.Y; var scale = new Vector2(scaleX, scaleY); - return this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); + var (sourceFactory, targetFactory) = this.AnimateScaleImp(scale, initialScale, duration, easingType, easingMode); + return (sourceFactory, targetFactory, scale); } - private Vector2 AnimateScaleX( - AnimationBuilder sourceBuilder, - AnimationBuilder targetBuilder, + private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory, Vector2) AnimateScaleX( Vector2 sourceActualSize, Vector2 targetActualSize, + Vector2 initialScale, TimeSpan duration, EasingType easingType, EasingMode easingMode) { var scaleX = targetActualSize.X / sourceActualSize.X; var scale = new Vector2(scaleX, scaleX); - return this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); + var (sourceFactory, targetFactory) = this.AnimateScaleImp(scale, initialScale, duration, easingType, easingMode); + return (sourceFactory, targetFactory, scale); } - private Vector2 AnimateScaleY( - AnimationBuilder sourceBuilder, - AnimationBuilder targetBuilder, + private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory, Vector2) AnimateScaleY( Vector2 sourceActualSize, Vector2 targetActualSize, + Vector2 initialScale, TimeSpan duration, EasingType easingType, EasingMode easingMode) { var scaleY = targetActualSize.Y / sourceActualSize.Y; var scale = new Vector2(scaleY, scaleY); - return this.AnimateScale(sourceBuilder, targetBuilder, scale, duration, easingType, easingMode); + var (sourceFactory, targetFactory) = this.AnimateScaleImp(scale, initialScale, duration, easingType, easingMode); + return (sourceFactory, targetFactory, scale); } - private Vector2 AnimateScale( - AnimationBuilder sourceBuilder, - AnimationBuilder targetBuilder, + private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory) AnimateScaleImp( Vector2 targetScale, + Vector2 initialScale, TimeSpan duration, EasingType easingType, EasingMode easingMode) { - if (this._isInterruptedAnimation) - { - _ = sourceBuilder.Scale(Vector2.One, duration: almostZeroDuration); - _ = targetBuilder.Scale(Vector2.One, duration: duration, easingType: easingType, easingMode: easingMode); - } - - _ = sourceBuilder.Scale(targetScale, duration: duration, easingType: easingType, easingMode: easingMode); - _ = targetBuilder.Scale(Vector2.One, GetInverseScale(targetScale), duration: duration, easingType: easingType, easingMode: easingMode); - return targetScale; + return (this.Scale(targetScale, initialScale, duration: duration, easingType: easingType, easingMode: easingMode), + this.Scale(Vector2.One, GetInverseScale(targetScale / initialScale), duration: duration, easingType: easingType, easingMode: easingMode)); } - private void AnimateOpacity( - AnimationBuilder sourceBuilder, - AnimationBuilder targetBuilder, - TimeSpan duration) + private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory) AnimateOpacity(TimeSpan duration) { - if (this._isInterruptedAnimation) - { - _ = sourceBuilder.Opacity(0, duration: almostZeroDuration); - _ = targetBuilder.Opacity(1, duration: almostZeroDuration); - return; - } - var useDuration = TimeSpan.FromMilliseconds(Math.Min(200, duration.TotalMilliseconds / 3)); - _ = sourceBuilder.Opacity().TimedKeyFrames( - b => b - .KeyFrame(TimeSpan.Zero, 1) - .KeyFrame(useDuration, 0, EasingType.Cubic, EasingMode.EaseIn)); - _ = targetBuilder.Opacity().TimedKeyFrames( - b => b - .KeyFrame(TimeSpan.Zero, 0) - .KeyFrame(useDuration, 1, EasingType.Cubic, EasingMode.EaseOut)); + var normalizedProgress = (float)(useDuration / duration); + var sourceNormalizedKeyFrames = new Dictionary + { + [normalizedProgress] = (0, EasingType.Cubic, EasingMode.EaseIn) + }; + var targetNormalizedKeyFrames = new Dictionary + { + [normalizedProgress] = (1, EasingType.Cubic, EasingMode.EaseOut) + }; + return (this.Opacity(0, 1, duration: duration, normalizedKeyFrames: sourceNormalizedKeyFrames), + this.Opacity(1, 0, duration: duration, normalizedKeyFrames: targetNormalizedKeyFrames)); } - private void AnimateClip( - AnimationBuilder sourceBuilder, - AnimationBuilder targetBuilder, + private (IKeyFrameCompositionAnimationFactory[], IKeyFrameCompositionAnimationFactory[]) AnimateClip( Vector2 sourceActualSize, Vector2 targetActualSize, Vector2 sourceCenterPoint, @@ -496,13 +482,6 @@ private void AnimateClip( // -4 is used to prevent shadows from being cropped. var defaultValue = -4; var defaultThickness = new Thickness(defaultValue); - if (this._isInterruptedAnimation) - { - _ = sourceBuilder.Clip(defaultThickness, duration: almostZeroDuration); - _ = targetBuilder.Clip(defaultThickness, duration: duration, easingType: easingType, easingMode: easingMode); - return; - } - var inverseScale = GetInverseScale(targetScale); var sourceEndViewportLeft = (axis is Axis.Y ? -sourceCenterPoint.X * targetScale.X : -targetCenterPoint.X) + (sourceCenterPoint.X * targetScale.X); @@ -525,18 +504,20 @@ private void AnimateClip( var targetRight = targetActualSize.X - targetRightBottom.X; var targetBottom = targetActualSize.Y - targetRightBottom.Y; - _ = sourceBuilder.Clip( - GetFixedThickness(new Thickness(sourceLeftTop.X, sourceLeftTop.Y, sourceRight, sourceBottom), defaultValue), - from: defaultThickness, - duration: duration, - easingType: easingType, - easingMode: easingMode); - _ = targetBuilder.Clip( - defaultThickness, - GetFixedThickness(new Thickness(targetLeftTop.X, targetLeftTop.Y, targetRight, targetBottom), defaultValue), - duration: duration, - easingType: easingType, - easingMode: easingMode); + return ( + this.Clip( + GetFixedThickness(new Thickness(sourceLeftTop.X, sourceLeftTop.Y, sourceRight, sourceBottom), defaultValue), + from: defaultThickness, + duration: duration, + easingType: easingType, + easingMode: easingMode), + this.Clip( + defaultThickness, + GetFixedThickness(new Thickness(targetLeftTop.X, targetLeftTop.Y, targetRight, targetBottom), defaultValue), + duration: duration, + easingType: easingType, + easingMode: easingMode) + ); } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index f16feccd587..acec36c01c3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -120,6 +120,12 @@ public FrameworkElement Target /// public TimeSpan Duration { get; set; } = TimeSpan.FromMilliseconds(600); + /// + /// Gets or sets the reverse duration of the connected animation between two UI elements. + /// The default value is 600ms. + /// + public TimeSpan ReverseDuration { get; set; } = TimeSpan.FromMilliseconds(600); + /// /// Gets or sets the duration of the show animation for independent or unpaired UI elements. /// The default value is 200ms. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index e6b656e079d..2a43583c0cb 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -23,15 +23,17 @@ public sealed partial class TransitionHelper private readonly Dictionary targetConnectedAnimatedElements = new(); private readonly List sourceIndependentAnimatedElements = new(); private readonly List targetIndependentAnimatedElements = new(); - private readonly TimeSpan almostZeroDuration = TimeSpan.FromMilliseconds(1); private CancellationTokenSource _animateCancellationTokenSource; private CancellationTokenSource _reverseCancellationTokenSource; - private TaskCompletionSource _animateTaskSource; - private TaskCompletionSource _reverseTaskSource; private bool _needUpdateSourceLayout; private bool _needUpdateTargetLayout; private bool _isInterruptedAnimation; + private DateTime? _lastAnimationStartTime; + private float? _lastStartProgress; + private TimeSpan? _lastAnimationDuration; + + private KeyFrameAnimationGroupController _currentAnimationGroupController; private IEnumerable SourceAnimatedElements => this.sourceConnectedAnimatedElements.Values.Concat(this.sourceIndependentAnimatedElements); @@ -71,21 +73,24 @@ public async Task StartAsync(CancellationToken token, bool forceUpdateAnimatedEl if (this._reverseCancellationTokenSource is not null) { - if (this._isInterruptedAnimation) - { - this._isInterruptedAnimation = false; - await this._reverseTaskSource.Task; - } - else - { - this._reverseCancellationTokenSource.Cancel(); - this._isInterruptedAnimation = true; - } + this._isInterruptedAnimation = !this._isInterruptedAnimation; + this._reverseCancellationTokenSource.Cancel(); + this._lastStartProgress = GetStartProgress(this.ReverseDuration); + this._reverseCancellationTokenSource = null; + } + else if (this.IsTargetState) + { + return; + } + else + { + this._currentAnimationGroupController = null; + await this.InitControlsStateAsync(forceUpdateAnimatedElements); } this._animateCancellationTokenSource = new CancellationTokenSource(); - var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, _animateCancellationTokenSource.Token); - await StartInterruptibleAnimationsAsync(false, linkedTokenSource.Token, forceUpdateAnimatedElements); + var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, this._animateCancellationTokenSource.Token); + await StartInterruptibleAnimationsAsync(false, linkedTokenSource.Token, this._lastStartProgress, this.Duration); this._animateCancellationTokenSource = null; } @@ -123,21 +128,24 @@ public async Task ReverseAsync(CancellationToken token, bool forceUpdateAnimated if (this._animateCancellationTokenSource is not null) { - if (this._isInterruptedAnimation) - { - this._isInterruptedAnimation = false; - await this._animateTaskSource.Task; - } - else - { - this._animateCancellationTokenSource.Cancel(); - this._isInterruptedAnimation = true; - } + this._isInterruptedAnimation = !this._isInterruptedAnimation; + this._animateCancellationTokenSource.Cancel(); + this._lastStartProgress = GetStartProgress(this.Duration); + this._animateCancellationTokenSource = null; + } + else if (this.IsTargetState is false) + { + return; + } + else + { + this._currentAnimationGroupController = null; + await this.InitControlsStateAsync(forceUpdateAnimatedElements); } this._reverseCancellationTokenSource = new CancellationTokenSource(); - var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, _reverseCancellationTokenSource.Token); - await StartInterruptibleAnimationsAsync(true, linkedTokenSource.Token, forceUpdateAnimatedElements); + var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, this._reverseCancellationTokenSource.Token); + await StartInterruptibleAnimationsAsync(true, linkedTokenSource.Token, this._lastStartProgress, this.ReverseDuration); this._reverseCancellationTokenSource = null; } @@ -162,5 +170,21 @@ public void Reset(bool toInitialState = true) this._isInterruptedAnimation = false; this.RestoreState(!toInitialState); } + + private float? GetStartProgress(TimeSpan lastTotalDuration) + { + var startProgress = 1 - this._lastStartProgress ?? 1; + if (_lastAnimationStartTime.HasValue) + { + var elapsedProgress = (DateTime.Now - _lastAnimationStartTime.Value) / lastTotalDuration; + startProgress -= (float)elapsedProgress; + } + else + { + return null; + } + + return Math.Max(0, Math.Min(startProgress, 1)); + } } } From 2d0dcf2e88eb11d75564986f5e76a7375497f1ed Mon Sep 17 00:00:00 2001 From: hhchaos Date: Thu, 21 Jul 2022 23:17:37 +0800 Subject: [PATCH 52/94] Update TransitionHelper.Animation.cs --- .../Helpers/TransitionHelper.Animation.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index e8d274d0111..c3b215fea87 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -1,3 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System; using System.Collections.Generic; using System.Diagnostics.Contracts; From db7fd0b777648654fe6bc4f888cf63d3c0463b9c Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 22 Jul 2022 12:45:10 +0800 Subject: [PATCH 53/94] update KeyFrameAnimationGroupController --- .../Helpers/TransitionHelper.Animation.cs | 52 ++++++++++++----- .../Helpers/TransitionHelper.Helpers.cs | 8 +-- .../Helpers/TransitionHelper.Logic.cs | 56 +++++++------------ .../Helpers/TransitionHelper.cs | 31 +--------- 4 files changed, 65 insertions(+), 82 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index e8d274d0111..b5714fae385 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -25,6 +25,8 @@ private interface IKeyFrameCompositionAnimationFactory private interface IKeyFrameAnimationGroupController { + float? LastStopProgress { get; } + Task StartAsync(CancellationToken token, TimeSpan? duration, float? startProgress); Task ReverseAsync(CancellationToken token, TimeSpan? duration, float? startProgress); @@ -250,9 +252,11 @@ private sealed class KeyFrameAnimationGroupController : IKeyFrameAnimationGroupC { private readonly Dictionary> animationFactories = new(); + public float? LastStopProgress { get; private set; } = null; + public void AddAnimationFor(UIElement target, IKeyFrameCompositionAnimationFactory factory) { - if(factory is null) + if (factory is null) { return; } @@ -285,46 +289,64 @@ public void AddAnimationGroupFor(UIElement target, IKeyFrameCompositionAnimation } } - public Task StartAsync(CancellationToken token, TimeSpan? duration, float? startProgress) + public Task StartAsync(CancellationToken token, TimeSpan? duration = null, float? startProgress = null) { - return AnimateAsync(false, token, duration, startProgress); + return AnimateAsync(false, token, duration, startProgress ?? this.LastStopProgress); } - public Task ReverseAsync(CancellationToken token, TimeSpan? duration, float? startProgress) + public Task ReverseAsync(CancellationToken token, TimeSpan? duration = null, float? startProgress = null) { - return AnimateAsync(true, token, duration, startProgress); + return AnimateAsync(true, token, duration, startProgress ?? (1 - this.LastStopProgress)); } private Task AnimateAsync(bool reversed, CancellationToken token, TimeSpan? duration, float? startProgress) { List tasks = null; List<(CompositionObject Target, string Path)> compositionAnimations = null; + DateTime? animationStartTime = null; + this.LastStopProgress = null; if (this.animationFactories.Count > 0) { + if (duration.HasValue) + { + var elapsedDuration = duration.Value * (startProgress ?? 0d); + animationStartTime = DateTime.Now - elapsedDuration; + } + tasks = new List(this.animationFactories.Count); compositionAnimations = new List<(CompositionObject Target, string Path)>(); foreach (var item in this.animationFactories) { - tasks.Add(StartFor(item.Key, reversed, duration, startProgress, compositionAnimations)); + tasks.Add(StartForAsync(item.Key, reversed, duration, startProgress, compositionAnimations)); } } static void Stop(object state) { - if (state is List<(CompositionObject Target, string Path)> animations) + var (controller, reversed, duration, animationStartTime, animations) = ((KeyFrameAnimationGroupController, bool, TimeSpan?, DateTime?, List<(CompositionObject Target, string Path)>))state; + foreach (var (target, path) in animations) { - foreach (var (target, path) in animations) - { - target.StopAnimation(path); - } + target.StopAnimation(path); } + + if (duration.HasValue is false || animationStartTime.HasValue is false) + { + return; + } + + var stopProgress = Math.Max(0, Math.Min((DateTime.Now - animationStartTime.Value) / duration.Value, 1)); + controller.LastStopProgress = (float)(reversed ? 1 - stopProgress : stopProgress); + } + + if (compositionAnimations is not null) + { + token.Register(static obj => Stop(obj), (this, reversed, duration, animationStartTime, compositionAnimations)); } - token.Register(static obj => Stop(obj), compositionAnimations); return Task.WhenAll(tasks); } - private Task StartFor(UIElement element, bool reversed, TimeSpan? duration, float? startProgress, List<(CompositionObject Target, string Path)> animations) + private Task StartForAsync(UIElement element, bool reversed, TimeSpan? duration, float? startProgress, List<(CompositionObject Target, string Path)> animations) { if (!this.animationFactories.TryGetValue(element, out var factories) || factories.Count > 0 is false) { @@ -333,8 +355,8 @@ private Task StartFor(UIElement element, bool reversed, TimeSpan? duration, floa ElementCompositionPreview.SetIsTranslationEnabled(element, true); var visual = element.GetVisual(); - CompositionScopedBatch batch = visual.Compositor.CreateScopedBatch(CompositionBatchTypes.Animation); - TaskCompletionSource taskCompletionSource = new(); + var batch = visual.Compositor.CreateScopedBatch(CompositionBatchTypes.Animation); + var taskCompletionSource = new TaskCompletionSource(); batch.Completed += (_, _) => taskCompletionSource.SetResult(null); foreach (var factory in factories) { diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index a3704c19692..ca210bd80b7 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -85,10 +85,10 @@ private static void RestoreElement(UIElement animatedElement) private static Thickness GetFixedThickness(Thickness thickness, double defaultValue) { - var left = thickness.Left < 0.1 ? defaultValue : thickness.Left; - var top = thickness.Top < 0.1 ? defaultValue : thickness.Top; - var right = thickness.Right < 0.1 ? defaultValue : thickness.Right; - var bottom = thickness.Bottom < 0.1 ? defaultValue : thickness.Bottom; + var left = thickness.Left < AlmostZero ? defaultValue : thickness.Left; + var top = thickness.Top < AlmostZero ? defaultValue : thickness.Top; + var right = thickness.Right < AlmostZero ? defaultValue : thickness.Right; + var bottom = thickness.Bottom < AlmostZero ? defaultValue : thickness.Bottom; return new Thickness(left, top, right, bottom); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 10e9ec54648..2745d6f1add 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -62,22 +62,15 @@ private void UpdateAnimatedElements(DependencyObject parent, IDictionary(3); var sourceUnpairedElements = this.sourceConnectedAnimatedElements @@ -186,17 +179,23 @@ private Task AnimateControls(TimeSpan duration, float? startProgress, bool rever } } + TimeSpan? startTime = null; + if (_currentAnimationGroupController.LastStopProgress.HasValue) + { + var startProgress = reversed ? (1 - _currentAnimationGroupController.LastStopProgress.Value) : _currentAnimationGroupController.LastStopProgress.Value; + startTime = startProgress * duration; + } + animationTasks.Add(reversed - ? _currentAnimationGroupController.ReverseAsync(token, duration, startProgress) - : _currentAnimationGroupController.StartAsync(token, duration, startProgress)); + ? _currentAnimationGroupController.ReverseAsync(token, duration) + : _currentAnimationGroupController.StartAsync(token, duration)); animationTasks.Add( this.AnimateIndependentElements( this.sourceIndependentAnimatedElements.Concat(sourceUnpairedElements), reversed, token, - duration, - startProgress, + startTime, IndependentElementEasingType, IndependentElementEasingMode)); animationTasks.Add( @@ -204,8 +203,7 @@ private Task AnimateControls(TimeSpan duration, float? startProgress, bool rever this.targetIndependentAnimatedElements.Concat(targetUnpairedElements), !reversed, token, - duration, - startProgress, + startTime, IndependentElementEasingType, IndependentElementEasingMode)); @@ -307,8 +305,7 @@ private Task AnimateIndependentElements( IEnumerable independentElements, bool isShow, CancellationToken token, - TimeSpan totalDuration, - float? startProgress, + TimeSpan? startTime, EasingType easingType, EasingMode easingMode) { @@ -317,12 +314,6 @@ private Task AnimateIndependentElements( return Task.CompletedTask; } - var startTime = TimeSpan.Zero; - if (startProgress.HasValue) - { - startTime = totalDuration * startProgress.Value; - } - var controller = new KeyFrameAnimationGroupController(); var duration = isShow ? this.IndependentElementShowDuration : this.IndependentElementHideDuration; @@ -331,15 +322,10 @@ private Task AnimateIndependentElements( var translationTo = isShow ? Vector2.Zero : this.IndependentElementHideTranslation.ToVector2(); var opacityFrom = isShow ? 0 : 1; var opacityTo = isShow ? 1 : 0; - if (this._isInterruptedAnimation) - { - duration *= InterruptedAnimationReverseDurationRatio; - delay *= InterruptedAnimationReverseDurationRatio; - } foreach (var item in elements) { - if (delay < startTime) + if (startTime.HasValue && delay < startTime) { if (isShow) { @@ -353,12 +339,12 @@ private Task AnimateIndependentElements( else { var useDelay = delay - startTime; - if (Math.Abs(this.IndependentElementHideTranslation.X) > 0.01 || - Math.Abs(this.IndependentElementHideTranslation.Y) > 0.01) + if (Math.Abs(this.IndependentElementHideTranslation.X) > AlmostZero || + Math.Abs(this.IndependentElementHideTranslation.Y) > AlmostZero) { controller.AddAnimationFor(item, this.Translation( translationTo, - this._isInterruptedAnimation ? null : translationFrom, + startTime.HasValue ? null : translationFrom, useDelay, duration: duration, easingType: easingType, @@ -367,7 +353,7 @@ private Task AnimateIndependentElements( controller.AddAnimationFor(item, this.Opacity( opacityTo, - this._isInterruptedAnimation ? null : opacityFrom, + startTime.HasValue ? null : opacityFrom, delay, duration, easingType: easingType, @@ -380,7 +366,7 @@ private Task AnimateIndependentElements( } } - return controller.StartAsync(token, null, null); + return controller.StartAsync(token); } private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory) AnimateTranslation( diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 2a43583c0cb..427681d5b7f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -18,7 +18,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations [ContentProperty(Name = nameof(Configs))] public sealed partial class TransitionHelper { - private const double InterruptedAnimationReverseDurationRatio = 0.7; + private const double AlmostZero = 0.01; private readonly Dictionary sourceConnectedAnimatedElements = new(); private readonly Dictionary targetConnectedAnimatedElements = new(); private readonly List sourceIndependentAnimatedElements = new(); @@ -28,10 +28,6 @@ public sealed partial class TransitionHelper private CancellationTokenSource _reverseCancellationTokenSource; private bool _needUpdateSourceLayout; private bool _needUpdateTargetLayout; - private bool _isInterruptedAnimation; - private DateTime? _lastAnimationStartTime; - private float? _lastStartProgress; - private TimeSpan? _lastAnimationDuration; private KeyFrameAnimationGroupController _currentAnimationGroupController; @@ -73,9 +69,7 @@ public async Task StartAsync(CancellationToken token, bool forceUpdateAnimatedEl if (this._reverseCancellationTokenSource is not null) { - this._isInterruptedAnimation = !this._isInterruptedAnimation; this._reverseCancellationTokenSource.Cancel(); - this._lastStartProgress = GetStartProgress(this.ReverseDuration); this._reverseCancellationTokenSource = null; } else if (this.IsTargetState) @@ -90,7 +84,7 @@ public async Task StartAsync(CancellationToken token, bool forceUpdateAnimatedEl this._animateCancellationTokenSource = new CancellationTokenSource(); var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, this._animateCancellationTokenSource.Token); - await StartInterruptibleAnimationsAsync(false, linkedTokenSource.Token, this._lastStartProgress, this.Duration); + await StartInterruptibleAnimationsAsync(false, linkedTokenSource.Token, this.Duration); this._animateCancellationTokenSource = null; } @@ -128,9 +122,7 @@ public async Task ReverseAsync(CancellationToken token, bool forceUpdateAnimated if (this._animateCancellationTokenSource is not null) { - this._isInterruptedAnimation = !this._isInterruptedAnimation; this._animateCancellationTokenSource.Cancel(); - this._lastStartProgress = GetStartProgress(this.Duration); this._animateCancellationTokenSource = null; } else if (this.IsTargetState is false) @@ -145,7 +137,7 @@ public async Task ReverseAsync(CancellationToken token, bool forceUpdateAnimated this._reverseCancellationTokenSource = new CancellationTokenSource(); var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, this._reverseCancellationTokenSource.Token); - await StartInterruptibleAnimationsAsync(true, linkedTokenSource.Token, this._lastStartProgress, this.ReverseDuration); + await StartInterruptibleAnimationsAsync(true, linkedTokenSource.Token, this.ReverseDuration); this._reverseCancellationTokenSource = null; } @@ -167,24 +159,7 @@ public void Reset(bool toInitialState = true) this._reverseCancellationTokenSource = null; } - this._isInterruptedAnimation = false; this.RestoreState(!toInitialState); } - - private float? GetStartProgress(TimeSpan lastTotalDuration) - { - var startProgress = 1 - this._lastStartProgress ?? 1; - if (_lastAnimationStartTime.HasValue) - { - var elapsedProgress = (DateTime.Now - _lastAnimationStartTime.Value) / lastTotalDuration; - startProgress -= (float)elapsedProgress; - } - else - { - return null; - } - - return Math.Max(0, Math.Min(startProgress, 1)); - } } } From 511b7a8136a3f1149c63ac66582533281ad638eb Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 22 Jul 2022 12:51:34 +0800 Subject: [PATCH 54/94] update animations --- .../Helpers/TransitionHelper.Logic.cs | 29 +++++-------------- .../Helpers/TransitionHelper.cs | 1 - 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 2745d6f1add..9672f788b12 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -217,8 +217,6 @@ private void BuildConnectedAnimationController(KeyFrameAnimationGroupController var sourceCenterPoint = sourceActualSize * config.NormalizedCenterPoint.ToVector2(); var targetCenterPoint = targetActualSize * config.NormalizedCenterPoint.ToVector2(); - var currentSourceScale = GetXY(source.GetVisual().Scale); - source.GetVisual().CenterPoint = new Vector3(sourceCenterPoint, 0); target.GetVisual().CenterPoint = new Vector3(targetCenterPoint, 0); var (sourceTranslationAnimation, targetTranslationAnimation) = this.AnimateTranslation( @@ -226,7 +224,6 @@ private void BuildConnectedAnimationController(KeyFrameAnimationGroupController target, sourceCenterPoint, targetCenterPoint, - currentSourceScale, duration, config.EasingType, config.EasingMode); @@ -237,21 +234,18 @@ private void BuildConnectedAnimationController(KeyFrameAnimationGroupController ScaleMode.Scale => this.AnimateScale( sourceActualSize, targetActualSize, - currentSourceScale, duration, config.EasingType, config.EasingMode), ScaleMode.ScaleX => this.AnimateScaleX( sourceActualSize, targetActualSize, - currentSourceScale, duration, config.EasingType, config.EasingMode), ScaleMode.ScaleY => this.AnimateScaleY( sourceActualSize, targetActualSize, - currentSourceScale, duration, config.EasingType, config.EasingMode), @@ -374,12 +368,11 @@ private Task AnimateIndependentElements( UIElement target, Vector2 sourceCenterPoint, Vector2 targetCenterPoint, - Vector2 initialScale, TimeSpan duration, EasingType easingType, EasingMode easingMode) { - var diff = ((target.TransformToVisual(source).TransformPoint(default).ToVector2() - sourceCenterPoint) * initialScale) + targetCenterPoint; + var diff = target.TransformToVisual(source).TransformPoint(default).ToVector2() - sourceCenterPoint + targetCenterPoint; return (this.Translation(diff, Vector2.Zero, duration: duration, easingType: easingType, easingMode: easingMode), this.Translation(Vector2.Zero, -diff, duration: duration, easingType: easingType, easingMode: easingMode)); } @@ -387,7 +380,6 @@ private Task AnimateIndependentElements( private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory, Vector2) AnimateScale( Vector2 sourceActualSize, Vector2 targetActualSize, - Vector2 initialScale, TimeSpan duration, EasingType easingType, EasingMode easingMode) @@ -395,60 +387,55 @@ private Task AnimateIndependentElements( var scaleX = targetActualSize.X / sourceActualSize.X; var scaleY = targetActualSize.Y / sourceActualSize.Y; var scale = new Vector2(scaleX, scaleY); - var (sourceFactory, targetFactory) = this.AnimateScaleImp(scale, initialScale, duration, easingType, easingMode); + var (sourceFactory, targetFactory) = this.AnimateScaleImp(scale, duration, easingType, easingMode); return (sourceFactory, targetFactory, scale); } private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory, Vector2) AnimateScaleX( Vector2 sourceActualSize, Vector2 targetActualSize, - Vector2 initialScale, TimeSpan duration, EasingType easingType, EasingMode easingMode) { var scaleX = targetActualSize.X / sourceActualSize.X; var scale = new Vector2(scaleX, scaleX); - var (sourceFactory, targetFactory) = this.AnimateScaleImp(scale, initialScale, duration, easingType, easingMode); + var (sourceFactory, targetFactory) = this.AnimateScaleImp(scale, duration, easingType, easingMode); return (sourceFactory, targetFactory, scale); } private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory, Vector2) AnimateScaleY( Vector2 sourceActualSize, Vector2 targetActualSize, - Vector2 initialScale, TimeSpan duration, EasingType easingType, EasingMode easingMode) { var scaleY = targetActualSize.Y / sourceActualSize.Y; var scale = new Vector2(scaleY, scaleY); - var (sourceFactory, targetFactory) = this.AnimateScaleImp(scale, initialScale, duration, easingType, easingMode); + var (sourceFactory, targetFactory) = this.AnimateScaleImp(scale, duration, easingType, easingMode); return (sourceFactory, targetFactory, scale); } private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory) AnimateScaleImp( Vector2 targetScale, - Vector2 initialScale, TimeSpan duration, EasingType easingType, EasingMode easingMode) { - return (this.Scale(targetScale, initialScale, duration: duration, easingType: easingType, easingMode: easingMode), - this.Scale(Vector2.One, GetInverseScale(targetScale / initialScale), duration: duration, easingType: easingType, easingMode: easingMode)); + return (this.Scale(targetScale, Vector2.One, duration: duration, easingType: easingType, easingMode: easingMode), + this.Scale(Vector2.One, GetInverseScale(targetScale), duration: duration, easingType: easingType, easingMode: easingMode)); } private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory) AnimateOpacity(TimeSpan duration) { - var useDuration = TimeSpan.FromMilliseconds(Math.Min(200, duration.TotalMilliseconds / 3)); - var normalizedProgress = (float)(useDuration / duration); var sourceNormalizedKeyFrames = new Dictionary { - [normalizedProgress] = (0, EasingType.Cubic, EasingMode.EaseIn) + [0.3f] = (0, EasingType.Cubic, EasingMode.EaseIn) }; var targetNormalizedKeyFrames = new Dictionary { - [normalizedProgress] = (1, EasingType.Cubic, EasingMode.EaseOut) + [0.3f] = (1, EasingType.Cubic, EasingMode.EaseOut) }; return (this.Opacity(0, 1, duration: duration, normalizedKeyFrames: sourceNormalizedKeyFrames), this.Opacity(1, 0, duration: duration, normalizedKeyFrames: targetNormalizedKeyFrames)); diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 427681d5b7f..60ca160dfe2 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Linq; using System.Threading; From 953fb995af20d37305ef5ce20349c424f6e23aad Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 22 Jul 2022 13:17:15 +0800 Subject: [PATCH 55/94] update default EasingType --- .../Helpers/TransitionConfig.cs | 4 ++-- .../Helpers/TransitionHelper.Helpers.cs | 2 -- .../Helpers/TransitionHelper.Logic.cs | 8 +++----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs index be13569bf48..9b45ee9cc52 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs @@ -38,9 +38,9 @@ public class TransitionConfig /// /// Gets or sets the easing function type for the transition. - /// The default value is . + /// The default value is . /// - public EasingType EasingType { get; set; } = EasingType.Default; + public EasingType EasingType { get; set; } = EasingType.Cubic; /// /// Gets or sets the easing function mode for the transition. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index ca210bd80b7..2185e2e06fe 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -81,8 +81,6 @@ private static void RestoreElement(UIElement animatedElement) private static Vector2 GetInverseScale(Vector2 scale) => new(1 / scale.X, 1 / scale.Y); - private static Vector2 GetXY(Vector3 value) => new(value.X, value.Y); - private static Thickness GetFixedThickness(Thickness thickness, double defaultValue) { var left = thickness.Left < AlmostZero ? defaultValue : thickness.Left; diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 9672f788b12..648ffd49f44 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -260,7 +260,6 @@ private void BuildConnectedAnimationController(KeyFrameAnimationGroupController sourceOpacityAnimation, sourceScaleAnimation }); - controller.AddAnimationGroupFor( target, new[] @@ -269,8 +268,7 @@ private void BuildConnectedAnimationController(KeyFrameAnimationGroupController targetOpacityAnimation, targetScaleAnimation }); - - if (config is { EnableClipAnimation: true, ScaleMode: not ScaleMode.Scale }) + if (config.EnableClipAnimation) { Axis? axis = config.ScaleMode switch { @@ -431,11 +429,11 @@ private Task AnimateIndependentElements( { var sourceNormalizedKeyFrames = new Dictionary { - [0.3f] = (0, EasingType.Cubic, EasingMode.EaseIn) + [0.5f] = (0, EasingType.Cubic, EasingMode.EaseInOut) }; var targetNormalizedKeyFrames = new Dictionary { - [0.3f] = (1, EasingType.Cubic, EasingMode.EaseOut) + [0.5f] = (1, EasingType.Cubic, EasingMode.EaseInOut) }; return (this.Opacity(0, 1, duration: duration, normalizedKeyFrames: sourceNormalizedKeyFrames), this.Opacity(1, 0, duration: duration, normalizedKeyFrames: targetNormalizedKeyFrames)); From 62feb0600e5f7293e94bc4de1983eaf42e25b6b6 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 22 Jul 2022 13:53:44 +0800 Subject: [PATCH 56/94] add IndependentTranslation --- .../TransitionHelperCode.bind | 15 ++++++++---- .../TransitionHelperXaml.bind | 2 ++ .../TransitionHelper.AttachedProperty.cs | 24 +++++++++++++++++++ .../Helpers/TransitionHelper.Logic.cs | 11 ++++----- .../Helpers/TransitionHelper.Properties.cs | 4 ++-- 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind index 588cc6c1af5..58595af852b 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperCode.bind @@ -1,24 +1,29 @@ // Create a TransitionHelper. var transitionHelper = new TransitionHelper(); -// Configure TransitionHelper.Items that use the default configuration do not need to be listed here. +// Configure the elements that need to be connected by animation, if the elements use the default configuration, they do not need to be listed here. transitionHelper.Configs = new List { - new TransitionConfig + new() { Id = "background", ScaleMode = ScaleMode.Scale }, - new TransitionConfig + new() { Id = "image", ScaleMode = ScaleMode.Scale }, - new TransitionConfig + new() { Id = "name", ScaleMode = ScaleMode.ScaleY - } + }, + new() + { + Id = "desc", + EnableClipAnimation = true + }, }; transitionHelper.Source = FirstControl; transitionHelper.Target = SecondControl; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index 41b2512464b..d29d6973db5 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -15,6 +15,8 @@ ScaleMode="Scale" /> + diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs index 3074d913a7e..9dab78bdc89 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Windows.Foundation; using Windows.UI.Xaml; namespace Microsoft.Toolkit.Uwp.UI.Animations @@ -57,5 +58,28 @@ public static void SetIsIndependent(DependencyObject obj, bool value) /// public static readonly DependencyProperty IsIndependentProperty = DependencyProperty.RegisterAttached("IsIndependent", typeof(bool), typeof(TransitionHelper), new PropertyMetadata(false)); + + /// + /// Get the translation used by the show or hide animation for independent or unpaired UI elements. + /// + /// A bool value indicating whether the UI element needs to be connected by animation. + public static Point? GetIndependentTranslation(DependencyObject obj) + { + return (Point?)obj.GetValue(IndependentTranslationProperty); + } + + /// + /// Set the translation used by the show or hide animation for independent or unpaired UI elements. + /// + public static void SetIndependentTranslation(DependencyObject obj, Point? value) + { + obj.SetValue(IndependentTranslationProperty, value); + } + + /// + /// IsIndependent is used by the show or hide animation for independent or unpaired UI elements. + /// + public static readonly DependencyProperty IndependentTranslationProperty = + DependencyProperty.RegisterAttached("IndependentTranslation", typeof(Point?), typeof(TransitionHelper), null); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 648ffd49f44..273e5a2b019 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -307,14 +307,10 @@ private Task AnimateIndependentElements( } var controller = new KeyFrameAnimationGroupController(); - var duration = isShow ? this.IndependentElementShowDuration : this.IndependentElementHideDuration; var delay = isShow ? this.IndependentElementShowDelay : TimeSpan.Zero; - var translationFrom = isShow ? this.IndependentElementHideTranslation.ToVector2() : Vector2.Zero; - var translationTo = isShow ? Vector2.Zero : this.IndependentElementHideTranslation.ToVector2(); var opacityFrom = isShow ? 0 : 1; var opacityTo = isShow ? 1 : 0; - foreach (var item in elements) { if (startTime.HasValue && delay < startTime) @@ -330,9 +326,12 @@ private Task AnimateIndependentElements( } else { + var independentTranslation = GetIndependentTranslation(item) ?? this.DefaultIndependentTranslation; + var translationFrom = isShow ? independentTranslation.ToVector2() : Vector2.Zero; + var translationTo = isShow ? Vector2.Zero : independentTranslation.ToVector2(); var useDelay = delay - startTime; - if (Math.Abs(this.IndependentElementHideTranslation.X) > AlmostZero || - Math.Abs(this.IndependentElementHideTranslation.Y) > AlmostZero) + if (Math.Abs(independentTranslation.X) > AlmostZero || + Math.Abs(independentTranslation.Y) > AlmostZero) { controller.AddAnimationFor(item, this.Translation( translationTo, diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index acec36c01c3..b48d0224ecd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -151,10 +151,10 @@ public FrameworkElement Target public TimeSpan IndependentElementHideDuration { get; set; } = TimeSpan.FromMilliseconds(100); /// - /// Gets or sets the translation of the hide animation for independent or unpaired UI elements. + /// Gets or sets the default translation used by the show or hide animation for independent or unpaired UI elements. /// The default value is (0, 20). /// - public Point IndependentElementHideTranslation { get; set; } = new(0, 20); + public Point DefaultIndependentTranslation { get; set; } = new(0, 20); /// /// Gets or sets the easing function type for animation of independent or unpaired UI elements. From 8aca5514e656b6d7834a540b555001d1a3b593ae Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 22 Jul 2022 15:51:55 +0800 Subject: [PATCH 57/94] remove useless code --- .../Helpers/TransitionHelper.Logic.cs | 11 ----------- .../Helpers/TransitionHelper.cs | 18 ++++++++++++++---- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 273e5a2b019..cc3ee9c95d5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -62,17 +62,6 @@ private void UpdateAnimatedElements(DependencyObject parent, IDictionary @@ -122,7 +127,6 @@ public async Task ReverseAsync(CancellationToken token, bool forceUpdateAnimated if (this._animateCancellationTokenSource is not null) { this._animateCancellationTokenSource.Cancel(); - this._animateCancellationTokenSource = null; } else if (this.IsTargetState is false) { @@ -136,8 +140,14 @@ public async Task ReverseAsync(CancellationToken token, bool forceUpdateAnimated this._reverseCancellationTokenSource = new CancellationTokenSource(); var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, this._reverseCancellationTokenSource.Token); - await StartInterruptibleAnimationsAsync(true, linkedTokenSource.Token, this.ReverseDuration); + await this.AnimateControls(this.ReverseDuration, true, linkedTokenSource.Token); this._reverseCancellationTokenSource = null; + if (linkedTokenSource.Token.IsCancellationRequested) + { + return; + } + + this.RestoreState(false); } /// From 744f3395d7383aab2a274eb51e02a2f5f19b00f8 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 22 Jul 2022 16:05:53 +0800 Subject: [PATCH 58/94] Fix null --- .../Helpers/TransitionHelper.Animation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index ca9c3492350..c1b72f1f37b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -347,7 +347,7 @@ static void Stop(object state) token.Register(static obj => Stop(obj), (this, reversed, duration, animationStartTime, compositionAnimations)); } - return Task.WhenAll(tasks); + return tasks is null ? Task.CompletedTask : Task.WhenAll(tasks); } private Task StartForAsync(UIElement element, bool reversed, TimeSpan? duration, float? startProgress, List<(CompositionObject Target, string Path)> animations) From 2adde3bbaa434b597ea4307c10f5239230d3e8d5 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 22 Jul 2022 17:07:15 +0800 Subject: [PATCH 59/94] update Opacity animation --- .../Helpers/TransitionHelper.Animation.cs | 60 +++++++++---------- .../Helpers/TransitionHelper.Logic.cs | 18 ++++-- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index c1b72f1f37b..c44b6e28de2 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -24,7 +24,7 @@ public sealed partial class TransitionHelper { private interface IKeyFrameCompositionAnimationFactory { - KeyFrameAnimation GetAnimation(CompositionObject targetHint, out CompositionObject target); + KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, out CompositionObject target); } private interface IKeyFrameAnimationGroupController @@ -48,14 +48,18 @@ private sealed record KeyFrameAnimationFactory( TimeSpan? Duration, EasingType? EasingType, EasingMode? EasingMode, - Dictionary NormalizedKeyFrames) + Dictionary NormalizedKeyFrames, + Dictionary ReversedNormalizedKeyFrames) : IKeyFrameCompositionAnimationFactory where T : unmanaged { - public KeyFrameAnimation GetAnimation(CompositionObject targetHint, out CompositionObject target) + public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, out CompositionObject target) { target = null; + var direction = reversed ? AnimationDirection.Reverse : AnimationDirection.Normal; + var keyFrames = reversed ? ReversedNormalizedKeyFrames : NormalizedKeyFrames; + if (typeof(T) == typeof(float)) { var scalarAnimation = targetHint.Compositor.CreateScalarKeyFrameAnimation( @@ -65,11 +69,10 @@ public KeyFrameAnimation GetAnimation(CompositionObject targetHint, out Composit Delay, Duration, GetEasingFunction(targetHint.Compositor, EasingType, EasingMode), - iterationBehavior: AnimationIterationBehavior.Count, - iterationCount: 1); - if (NormalizedKeyFrames?.Count > 0) + direction: direction); + if (keyFrames?.Count > 0) { - foreach (var item in NormalizedKeyFrames) + foreach (var item in keyFrames) { var (value, easingType, easingMode) = item.Value; scalarAnimation.InsertKeyFrame(item.Key, CastTo(value), GetEasingFunction(targetHint.Compositor, easingType, easingMode)); @@ -88,11 +91,10 @@ public KeyFrameAnimation GetAnimation(CompositionObject targetHint, out Composit Delay, Duration, GetEasingFunction(targetHint.Compositor, EasingType, EasingMode), - iterationBehavior: AnimationIterationBehavior.Count, - iterationCount: 1); - if (NormalizedKeyFrames?.Count > 0) + direction: direction); + if (keyFrames?.Count > 0) { - foreach (var item in NormalizedKeyFrames) + foreach (var item in keyFrames) { var (value, easingType, easingMode) = item.Value; vector2Animation.InsertKeyFrame(item.Key, CastTo(value), GetEasingFunction(targetHint.Compositor, easingType, easingMode)); @@ -139,20 +141,20 @@ private sealed record ClipScalarAnimationFactory( EasingMode? EasingMode) : IKeyFrameCompositionAnimationFactory { - public KeyFrameAnimation GetAnimation(CompositionObject targetHint, out CompositionObject target) + public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, out CompositionObject target) { - Visual visual = (Visual)targetHint; - InsetClip clip = visual.Clip as InsetClip ?? (InsetClip)(visual.Clip = visual.Compositor.CreateInsetClip()); - CompositionEasingFunction easingFunction = GetEasingFunction(clip.Compositor, EasingType, EasingMode); - ScalarKeyFrameAnimation animation = clip.Compositor.CreateScalarKeyFrameAnimation( + var direction = reversed ? AnimationDirection.Reverse : AnimationDirection.Normal; + var visual = (Visual)targetHint; + var clip = visual.Clip as InsetClip ?? (InsetClip)(visual.Clip = visual.Compositor.CreateInsetClip()); + var easingFunction = GetEasingFunction(clip.Compositor, EasingType, EasingMode); + var animation = clip.Compositor.CreateScalarKeyFrameAnimation( Property, To, From, Delay, Duration, easingFunction, - iterationBehavior: AnimationIterationBehavior.Count, - iterationCount: 1); + direction: direction); target = clip; @@ -223,9 +225,10 @@ private IKeyFrameCompositionAnimationFactory Translation( TimeSpan? duration = null, EasingType? easingType = null, EasingMode? easingMode = null, - Dictionary normalizedKeyFrames = null) + Dictionary normalizedKeyFrames = null, + Dictionary reversedNormalizedKeyFrames = null) { - return new KeyFrameAnimationFactory("Translation.XY", to, from, delay, duration, easingType, easingMode, normalizedKeyFrames); + return new KeyFrameAnimationFactory("Translation.XY", to, from, delay, duration, easingType, easingMode, normalizedKeyFrames, reversedNormalizedKeyFrames); } private IKeyFrameCompositionAnimationFactory Opacity( @@ -235,9 +238,10 @@ private IKeyFrameCompositionAnimationFactory Opacity( TimeSpan? duration = null, EasingType? easingType = null, EasingMode? easingMode = null, - Dictionary normalizedKeyFrames = null) + Dictionary normalizedKeyFrames = null, + Dictionary reversedNormalizedKeyFrames = null) { - return new KeyFrameAnimationFactory(nameof(Visual.Opacity), (float)to, (float?)from, delay, duration, easingType, easingMode, normalizedKeyFrames); + return new KeyFrameAnimationFactory(nameof(Visual.Opacity), (float)to, (float?)from, delay, duration, easingType, easingMode, normalizedKeyFrames, reversedNormalizedKeyFrames); } private IKeyFrameCompositionAnimationFactory Scale( @@ -247,9 +251,10 @@ private IKeyFrameCompositionAnimationFactory Scale( TimeSpan? duration = null, EasingType? easingType = null, EasingMode? easingMode = null, - Dictionary normalizedKeyFrames = null) + Dictionary normalizedKeyFrames = null, + Dictionary reversedNormalizedKeyFrames = null) { - return new KeyFrameAnimationFactory("Scale.XY", to, from, delay, duration, easingType, easingMode, normalizedKeyFrames); + return new KeyFrameAnimationFactory("Scale.XY", to, from, delay, duration, easingType, easingMode, normalizedKeyFrames, reversedNormalizedKeyFrames); } private sealed class KeyFrameAnimationGroupController : IKeyFrameAnimationGroupController @@ -364,17 +369,12 @@ private Task StartForAsync(UIElement element, bool reversed, TimeSpan? duration, batch.Completed += (_, _) => taskCompletionSource.SetResult(null); foreach (var factory in factories) { - var animation = factory.GetAnimation(visual, out var target); + var animation = factory.GetAnimation(visual, reversed, out var target); if (duration.HasValue) { animation.Duration = duration.Value; } - if (reversed) - { - animation.Direction = AnimationDirection.Reverse; - } - (target ?? visual).StartAnimation(animation.Target, animation); if (startProgress.HasValue) { diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index cc3ee9c95d5..063c2709697 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -417,14 +417,24 @@ private Task AnimateIndependentElements( { var sourceNormalizedKeyFrames = new Dictionary { - [0.5f] = (0, EasingType.Cubic, EasingMode.EaseInOut) + [0.3f] = (0, EasingType.Cubic, EasingMode.EaseIn) + }; + var reversedSourceNormalizedKeyFrames = new Dictionary + { + [0.7f] = (1, null, null), + [1] = (0, EasingType.Cubic, EasingMode.EaseIn) }; var targetNormalizedKeyFrames = new Dictionary { - [0.5f] = (1, EasingType.Cubic, EasingMode.EaseInOut) + [0.3f] = (1, EasingType.Cubic, EasingMode.EaseOut) + }; + var reversedTargetNormalizedKeyFrames = new Dictionary + { + [0.7f] = (0, null, null), + [1] = (1, EasingType.Cubic, EasingMode.EaseOut) }; - return (this.Opacity(0, 1, duration: duration, normalizedKeyFrames: sourceNormalizedKeyFrames), - this.Opacity(1, 0, duration: duration, normalizedKeyFrames: targetNormalizedKeyFrames)); + return (this.Opacity(0, 1, duration: duration, normalizedKeyFrames: sourceNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedSourceNormalizedKeyFrames), + this.Opacity(1, 0, duration: duration, normalizedKeyFrames: targetNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedTargetNormalizedKeyFrames)); } private (IKeyFrameCompositionAnimationFactory[], IKeyFrameCompositionAnimationFactory[]) AnimateClip( From b734dce422d7b45b74cd321a8b73220be1c305ee Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Mon, 25 Jul 2022 12:12:16 +0800 Subject: [PATCH 60/94] Fix independent animation --- .../Helpers/TransitionHelper.Logic.cs | 20 +++++++++++-------- .../Helpers/TransitionHelper.Properties.cs | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 063c2709697..e7e3489615a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -318,7 +318,7 @@ private Task AnimateIndependentElements( var independentTranslation = GetIndependentTranslation(item) ?? this.DefaultIndependentTranslation; var translationFrom = isShow ? independentTranslation.ToVector2() : Vector2.Zero; var translationTo = isShow ? Vector2.Zero : independentTranslation.ToVector2(); - var useDelay = delay - startTime; + var useDelay = delay - (startTime ?? TimeSpan.Zero); if (Math.Abs(independentTranslation.X) > AlmostZero || Math.Abs(independentTranslation.Y) > AlmostZero) { @@ -334,7 +334,7 @@ private Task AnimateIndependentElements( controller.AddAnimationFor(item, this.Opacity( opacityTo, startTime.HasValue ? null : opacityFrom, - delay, + useDelay, duration, easingType: easingType, easingMode: easingMode)); @@ -417,21 +417,25 @@ private Task AnimateIndependentElements( { var sourceNormalizedKeyFrames = new Dictionary { - [0.3f] = (0, EasingType.Cubic, EasingMode.EaseIn) + [0.3f] = (1, null, null), + [0.6f] = (0, EasingType.Cubic, EasingMode.EaseIn) }; var reversedSourceNormalizedKeyFrames = new Dictionary { - [0.7f] = (1, null, null), - [1] = (0, EasingType.Cubic, EasingMode.EaseIn) + [0.4f] = (1, null, null), + [0.7f] = (0, EasingType.Cubic, EasingMode.EaseIn), + [1] = (0, null, null) }; var targetNormalizedKeyFrames = new Dictionary { - [0.3f] = (1, EasingType.Cubic, EasingMode.EaseOut) + [0.3f] = (0, null, null), + [0.6f] = (1, EasingType.Cubic, EasingMode.EaseOut) }; var reversedTargetNormalizedKeyFrames = new Dictionary { - [0.7f] = (0, null, null), - [1] = (1, EasingType.Cubic, EasingMode.EaseOut) + [0.4f] = (0, null, null), + [0.7f] = (1, EasingType.Cubic, EasingMode.EaseOut), + [1] = (1, null, null) }; return (this.Opacity(0, 1, duration: duration, normalizedKeyFrames: sourceNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedSourceNormalizedKeyFrames), this.Opacity(1, 0, duration: duration, normalizedKeyFrames: targetNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedTargetNormalizedKeyFrames)); diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index b48d0224ecd..38b416fcca1 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -136,7 +136,7 @@ public FrameworkElement Target /// Gets or sets the delay of the show animation for independent or unpaired UI elements. /// The default value is 300ms. /// - public TimeSpan IndependentElementShowDelay { get; set; } = TimeSpan.FromMilliseconds(300); + public TimeSpan IndependentElementShowDelay { get; set; } = TimeSpan.FromMilliseconds(400); /// /// Gets or sets the interval between the show animations for independent or unpaired UI elements. From f265ad5a44b8ef976892e7a18b48bd7d68fd0872 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Mon, 25 Jul 2022 12:17:11 +0800 Subject: [PATCH 61/94] update --- .../Helpers/TransitionHelper.Properties.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 38b416fcca1..b48d0224ecd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -136,7 +136,7 @@ public FrameworkElement Target /// Gets or sets the delay of the show animation for independent or unpaired UI elements. /// The default value is 300ms. /// - public TimeSpan IndependentElementShowDelay { get; set; } = TimeSpan.FromMilliseconds(400); + public TimeSpan IndependentElementShowDelay { get; set; } = TimeSpan.FromMilliseconds(300); /// /// Gets or sets the interval between the show animations for independent or unpaired UI elements. From ef1add7b4e6d0f462b8a53f72ee0094582364f59 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Mon, 25 Jul 2022 16:21:53 +0800 Subject: [PATCH 62/94] update --- .../Helpers/TransitionHelper.Logic.cs | 16 ++++++++++++---- .../Helpers/TransitionHelper.Properties.cs | 3 ++- .../Helpers/TransitionHelper.cs | 1 + 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index e7e3489615a..507c7f4df6e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -65,10 +65,18 @@ private void UpdateAnimatedElements(DependencyObject parent, IDictionary Date: Wed, 27 Jul 2022 18:06:29 +0800 Subject: [PATCH 63/94] Add InverseEasingFunctionWhenReversing --- .../Helpers/TransitionConfig.cs | 4 +- .../Helpers/TransitionHelper.Animation.cs | 57 ++++++++++++------- .../Helpers/TransitionHelper.Logic.cs | 18 +++--- .../Helpers/TransitionHelper.Properties.cs | 5 ++ 4 files changed, 52 insertions(+), 32 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs index 9b45ee9cc52..be13569bf48 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs @@ -38,9 +38,9 @@ public class TransitionConfig /// /// Gets or sets the easing function type for the transition. - /// The default value is . + /// The default value is . /// - public EasingType EasingType { get; set; } = EasingType.Cubic; + public EasingType EasingType { get; set; } = EasingType.Default; /// /// Gets or sets the easing function mode for the transition. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index c44b6e28de2..45a97f35c68 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -24,7 +24,7 @@ public sealed partial class TransitionHelper { private interface IKeyFrameCompositionAnimationFactory { - KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, out CompositionObject target); + KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool inverseEasingFunction, out CompositionObject target); } private interface IKeyFrameAnimationGroupController @@ -33,7 +33,7 @@ private interface IKeyFrameAnimationGroupController Task StartAsync(CancellationToken token, TimeSpan? duration, float? startProgress); - Task ReverseAsync(CancellationToken token, TimeSpan? duration, float? startProgress); + Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, TimeSpan? duration, float? startProgress); void AddAnimationFor(UIElement target, IKeyFrameCompositionAnimationFactory factory); @@ -53,12 +53,12 @@ private sealed record KeyFrameAnimationFactory( : IKeyFrameCompositionAnimationFactory where T : unmanaged { - public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, out CompositionObject target) + public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool inverseEasingFunction, out CompositionObject target) { target = null; var direction = reversed ? AnimationDirection.Reverse : AnimationDirection.Normal; - var keyFrames = reversed ? ReversedNormalizedKeyFrames : NormalizedKeyFrames; + var keyFrames = (reversed && inverseEasingFunction && ReversedNormalizedKeyFrames is not null) ? ReversedNormalizedKeyFrames : NormalizedKeyFrames; if (typeof(T) == typeof(float)) { @@ -68,14 +68,14 @@ public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reverse CastToNullable(From), Delay, Duration, - GetEasingFunction(targetHint.Compositor, EasingType, EasingMode), + GetEasingFunction(targetHint.Compositor, inverseEasingFunction, EasingType, EasingMode), direction: direction); if (keyFrames?.Count > 0) { foreach (var item in keyFrames) { var (value, easingType, easingMode) = item.Value; - scalarAnimation.InsertKeyFrame(item.Key, CastTo(value), GetEasingFunction(targetHint.Compositor, easingType, easingMode)); + scalarAnimation.InsertKeyFrame(item.Key, CastTo(value), GetEasingFunction(targetHint.Compositor, inverseEasingFunction, easingType, easingMode)); } } @@ -90,14 +90,14 @@ public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reverse CastToNullable(From), Delay, Duration, - GetEasingFunction(targetHint.Compositor, EasingType, EasingMode), + GetEasingFunction(targetHint.Compositor, inverseEasingFunction, EasingType, EasingMode), direction: direction); if (keyFrames?.Count > 0) { foreach (var item in keyFrames) { var (value, easingType, easingMode) = item.Value; - vector2Animation.InsertKeyFrame(item.Key, CastTo(value), GetEasingFunction(targetHint.Compositor, easingType, easingMode)); + vector2Animation.InsertKeyFrame(item.Key, CastTo(value), GetEasingFunction(targetHint.Compositor, inverseEasingFunction, easingType, easingMode)); } } @@ -141,12 +141,12 @@ private sealed record ClipScalarAnimationFactory( EasingMode? EasingMode) : IKeyFrameCompositionAnimationFactory { - public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, out CompositionObject target) + public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool inverseEasingFunction, out CompositionObject target) { var direction = reversed ? AnimationDirection.Reverse : AnimationDirection.Normal; var visual = (Visual)targetHint; var clip = visual.Clip as InsetClip ?? (InsetClip)(visual.Clip = visual.Compositor.CreateInsetClip()); - var easingFunction = GetEasingFunction(clip.Compositor, EasingType, EasingMode); + var easingFunction = GetEasingFunction(clip.Compositor, inverseEasingFunction, EasingType, EasingMode); var animation = clip.Compositor.CreateScalarKeyFrameAnimation( Property, To, @@ -162,12 +162,24 @@ public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reverse } } - private static CompositionEasingFunction GetEasingFunction(Compositor compositor, EasingType? easingType, EasingMode? easingMode) + private static CompositionEasingFunction GetEasingFunction(Compositor compositor, bool inverse, EasingType? easingType, EasingMode? easingMode) { CompositionEasingFunction easingFunction = null; if (easingType.HasValue && easingMode.HasValue) { - easingFunction = compositor.TryCreateEasingFunction(easingType.Value, easingMode.Value); + if (easingType == EasingType.Default && easingMode == EasingMode.EaseInOut) + { + return inverse ? compositor.CreateCubicBezierEasingFunction(new(1, 0), new(1, 1)) : null; + } + + if (easingType == EasingType.Linear) + { + return compositor.CreateLinearEasingFunction(); + } + + var (a, b) = AnimationExtensions.EasingMaps[(easingType.Value, easingMode.Value)]; + + return inverse ? compositor.CreateCubicBezierEasingFunction(b, a) : compositor.CreateCubicBezierEasingFunction(a, b); } return easingFunction; @@ -300,15 +312,22 @@ public void AddAnimationGroupFor(UIElement target, IKeyFrameCompositionAnimation public Task StartAsync(CancellationToken token, TimeSpan? duration = null, float? startProgress = null) { - return AnimateAsync(false, token, duration, startProgress ?? this.LastStopProgress); + return AnimateAsync(false, token, false, duration, startProgress ?? this.LastStopProgress); } - public Task ReverseAsync(CancellationToken token, TimeSpan? duration = null, float? startProgress = null) + public Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, TimeSpan? duration = null, float? startProgress = null) { - return AnimateAsync(true, token, duration, startProgress ?? (1 - this.LastStopProgress)); + var start = startProgress; + if (startProgress.HasValue is false && this.LastStopProgress.HasValue) + { + start = 1 - this.LastStopProgress.Value; + } + + var inverse = (start.HasValue is false) && inverseEasingFunction; + return AnimateAsync(true, token, inverse, duration, start); } - private Task AnimateAsync(bool reversed, CancellationToken token, TimeSpan? duration, float? startProgress) + private Task AnimateAsync(bool reversed, CancellationToken token, bool inverseEasingFunction, TimeSpan? duration, float? startProgress) { List tasks = null; List<(CompositionObject Target, string Path)> compositionAnimations = null; @@ -326,7 +345,7 @@ private Task AnimateAsync(bool reversed, CancellationToken token, TimeSpan? dura compositionAnimations = new List<(CompositionObject Target, string Path)>(); foreach (var item in this.animationFactories) { - tasks.Add(StartForAsync(item.Key, reversed, duration, startProgress, compositionAnimations)); + tasks.Add(StartForAsync(item.Key, reversed, inverseEasingFunction, duration, startProgress, compositionAnimations)); } } @@ -355,7 +374,7 @@ static void Stop(object state) return tasks is null ? Task.CompletedTask : Task.WhenAll(tasks); } - private Task StartForAsync(UIElement element, bool reversed, TimeSpan? duration, float? startProgress, List<(CompositionObject Target, string Path)> animations) + private Task StartForAsync(UIElement element, bool reversed, bool inverseEasingFunction, TimeSpan? duration, float? startProgress, List<(CompositionObject Target, string Path)> animations) { if (!this.animationFactories.TryGetValue(element, out var factories) || factories.Count > 0 is false) { @@ -369,7 +388,7 @@ private Task StartForAsync(UIElement element, bool reversed, TimeSpan? duration, batch.Completed += (_, _) => taskCompletionSource.SetResult(null); foreach (var factory in factories) { - var animation = factory.GetAnimation(visual, reversed, out var target); + var animation = factory.GetAnimation(visual, reversed, inverseEasingFunction, out var target); if (duration.HasValue) { animation.Duration = duration.Value; diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 507c7f4df6e..ffa07e307f0 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -184,7 +184,7 @@ private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken } animationTasks.Add(reversed - ? _currentAnimationGroupController.ReverseAsync(token, duration) + ? _currentAnimationGroupController.ReverseAsync(token, this.InverseEasingFunctionWhenReversing, duration) : _currentAnimationGroupController.StartAsync(token, duration)); animationTasks.Add( @@ -425,25 +425,21 @@ private Task AnimateIndependentElements( { var sourceNormalizedKeyFrames = new Dictionary { - [0.3f] = (1, null, null), - [0.6f] = (0, EasingType.Cubic, EasingMode.EaseIn) + [0.3f] = (0, EasingType.Cubic, EasingMode.EaseIn) }; var reversedSourceNormalizedKeyFrames = new Dictionary { - [0.4f] = (1, null, null), - [0.7f] = (0, EasingType.Cubic, EasingMode.EaseIn), - [1] = (0, null, null) + [0.7f] = (1, null, null), + [1] = (0, EasingType.Cubic, EasingMode.EaseIn) }; var targetNormalizedKeyFrames = new Dictionary { - [0.3f] = (0, null, null), - [0.6f] = (1, EasingType.Cubic, EasingMode.EaseOut) + [0.3f] = (1, EasingType.Cubic, EasingMode.EaseOut) }; var reversedTargetNormalizedKeyFrames = new Dictionary { - [0.4f] = (0, null, null), - [0.7f] = (1, EasingType.Cubic, EasingMode.EaseOut), - [1] = (1, null, null) + [0.7f] = (0, null, null), + [1] = (1, EasingType.Cubic, EasingMode.EaseOut) }; return (this.Opacity(0, 1, duration: duration, normalizedKeyFrames: sourceNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedSourceNormalizedKeyFrames), this.Opacity(1, 0, duration: duration, normalizedKeyFrames: targetNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedTargetNormalizedKeyFrames)); diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index cdf3e5c0c2a..7d229d31eb1 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -127,6 +127,11 @@ public FrameworkElement Target /// public TimeSpan ReverseDuration { get; set; } = TimeSpan.FromMilliseconds(600); + /// + /// Gets or sets a value indicating whether to use the inverse easing function when animating in reverse direction. + /// + public bool InverseEasingFunctionWhenReversing { get; set; } = true; + /// /// Gets or sets the duration of the show animation for independent or unpaired UI elements. /// The default value is 200ms. From 2d07f7506272954a89ddd1e297b704e01280ec06 Mon Sep 17 00:00:00 2001 From: hhchaos Date: Wed, 27 Jul 2022 23:34:24 +0800 Subject: [PATCH 64/94] fix inverse easing --- .../Helpers/TransitionHelper.Animation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index 45a97f35c68..041fc5917e6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -179,7 +179,7 @@ private static CompositionEasingFunction GetEasingFunction(Compositor compositor var (a, b) = AnimationExtensions.EasingMaps[(easingType.Value, easingMode.Value)]; - return inverse ? compositor.CreateCubicBezierEasingFunction(b, a) : compositor.CreateCubicBezierEasingFunction(a, b); + return inverse ? compositor.CreateCubicBezierEasingFunction(new(a.Y, a.X), new(b.Y, b.X)) : compositor.CreateCubicBezierEasingFunction(a, b); } return easingFunction; From 34895453d659c9e9ce201c835975da27c82bb36f Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Thu, 28 Jul 2022 17:39:45 +0800 Subject: [PATCH 65/94] Fix inverse easing --- .../Helpers/TransitionHelper.Animation.cs | 33 +++++++++++-------- .../Helpers/TransitionHelper.Properties.cs | 1 + 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index 45a97f35c68..61eb9cb86e3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -24,7 +24,7 @@ public sealed partial class TransitionHelper { private interface IKeyFrameCompositionAnimationFactory { - KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool inverseEasingFunction, out CompositionObject target); + KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool useReversedKeyframes, bool inverseEasingFunction, out CompositionObject target); } private interface IKeyFrameAnimationGroupController @@ -53,12 +53,12 @@ private sealed record KeyFrameAnimationFactory( : IKeyFrameCompositionAnimationFactory where T : unmanaged { - public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool inverseEasingFunction, out CompositionObject target) + public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool useReversedKeyframes, bool inverseEasingFunction, out CompositionObject target) { target = null; var direction = reversed ? AnimationDirection.Reverse : AnimationDirection.Normal; - var keyFrames = (reversed && inverseEasingFunction && ReversedNormalizedKeyFrames is not null) ? ReversedNormalizedKeyFrames : NormalizedKeyFrames; + var keyFrames = (reversed && useReversedKeyframes && ReversedNormalizedKeyFrames is not null) ? ReversedNormalizedKeyFrames : NormalizedKeyFrames; if (typeof(T) == typeof(float)) { @@ -141,7 +141,7 @@ private sealed record ClipScalarAnimationFactory( EasingMode? EasingMode) : IKeyFrameCompositionAnimationFactory { - public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool inverseEasingFunction, out CompositionObject target) + public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool useReversedKeyframes, bool inverseEasingFunction, out CompositionObject target) { var direction = reversed ? AnimationDirection.Reverse : AnimationDirection.Normal; var visual = (Visual)targetHint; @@ -169,7 +169,7 @@ private static CompositionEasingFunction GetEasingFunction(Compositor compositor { if (easingType == EasingType.Default && easingMode == EasingMode.EaseInOut) { - return inverse ? compositor.CreateCubicBezierEasingFunction(new(1, 0), new(1, 1)) : null; + return inverse ? compositor.CreateCubicBezierEasingFunction(new(0.94f, 0), new(0.52f, 0.41f)) : null; } if (easingType == EasingType.Linear) @@ -179,7 +179,7 @@ private static CompositionEasingFunction GetEasingFunction(Compositor compositor var (a, b) = AnimationExtensions.EasingMaps[(easingType.Value, easingMode.Value)]; - return inverse ? compositor.CreateCubicBezierEasingFunction(b, a) : compositor.CreateCubicBezierEasingFunction(a, b); + return inverse ? compositor.CreateCubicBezierEasingFunction(new(b.Y, b.X), new(a.Y, a.X)) : compositor.CreateCubicBezierEasingFunction(a, b); } return easingFunction; @@ -275,6 +275,8 @@ private sealed class KeyFrameAnimationGroupController : IKeyFrameAnimationGroupC public float? LastStopProgress { get; private set; } = null; + private bool _lastInverseEasingFunction = false; + public void AddAnimationFor(UIElement target, IKeyFrameCompositionAnimationFactory factory) { if (factory is null) @@ -312,7 +314,10 @@ public void AddAnimationGroupFor(UIElement target, IKeyFrameCompositionAnimation public Task StartAsync(CancellationToken token, TimeSpan? duration = null, float? startProgress = null) { - return AnimateAsync(false, token, false, duration, startProgress ?? this.LastStopProgress); + var start = startProgress ?? this.LastStopProgress; + var isInterruptedAnimation = start.HasValue; + var inverse = isInterruptedAnimation && this._lastInverseEasingFunction; + return AnimateAsync(false, false, token, inverse, duration, start); } public Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, TimeSpan? duration = null, float? startProgress = null) @@ -323,16 +328,18 @@ public Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, Ti start = 1 - this.LastStopProgress.Value; } - var inverse = (start.HasValue is false) && inverseEasingFunction; - return AnimateAsync(true, token, inverse, duration, start); + var isInterruptedAnimation = start.HasValue; + var inverse = (isInterruptedAnimation && this._lastInverseEasingFunction) || (!isInterruptedAnimation && inverseEasingFunction); + return AnimateAsync(true, !isInterruptedAnimation, token, inverse, duration, start); } - private Task AnimateAsync(bool reversed, CancellationToken token, bool inverseEasingFunction, TimeSpan? duration, float? startProgress) + private Task AnimateAsync(bool reversed, bool useReversedKeyframes, CancellationToken token, bool inverseEasingFunction, TimeSpan? duration, float? startProgress) { List tasks = null; List<(CompositionObject Target, string Path)> compositionAnimations = null; DateTime? animationStartTime = null; this.LastStopProgress = null; + this._lastInverseEasingFunction = inverseEasingFunction; if (this.animationFactories.Count > 0) { if (duration.HasValue) @@ -345,7 +352,7 @@ private Task AnimateAsync(bool reversed, CancellationToken token, bool inverseEa compositionAnimations = new List<(CompositionObject Target, string Path)>(); foreach (var item in this.animationFactories) { - tasks.Add(StartForAsync(item.Key, reversed, inverseEasingFunction, duration, startProgress, compositionAnimations)); + tasks.Add(StartForAsync(item.Key, reversed, useReversedKeyframes, inverseEasingFunction, duration, startProgress, compositionAnimations)); } } @@ -374,7 +381,7 @@ static void Stop(object state) return tasks is null ? Task.CompletedTask : Task.WhenAll(tasks); } - private Task StartForAsync(UIElement element, bool reversed, bool inverseEasingFunction, TimeSpan? duration, float? startProgress, List<(CompositionObject Target, string Path)> animations) + private Task StartForAsync(UIElement element, bool reversed, bool useReversedKeyframes, bool inverseEasingFunction, TimeSpan? duration, float? startProgress, List<(CompositionObject Target, string Path)> animations) { if (!this.animationFactories.TryGetValue(element, out var factories) || factories.Count > 0 is false) { @@ -388,7 +395,7 @@ private Task StartForAsync(UIElement element, bool reversed, bool inverseEasingF batch.Completed += (_, _) => taskCompletionSource.SetResult(null); foreach (var factory in factories) { - var animation = factory.GetAnimation(visual, reversed, inverseEasingFunction, out var target); + var animation = factory.GetAnimation(visual, reversed, useReversedKeyframes, inverseEasingFunction, out var target); if (duration.HasValue) { animation.Duration = duration.Value; diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 7d229d31eb1..ba259296407 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -129,6 +129,7 @@ public FrameworkElement Target /// /// Gets or sets a value indicating whether to use the inverse easing function when animating in reverse direction. + /// The default value is true. /// public bool InverseEasingFunctionWhenReversing { get; set; } = true; From 932145da654008843d22037bf18b5f9753573bde Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Thu, 28 Jul 2022 19:09:07 +0800 Subject: [PATCH 66/94] update --- .../Helpers/TransitionHelper.Logic.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index ffa07e307f0..7a04d748b65 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -429,8 +429,8 @@ private Task AnimateIndependentElements( }; var reversedSourceNormalizedKeyFrames = new Dictionary { - [0.7f] = (1, null, null), - [1] = (0, EasingType.Cubic, EasingMode.EaseIn) + [0.5f] = (1, null, null), + [1] = (0, EasingType.Cubic, EasingMode.EaseOut) }; var targetNormalizedKeyFrames = new Dictionary { @@ -438,8 +438,8 @@ private Task AnimateIndependentElements( }; var reversedTargetNormalizedKeyFrames = new Dictionary { - [0.7f] = (0, null, null), - [1] = (1, EasingType.Cubic, EasingMode.EaseOut) + [0.5f] = (0, null, null), + [1] = (1, EasingType.Cubic, EasingMode.EaseIn) }; return (this.Opacity(0, 1, duration: duration, normalizedKeyFrames: sourceNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedSourceNormalizedKeyFrames), this.Opacity(1, 0, duration: duration, normalizedKeyFrames: targetNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedTargetNormalizedKeyFrames)); From 530212d4b83eeb1fad7fb13ac9e6bcad3d732cc9 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 29 Jul 2022 14:02:57 +0800 Subject: [PATCH 67/94] update TransitionHelper --- .../Helpers/TransitionConfig.cs | 8 +++---- .../Helpers/TransitionHelper.Animation.cs | 4 ++-- .../Helpers/TransitionHelper.Logic.cs | 22 ++++++++++--------- .../Helpers/TransitionHelper.Properties.cs | 12 ++++++++++ 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs index be13569bf48..cdd469f7be3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs @@ -38,14 +38,14 @@ public class TransitionConfig /// /// Gets or sets the easing function type for the transition. - /// The default value is . + /// If this value is not set, it will fall back to the value in . /// - public EasingType EasingType { get; set; } = EasingType.Default; + public EasingType? EasingType { get; set; } = null; /// /// Gets or sets the easing function mode for the transition. - /// The default value is . + /// If this value is not set, it will fall back to the value in . /// - public EasingMode EasingMode { get; set; } = EasingMode.EaseInOut; + public EasingMode? EasingMode { get; set; } = null; } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index 61eb9cb86e3..23f679e874a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -169,7 +169,7 @@ private static CompositionEasingFunction GetEasingFunction(Compositor compositor { if (easingType == EasingType.Default && easingMode == EasingMode.EaseInOut) { - return inverse ? compositor.CreateCubicBezierEasingFunction(new(0.94f, 0), new(0.52f, 0.41f)) : null; + return inverse ? compositor.CreateCubicBezierEasingFunction(new(1f, 0.06f), new(0.59f, 0.48f)) : null; } if (easingType == EasingType.Linear) @@ -179,7 +179,7 @@ private static CompositionEasingFunction GetEasingFunction(Compositor compositor var (a, b) = AnimationExtensions.EasingMaps[(easingType.Value, easingMode.Value)]; - return inverse ? compositor.CreateCubicBezierEasingFunction(new(b.Y, b.X), new(a.Y, a.X)) : compositor.CreateCubicBezierEasingFunction(a, b); + return inverse ? compositor.CreateCubicBezierEasingFunction(new(1 - b.X, 1 - b.Y), new(1 - a.X, 1 - a.Y)) : compositor.CreateCubicBezierEasingFunction(a, b); } return easingFunction; diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 7a04d748b65..10c4b032017 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -216,14 +216,16 @@ private void BuildConnectedAnimationController(KeyFrameAnimationGroupController source.GetVisual().CenterPoint = new Vector3(sourceCenterPoint, 0); target.GetVisual().CenterPoint = new Vector3(targetCenterPoint, 0); + var easingType = config.EasingType ?? this.DefaultEasingType; + var easingMode = config.EasingMode ?? this.DefaultEasingMode; var (sourceTranslationAnimation, targetTranslationAnimation) = this.AnimateTranslation( source, target, sourceCenterPoint, targetCenterPoint, duration, - config.EasingType, - config.EasingMode); + easingType, + easingMode); var (sourceOpacityAnimation, targetOpacityAnimation) = this.AnimateOpacity(duration); var (sourceScaleAnimation, targetScaleAnimation, targetScale) = config.ScaleMode switch { @@ -232,20 +234,20 @@ private void BuildConnectedAnimationController(KeyFrameAnimationGroupController sourceActualSize, targetActualSize, duration, - config.EasingType, - config.EasingMode), + easingType, + easingMode), ScaleMode.ScaleX => this.AnimateScaleX( sourceActualSize, targetActualSize, duration, - config.EasingType, - config.EasingMode), + easingType, + easingMode), ScaleMode.ScaleY => this.AnimateScaleY( sourceActualSize, targetActualSize, duration, - config.EasingType, - config.EasingMode), + easingType, + easingMode), _ => (null, null, Vector2.One), }; @@ -282,8 +284,8 @@ private void BuildConnectedAnimationController(KeyFrameAnimationGroupController targetCenterPoint, targetScale, duration, - config.EasingType, - config.EasingMode, + easingType, + easingMode, axis); controller.AddAnimationGroupFor(source, sourceClipAnimationGroup); controller.AddAnimationGroupFor(target, targetClipAnimationGroup); diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index ba259296407..0f8dd74c195 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -157,6 +157,18 @@ public FrameworkElement Target /// public TimeSpan IndependentElementHideDuration { get; set; } = TimeSpan.FromMilliseconds(100); + /// + /// Gets or sets the default easing function type for the transition. + /// The default value is . + /// + public EasingType DefaultEasingType { get; set; } = EasingType.Default; + + /// + /// Gets or sets the default easing function mode for the transition. + /// The default value is . + /// + public EasingMode DefaultEasingMode { get; set; } = EasingMode.EaseInOut; + /// /// Gets or sets the default translation used by the show or hide animation for independent or unpaired UI elements. /// The default value is (0, 20). From 62cee85c898b3bec538484b0667d4311154cbc53 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Mon, 1 Aug 2022 12:36:34 +0800 Subject: [PATCH 68/94] Add OpacityTransitionProgressKey --- .../Helpers/TransitionHelper.Logic.cs | 15 +++++++++------ .../Helpers/TransitionHelper.Properties.cs | 9 +++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 10c4b032017..3cb10f0824d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -8,6 +8,7 @@ using System.Numerics; using System.Threading; using System.Threading.Tasks; +using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Animation; @@ -226,7 +227,7 @@ private void BuildConnectedAnimationController(KeyFrameAnimationGroupController duration, easingType, easingMode); - var (sourceOpacityAnimation, targetOpacityAnimation) = this.AnimateOpacity(duration); + var (sourceOpacityAnimation, targetOpacityAnimation) = this.AnimateOpacity(duration, this.OpacityTransitionProgressKey); var (sourceScaleAnimation, targetScaleAnimation, targetScale) = config.ScaleMode switch { ScaleMode.None => (null, null, Vector2.One), @@ -423,24 +424,26 @@ private Task AnimateIndependentElements( this.Scale(Vector2.One, GetInverseScale(targetScale), duration: duration, easingType: easingType, easingMode: easingMode)); } - private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory) AnimateOpacity(TimeSpan duration) + private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory) AnimateOpacity(TimeSpan duration, Point opacityTransitionProgressKey) { + var normalKey = (float)Math.Max(0, Math.Min(opacityTransitionProgressKey.X, 1)); + var reversedKey = (float)Math.Max(0, Math.Min(1 - opacityTransitionProgressKey.Y, 1)); var sourceNormalizedKeyFrames = new Dictionary { - [0.3f] = (0, EasingType.Cubic, EasingMode.EaseIn) + [normalKey] = (0, EasingType.Cubic, EasingMode.EaseIn) }; var reversedSourceNormalizedKeyFrames = new Dictionary { - [0.5f] = (1, null, null), + [reversedKey] = (1, null, null), [1] = (0, EasingType.Cubic, EasingMode.EaseOut) }; var targetNormalizedKeyFrames = new Dictionary { - [0.3f] = (1, EasingType.Cubic, EasingMode.EaseOut) + [normalKey] = (1, EasingType.Cubic, EasingMode.EaseOut) }; var reversedTargetNormalizedKeyFrames = new Dictionary { - [0.5f] = (0, null, null), + [reversedKey] = (0, null, null), [1] = (1, EasingType.Cubic, EasingMode.EaseIn) }; return (this.Opacity(0, 1, duration: duration, normalizedKeyFrames: sourceNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedSourceNormalizedKeyFrames), diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 0f8dd74c195..3fc8be7e0ef 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -175,6 +175,15 @@ public FrameworkElement Target /// public Point DefaultIndependentTranslation { get; set; } = new(0, 20); + /// + /// Gets or sets the key point of opacity transition. + /// The time the keyframe of opacity from 0 to 1 or from 1 to 0 should occur at, expressed as a percentage of the animation duration. The allowed values are from (0, 0) to (1, 1). + /// .X will be used in the animation of the normal direction. + /// .Y will be used in the animation of the reverse direction. + /// The default value is (0.3, 0.3). + /// + public Point OpacityTransitionProgressKey { get; set; } = new(0.3, 0.3); + /// /// Gets or sets the easing function type for animation of independent or unpaired UI elements. /// The default value is . From 756478440b9805d81f88a26abc94f6b26c8f14b9 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Tue, 23 Aug 2022 16:09:08 +0800 Subject: [PATCH 69/94] Added coordinated animation --- .../Actions/StartTransitionActionXaml.bind | 2 +- .../Helpers/TransitionHelper.Animation.cs | 12 +- .../TransitionHelper.AttachedProperty.cs | 24 ++ .../Helpers/TransitionHelper.Helpers.cs | 85 +++++- .../Helpers/TransitionHelper.Logic.cs | 262 +++++++++++------- .../Helpers/TransitionHelper.Properties.cs | 2 + .../Helpers/TransitionHelper.cs | 20 +- 7 files changed, 296 insertions(+), 111 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind index d5e09514cb5..a555f9bd880 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Actions/StartTransitionActionXaml.bind @@ -73,7 +73,7 @@ FontWeight="SemiBold"> Windows Community Toolkit - The Windows Community Toolkit is a collection of helper functions, custom controls, and app services. It simplifies and demonstrates common developer patterns when building experiences for Windows. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index 23f679e874a..5cf48ba02db 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -22,6 +22,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// public sealed partial class TransitionHelper { + private const string TranslationPropertyName = "Translation"; + private const string TranslationXYPropertyName = "Translation.XY"; + private const string ScaleXYPropertyName = "Scale.XY"; + private interface IKeyFrameCompositionAnimationFactory { KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool useReversedKeyframes, bool inverseEasingFunction, out CompositionObject target); @@ -240,7 +244,7 @@ private IKeyFrameCompositionAnimationFactory Translation( Dictionary normalizedKeyFrames = null, Dictionary reversedNormalizedKeyFrames = null) { - return new KeyFrameAnimationFactory("Translation.XY", to, from, delay, duration, easingType, easingMode, normalizedKeyFrames, reversedNormalizedKeyFrames); + return new KeyFrameAnimationFactory(TranslationXYPropertyName, to, from, delay, duration, easingType, easingMode, normalizedKeyFrames, reversedNormalizedKeyFrames); } private IKeyFrameCompositionAnimationFactory Opacity( @@ -266,7 +270,7 @@ private IKeyFrameCompositionAnimationFactory Scale( Dictionary normalizedKeyFrames = null, Dictionary reversedNormalizedKeyFrames = null) { - return new KeyFrameAnimationFactory("Scale.XY", to, from, delay, duration, easingType, easingMode, normalizedKeyFrames, reversedNormalizedKeyFrames); + return new KeyFrameAnimationFactory(ScaleXYPropertyName, to, from, delay, duration, easingType, easingMode, normalizedKeyFrames, reversedNormalizedKeyFrames); } private sealed class KeyFrameAnimationGroupController : IKeyFrameAnimationGroupController @@ -312,7 +316,7 @@ public void AddAnimationGroupFor(UIElement target, IKeyFrameCompositionAnimation } } - public Task StartAsync(CancellationToken token, TimeSpan? duration = null, float? startProgress = null) + public Task StartAsync(CancellationToken token, TimeSpan? duration, float? startProgress) { var start = startProgress ?? this.LastStopProgress; var isInterruptedAnimation = start.HasValue; @@ -320,7 +324,7 @@ public Task StartAsync(CancellationToken token, TimeSpan? duration = null, float return AnimateAsync(false, false, token, inverse, duration, start); } - public Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, TimeSpan? duration = null, float? startProgress = null) + public Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, TimeSpan? duration, float? startProgress) { var start = startProgress; if (startProgress.HasValue is false && this.LastStopProgress.HasValue) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs index 9dab78bdc89..0f9a115cc94 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs @@ -81,5 +81,29 @@ public static void SetIndependentTranslation(DependencyObject obj, Point? value) /// public static readonly DependencyProperty IndependentTranslationProperty = DependencyProperty.RegisterAttached("IndependentTranslation", typeof(Point?), typeof(TransitionHelper), null); + + /// + /// Get the target animation id for coordinated animation of the UI element. + /// + /// The target animation id for coordinated animation of the UI element. + public static string GetCoordinatedTarget(DependencyObject obj) + { + return (string)obj.GetValue(CoordinatedTargetProperty); + } + + /// + /// Set the target animation id for coordinated animation of the UI element. + /// + public static void SetCoordinatedTarget(DependencyObject obj, string value) + { + obj.SetValue(CoordinatedTargetProperty, value); + } + + /// + /// CoordinatedTarget is used to mark the target animation id of coordinated animation. + /// These elements that use coordinated animation will travel alongside the target UI element. + /// + public static readonly DependencyProperty CoordinatedTargetProperty = + DependencyProperty.RegisterAttached("CoordinatedTarget", typeof(string), typeof(TransitionHelper), null); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index 2185e2e06fe..b99c3bd31cc 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -2,9 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Threading.Tasks; +using Windows.Foundation; +using Windows.UI.Composition; using Windows.UI.Xaml; using Windows.UI.Xaml.Hosting; @@ -36,7 +40,7 @@ public int GetHashCode(DependencyObject obj) private static IEnumerable GetAnimatedElements(DependencyObject targetElement) { return targetElement?.FindDescendantsOrSelf() - .Where(element => GetId(element) is not null || GetIsIndependent(element)) + .Where(element => GetId(element) is not null || GetCoordinatedTarget(element) is not null || GetIsIndependent(element)) .Distinct(new AnimatedElementComparer()) .OfType(); } @@ -73,15 +77,53 @@ private static void RestoreElement(UIElement animatedElement) { ElementCompositionPreview.SetIsTranslationEnabled(animatedElement, true); var visual = animatedElement.GetVisual(); + visual.StopAnimation(nameof(Visual.Opacity)); + visual.StopAnimation(TranslationXYPropertyName); + visual.StopAnimation(ScaleXYPropertyName); + if (visual.Clip is InsetClip clip) + { + clip.StopAnimation(nameof(InsetClip.LeftInset)); + clip.StopAnimation(nameof(InsetClip.TopInset)); + clip.StopAnimation(nameof(InsetClip.RightInset)); + clip.StopAnimation(nameof(InsetClip.BottomInset)); + } + visual.Opacity = 1; visual.Scale = Vector3.One; visual.Clip = null; - visual.Properties.InsertVector3("Translation", Vector3.Zero); + visual.Properties.InsertVector3(TranslationPropertyName, Vector3.Zero); + } + + private static void IsNotNullAndIsLoaded(FrameworkElement target, string name) + { + if (target is null) + { + throw new ArgumentNullException(name); + } + + if (target.IsLoaded is false) + { + throw new ArgumentException($"The {name} element has not been loaded yet.", name); + } + } + + private static Task UpdateControlLayout(FrameworkElement target) + { + var updateTargetLayoutTaskSource = new TaskCompletionSource(); + void OnTargetLayoutUpdated(object sender, object e) + { + target.LayoutUpdated -= OnTargetLayoutUpdated; + _ = updateTargetLayoutTaskSource.TrySetResult(null); + } + + target.LayoutUpdated += OnTargetLayoutUpdated; + target.UpdateLayout(); + return updateTargetLayoutTaskSource.Task; } private static Vector2 GetInverseScale(Vector2 scale) => new(1 / scale.X, 1 / scale.Y); - private static Thickness GetFixedThickness(Thickness thickness, double defaultValue) + private static Thickness GetFixedThickness(Thickness thickness, double defaultValue = -4 /* -4 is used to prevent shadows from being cropped.*/) { var left = thickness.Left < AlmostZero ? defaultValue : thickness.Left; var top = thickness.Top < AlmostZero ? defaultValue : thickness.Top; @@ -89,5 +131,42 @@ private static Thickness GetFixedThickness(Thickness thickness, double defaultVa var bottom = thickness.Bottom < AlmostZero ? defaultValue : thickness.Bottom; return new Thickness(left, top, right, bottom); } + + private static Thickness? GetCoordinatedElementClip(Vector2 scale, Point targetLocation, Size targetSize, Rect targetParentBounds) + { + var inverseScale = GetInverseScale(scale); + var targetBounds = new Rect(targetLocation, targetSize); + if (targetParentBounds.Contains(targetLocation) && targetParentBounds.Contains(new Point(targetBounds.Right, targetBounds.Bottom))) + { + return null; + } + + return GetFixedThickness( + new Thickness( + (targetParentBounds.X - targetBounds.X) * inverseScale.X, + (targetParentBounds.Y - targetBounds.Y) * inverseScale.Y, + (targetBounds.Right - targetParentBounds.Right) * inverseScale.X, + (targetBounds.Bottom - targetParentBounds.Bottom) * inverseScale.X)); + } + + private static Thickness? GetConnectedElementClip(Axis? axis, Vector2 scale, Vector2 actualSize, Vector2 centerPoint, Rect targetParentBounds) + { + var targetLocation = -centerPoint * scale; + var targetSize = (actualSize * scale).ToSize(); + if (axis is Axis.X) + { + var minY = Math.Min(targetParentBounds.Y, targetLocation.Y); + targetParentBounds.Height = Math.Max(targetParentBounds.Bottom, targetLocation.Y + targetSize.Height) - minY; + targetParentBounds.Y = minY; + } + else if (axis is Axis.Y) + { + var minX = Math.Min(targetParentBounds.X, targetLocation.X); + targetParentBounds.Width = Math.Max(targetParentBounds.Right, targetLocation.X + targetSize.Width) - minX; + targetParentBounds.X = minX; + } + + return GetCoordinatedElementClip(scale, targetLocation.ToPoint(), targetSize, targetParentBounds); + } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 3cb10f0824d..87f780f296a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -22,43 +22,45 @@ public sealed partial class TransitionHelper { private void UpdateSourceAnimatedElements() { - if (this.Source is null) - { - return; - } - - UpdateAnimatedElements(this.Source, this.sourceConnectedAnimatedElements, this.sourceIndependentAnimatedElements); + UpdateAnimatedElements(this.Source, this.sourceConnectedElements, this.sourceCoordinatedElements, this.sourceIndependentElements); } private void UpdateTargetAnimatedElements() { - if (this.Target is null) - { - return; - } - - UpdateAnimatedElements(this.Target, this.targetConnectedAnimatedElements, this.targetIndependentAnimatedElements); + UpdateAnimatedElements(this.Target, this.targetConnectedElements, this.targetCoordinatedElements, this.targetIndependentElements); } - private void UpdateAnimatedElements(DependencyObject parent, IDictionary connectedAnimatedElements, ICollection independentAnimatedElements) + private void UpdateAnimatedElements(DependencyObject parent, IDictionary connectedElements, IDictionary> coordinatedElements, ICollection independentElements) { - if (this.Source is null) + connectedElements.Clear(); + coordinatedElements.Clear(); + independentElements.Clear(); + + if (parent is null) { return; } - connectedAnimatedElements.Clear(); - independentAnimatedElements.Clear(); - foreach (var item in GetAnimatedElements(parent)) { if (GetId(item) is { } id) { - connectedAnimatedElements[id] = item; + connectedElements[id] = item; + } + else if (GetCoordinatedTarget(item) is { } targetId) + { + if (coordinatedElements.ContainsKey(targetId) is false) + { + coordinatedElements[targetId] = new List { item }; + } + else + { + coordinatedElements[targetId].Add(item); + } } else { - independentAnimatedElements.Add(item); + independentElements.Add(item); } } } @@ -66,13 +68,13 @@ private void UpdateAnimatedElements(DependencyObject parent, IDictionary(); - void OnTargetLayoutUpdated(object sender, object e) - { - target.LayoutUpdated -= OnTargetLayoutUpdated; - _ = updateTargetLayoutTaskSource.TrySetResult(null); - } - - target.LayoutUpdated += OnTargetLayoutUpdated; - target.UpdateLayout(); - return updateTargetLayoutTaskSource.Task; - } - private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken token) { var animationTasks = new List(3); - var sourceUnpairedElements = this.sourceConnectedAnimatedElements - .Where(item => !this.targetConnectedAnimatedElements.ContainsKey(item.Key)) - .Select(item => item.Value); - var targetUnpairedElements = this.targetConnectedAnimatedElements - .Where(item => !this.sourceConnectedAnimatedElements.ContainsKey(item.Key)) - .Select(item => item.Value); - var pairedElementKeys = this.sourceConnectedAnimatedElements - .Where(item => this.targetConnectedAnimatedElements.ContainsKey(item.Key)) + var sourceUnpairedElements = this.sourceConnectedElements + .Where(item => !this.targetConnectedElements.ContainsKey(item.Key)) + .SelectMany(item => + { + var result = new List(1) { item.Value }; + if (this.sourceCoordinatedElements.TryGetValue(item.Key, out var coordinatedElements)) + { + return result.Concat(coordinatedElements); + }; + + return result; + }); + var targetUnpairedElements = this.targetConnectedElements + .Where(item => !this.sourceConnectedElements.ContainsKey(item.Key)) + .SelectMany(item => + { + var result = new List(1) { item.Value }; + if (this.targetCoordinatedElements.TryGetValue(item.Key, out var coordinatedElements)) + { + return result.Concat(coordinatedElements); + }; + + return result; + }); + var pairedElementKeys = this.sourceConnectedElements + .Where(item => this.targetConnectedElements.ContainsKey(item.Key)) .Select(item => item.Key); if (_currentAnimationGroupController is null) { _currentAnimationGroupController = new KeyFrameAnimationGroupController(); foreach (var key in pairedElementKeys) { - var source = this.sourceConnectedAnimatedElements[key]; - var target = this.targetConnectedAnimatedElements[key]; + var source = this.sourceConnectedElements[key]; + var target = this.targetConnectedElements[key]; var animationConfig = this.Configs.FirstOrDefault(config => config.Id == key) ?? this.DefaultConfig; + this.sourceCoordinatedElements.TryGetValue(key, out var sourceAttachedElements); + this.targetCoordinatedElements.TryGetValue(key, out var targetAttachedElements); this.BuildConnectedAnimationController( _currentAnimationGroupController, source, target, + sourceAttachedElements, + targetAttachedElements, duration, animationConfig); } @@ -185,12 +195,12 @@ private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken } animationTasks.Add(reversed - ? _currentAnimationGroupController.ReverseAsync(token, this.InverseEasingFunctionWhenReversing, duration) - : _currentAnimationGroupController.StartAsync(token, duration)); + ? _currentAnimationGroupController.ReverseAsync(token, this.InverseEasingFunctionWhenReversing, duration, null) + : _currentAnimationGroupController.StartAsync(token, duration, null)); animationTasks.Add( this.AnimateIndependentElements( - this.sourceIndependentAnimatedElements.Concat(sourceUnpairedElements), + this.sourceIndependentElements.Concat(sourceUnpairedElements), reversed, token, startTime, @@ -198,7 +208,7 @@ private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken IndependentElementEasingMode)); animationTasks.Add( this.AnimateIndependentElements( - this.targetIndependentAnimatedElements.Concat(targetUnpairedElements), + this.targetIndependentElements.Concat(targetUnpairedElements), !reversed, token, startTime, @@ -208,7 +218,13 @@ private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken return Task.WhenAll(animationTasks); } - private void BuildConnectedAnimationController(KeyFrameAnimationGroupController controller, UIElement source, UIElement target, TimeSpan duration, TransitionConfig config) + private void BuildConnectedAnimationController( + IKeyFrameAnimationGroupController controller, + UIElement source, + UIElement target, + List sourceAttachedElements, + List targetAttachedElements, + TimeSpan duration, TransitionConfig config) { var sourceActualSize = source is FrameworkElement sourceElement ? new Vector2((float)sourceElement.ActualWidth, (float)sourceElement.ActualHeight) : source.ActualSize; var targetActualSize = target is FrameworkElement targetElement ? new Vector2((float)targetElement.ActualWidth, (float)targetElement.ActualHeight) : target.ActualSize; @@ -219,7 +235,7 @@ private void BuildConnectedAnimationController(KeyFrameAnimationGroupController target.GetVisual().CenterPoint = new Vector3(targetCenterPoint, 0); var easingType = config.EasingType ?? this.DefaultEasingType; var easingMode = config.EasingMode ?? this.DefaultEasingMode; - var (sourceTranslationAnimation, targetTranslationAnimation) = this.AnimateTranslation( + var (sourceTranslationAnimation, targetTranslationAnimation, translation) = this.AnimateTranslation( source, target, sourceCenterPoint, @@ -268,6 +284,71 @@ private void BuildConnectedAnimationController(KeyFrameAnimationGroupController targetOpacityAnimation, targetScaleAnimation }); + if (sourceAttachedElements?.Count > 0) + { + var targetControlBounds = new Rect(0, 0, this.Target.ActualWidth, this.Target.ActualHeight); + var targetTransformedBounds = this.Target.TransformToVisual(this.Source).TransformBounds(targetControlBounds); + var scale = targetScale; + foreach (var coordinatedElement in sourceAttachedElements) + { + var coordinatedElementActualSize = coordinatedElement is FrameworkElement coordinatedFrameworkElement ? new Vector2((float)coordinatedFrameworkElement.ActualWidth, (float)coordinatedFrameworkElement.ActualHeight) : coordinatedElement.ActualSize; + coordinatedElement.GetVisual().CenterPoint = new Vector3(coordinatedElementActualSize * config.NormalizedCenterPoint.ToVector2(), 0); + controller.AddAnimationGroupFor( + coordinatedElement, + new[] + { + sourceTranslationAnimation, + sourceOpacityAnimation, + sourceScaleAnimation + }); + var location = coordinatedElement.TransformToVisual(this.Source).TransformPoint(translation.ToPoint()); + var targetClip = GetCoordinatedElementClip(scale, location, (coordinatedElementActualSize * scale).ToSize(), targetTransformedBounds); + if (targetClip.HasValue) + { + controller.AddAnimationGroupFor( + coordinatedElement, + this.Clip( + targetClip.Value, + duration: duration, + easingType: easingType, + easingMode: easingMode)); + } + } + } + + if (targetAttachedElements?.Count > 0) + { + var sourceControlBounds = new Rect(0, 0, this.Source.ActualWidth, this.Source.ActualHeight); + var sourceTransformedBounds = this.Source.TransformToVisual(this.Target).TransformBounds(sourceControlBounds); + var scale = GetInverseScale(targetScale); + foreach (var coordinatedElement in targetAttachedElements) + { + var coordinatedElementActualSize = coordinatedElement is FrameworkElement coordinatedFrameworkElement ? new Vector2((float)coordinatedFrameworkElement.ActualWidth, (float)coordinatedFrameworkElement.ActualHeight) : coordinatedElement.ActualSize; + coordinatedElement.GetVisual().CenterPoint = new Vector3(coordinatedElementActualSize * config.NormalizedCenterPoint.ToVector2(), 0); + controller.AddAnimationGroupFor( + coordinatedElement, + new[] + { + targetTranslationAnimation, + targetOpacityAnimation, + targetScaleAnimation + }); + var location = coordinatedElement.TransformToVisual(this.Target).TransformPoint((-translation).ToPoint()); + var targetClip = GetCoordinatedElementClip(scale, location, (coordinatedElementActualSize * scale).ToSize(), sourceTransformedBounds); + if (targetClip.HasValue) + { + controller.AddAnimationGroupFor( + coordinatedElement, + this.Clip( + default, + from: targetClip.Value, + duration: duration, + easingType: easingType, + easingMode: easingMode)); + } + } + } + if (config.EnableClipAnimation) { Axis? axis = config.ScaleMode switch @@ -288,8 +369,15 @@ private void BuildConnectedAnimationController(KeyFrameAnimationGroupController easingType, easingMode, axis); - controller.AddAnimationGroupFor(source, sourceClipAnimationGroup); - controller.AddAnimationGroupFor(target, targetClipAnimationGroup); + if (sourceClipAnimationGroup is not null) + { + controller.AddAnimationGroupFor(source, sourceClipAnimationGroup); + } + + if (targetClipAnimationGroup is not null) + { + controller.AddAnimationGroupFor(target, targetClipAnimationGroup); + } } } @@ -357,10 +445,10 @@ private Task AnimateIndependentElements( } } - return controller.StartAsync(token); + return controller.StartAsync(token, null, null); } - private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory) AnimateTranslation( + private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory, Vector2) AnimateTranslation( UIElement source, UIElement target, Vector2 sourceCenterPoint, @@ -369,9 +457,10 @@ private Task AnimateIndependentElements( EasingType easingType, EasingMode easingMode) { - var diff = target.TransformToVisual(source).TransformPoint(default).ToVector2() - sourceCenterPoint + targetCenterPoint; - return (this.Translation(diff, Vector2.Zero, duration: duration, easingType: easingType, easingMode: easingMode), - this.Translation(Vector2.Zero, -diff, duration: duration, easingType: easingType, easingMode: easingMode)); + var translation = target.TransformToVisual(source).TransformPoint(default).ToVector2() - sourceCenterPoint + targetCenterPoint; + return (this.Translation(translation, Vector2.Zero, duration: duration, easingType: easingType, easingMode: easingMode), + this.Translation(Vector2.Zero, -translation, duration: duration, easingType: easingType, easingMode: easingMode), + translation); } private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory, Vector2) AnimateScale( @@ -461,44 +550,25 @@ private Task AnimateIndependentElements( EasingMode easingMode, Axis? axis) { - // -4 is used to prevent shadows from being cropped. - var defaultValue = -4; - var defaultThickness = new Thickness(defaultValue); - var inverseScale = GetInverseScale(targetScale); - - var sourceEndViewportLeft = (axis is Axis.Y ? -sourceCenterPoint.X * targetScale.X : -targetCenterPoint.X) + (sourceCenterPoint.X * targetScale.X); - var sourceEndViewportTop = (axis is Axis.X ? -sourceCenterPoint.Y * targetScale.Y : -targetCenterPoint.Y) + (sourceCenterPoint.Y * targetScale.Y); - var sourceEndViewportRight = (axis is Axis.Y ? (sourceActualSize.X - sourceCenterPoint.X) * targetScale.X : targetActualSize.X - targetCenterPoint.X) + (sourceCenterPoint.X * targetScale.X); - var sourceEndViewportBottom = (axis is Axis.X ? (sourceActualSize.Y - sourceCenterPoint.Y) * targetScale.Y : targetActualSize.Y - targetCenterPoint.Y) + (sourceCenterPoint.Y * targetScale.Y); - - var targetBeginViewportLeft = (axis is Axis.Y ? -targetCenterPoint.X * inverseScale.X : -sourceCenterPoint.X) + (targetCenterPoint.X * inverseScale.X); - var targetBeginViewportTop = (axis is Axis.X ? -targetCenterPoint.Y * inverseScale.Y : -sourceCenterPoint.Y) + (targetCenterPoint.Y * inverseScale.Y); - var targetBeginViewportRight = (axis is Axis.Y ? (targetActualSize.X - targetCenterPoint.X) * inverseScale.X : sourceActualSize.X - sourceCenterPoint.X) + (targetCenterPoint.X * inverseScale.X); - var targetBeginViewportBottom = (axis is Axis.X ? (targetActualSize.Y - targetCenterPoint.Y) * inverseScale.Y : sourceActualSize.Y - sourceCenterPoint.Y) + (targetCenterPoint.Y * inverseScale.Y); - - var sourceLeftTop = new Vector2(sourceEndViewportLeft, sourceEndViewportTop) * inverseScale; - var sourceRightBottom = new Vector2(sourceEndViewportRight, sourceEndViewportBottom) * inverseScale; - var sourceRight = sourceActualSize.X - sourceRightBottom.X; - var sourceBottom = sourceActualSize.Y - sourceRightBottom.Y; - - var targetLeftTop = new Vector2(targetBeginViewportLeft, targetBeginViewportTop) * targetScale; - var targetRightBottom = new Vector2(targetBeginViewportRight, targetBeginViewportBottom) * targetScale; - var targetRight = targetActualSize.X - targetRightBottom.X; - var targetBottom = targetActualSize.Y - targetRightBottom.Y; - + var sourceToClip = GetConnectedElementClip(axis, targetScale, sourceActualSize, sourceCenterPoint, new Rect((-targetCenterPoint).ToPoint(), targetActualSize.ToSize())); + var targetFromClip = GetConnectedElementClip(axis, GetInverseScale(targetScale), targetActualSize, targetCenterPoint, new Rect((-sourceCenterPoint).ToPoint(), sourceActualSize.ToSize())); return ( - this.Clip( - GetFixedThickness(new Thickness(sourceLeftTop.X, sourceLeftTop.Y, sourceRight, sourceBottom), defaultValue), - from: defaultThickness, - duration: duration, - easingType: easingType, - easingMode: easingMode), - this.Clip( - defaultThickness, - GetFixedThickness(new Thickness(targetLeftTop.X, targetLeftTop.Y, targetRight, targetBottom), defaultValue), - duration: duration, - easingType: easingType, - easingMode: easingMode) + sourceToClip.HasValue + ? this.Clip( + sourceToClip.Value, + GetFixedThickness(default), + duration: duration, + easingType: easingType, + easingMode: easingMode) + : null, + targetFromClip.HasValue + ? this.Clip( + GetFixedThickness(default), + targetFromClip.Value, + duration: duration, + easingType: easingType, + easingMode: easingMode) + : null ); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 3fc8be7e0ef..5eac4391994 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -43,6 +43,7 @@ public FrameworkElement Source RestoreElements(this.SourceAnimatedElements); } + this._currentAnimationGroupController = null; this._source = value; this._sourceZIndex = value is null ? -1 : Canvas.GetZIndex(value); this._needUpdateSourceLayout = true; @@ -73,6 +74,7 @@ public FrameworkElement Target RestoreElements(this.TargetAnimatedElements); } + this._currentAnimationGroupController = null; this._target = value; this._targetZIndex = value is null ? -1 : Canvas.GetZIndex(value); this._needUpdateTargetLayout = true; diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 088cf0c55c6..4450a53767f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -18,21 +18,23 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations public sealed partial class TransitionHelper { private const double AlmostZero = 0.01; - private readonly Dictionary sourceConnectedAnimatedElements = new(); - private readonly Dictionary targetConnectedAnimatedElements = new(); - private readonly List sourceIndependentAnimatedElements = new(); - private readonly List targetIndependentAnimatedElements = new(); + private readonly Dictionary sourceConnectedElements = new(); + private readonly Dictionary targetConnectedElements = new(); + private readonly Dictionary> sourceCoordinatedElements = new(); + private readonly Dictionary> targetCoordinatedElements = new(); + private readonly List sourceIndependentElements = new(); + private readonly List targetIndependentElements = new(); private CancellationTokenSource _animateCancellationTokenSource; private CancellationTokenSource _reverseCancellationTokenSource; private bool _needUpdateSourceLayout; private bool _needUpdateTargetLayout; - private KeyFrameAnimationGroupController _currentAnimationGroupController; + private IKeyFrameAnimationGroupController _currentAnimationGroupController; - private IEnumerable SourceAnimatedElements => this.sourceConnectedAnimatedElements.Values.Concat(this.sourceIndependentAnimatedElements); + private IEnumerable SourceAnimatedElements => this.sourceConnectedElements.Values.Concat(this.sourceIndependentElements).Concat(this.sourceCoordinatedElements.SelectMany(item => item.Value)); - private IEnumerable TargetAnimatedElements => this.targetConnectedAnimatedElements.Values.Concat(this.targetIndependentAnimatedElements); + private IEnumerable TargetAnimatedElements => this.targetConnectedElements.Values.Concat(this.targetIndependentElements).Concat(this.targetCoordinatedElements.SelectMany(item => item.Value)); /// /// Morphs from source control to target control. @@ -61,6 +63,8 @@ public Task StartAsync(bool forceUpdateAnimatedElements) /// A that completes when all animations have completed. public async Task StartAsync(CancellationToken token, bool forceUpdateAnimatedElements) { + IsNotNullAndIsLoaded(this.Source, nameof(this.Source)); + IsNotNullAndIsLoaded(this.Target, nameof(this.Target)); if (this._animateCancellationTokenSource is not null) { return; @@ -119,6 +123,8 @@ public Task ReverseAsync(bool forceUpdateAnimatedElements) /// A that completes when all animations have completed. public async Task ReverseAsync(CancellationToken token, bool forceUpdateAnimatedElements) { + IsNotNullAndIsLoaded(this.Source, nameof(this.Source)); + IsNotNullAndIsLoaded(this.Target, nameof(this.Target)); if (this._reverseCancellationTokenSource is not null) { return; From 7f241874a7796f50146281bb6556c00f3836e4d2 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Tue, 23 Aug 2022 16:29:51 +0800 Subject: [PATCH 70/94] fix format --- .../Helpers/TransitionHelper.Logic.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 87f780f296a..bc01d02db85 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -146,7 +146,7 @@ private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken if (this.sourceCoordinatedElements.TryGetValue(item.Key, out var coordinatedElements)) { return result.Concat(coordinatedElements); - }; + } return result; }); @@ -158,7 +158,7 @@ private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken if (this.targetCoordinatedElements.TryGetValue(item.Key, out var coordinatedElements)) { return result.Concat(coordinatedElements); - }; + } return result; }); @@ -224,7 +224,8 @@ private void BuildConnectedAnimationController( UIElement target, List sourceAttachedElements, List targetAttachedElements, - TimeSpan duration, TransitionConfig config) + TimeSpan duration, + TransitionConfig config) { var sourceActualSize = source is FrameworkElement sourceElement ? new Vector2((float)sourceElement.ActualWidth, (float)sourceElement.ActualHeight) : source.ActualSize; var targetActualSize = target is FrameworkElement targetElement ? new Vector2((float)targetElement.ActualWidth, (float)targetElement.ActualHeight) : target.ActualSize; From 0b9201d9a17c2656186934b40de02cb486865ec7 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Tue, 23 Aug 2022 16:36:28 +0800 Subject: [PATCH 71/94] change some name --- .../Helpers/TransitionHelper.Logic.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index bc01d02db85..215d1638ad6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -236,7 +236,7 @@ private void BuildConnectedAnimationController( target.GetVisual().CenterPoint = new Vector3(targetCenterPoint, 0); var easingType = config.EasingType ?? this.DefaultEasingType; var easingMode = config.EasingMode ?? this.DefaultEasingMode; - var (sourceTranslationAnimation, targetTranslationAnimation, translation) = this.AnimateTranslation( + var (sourceTranslationAnimation, targetTranslationAnimation, sourceTargetTranslation) = this.AnimateTranslation( source, target, sourceCenterPoint, @@ -245,7 +245,7 @@ private void BuildConnectedAnimationController( easingType, easingMode); var (sourceOpacityAnimation, targetOpacityAnimation) = this.AnimateOpacity(duration, this.OpacityTransitionProgressKey); - var (sourceScaleAnimation, targetScaleAnimation, targetScale) = config.ScaleMode switch + var (sourceScaleAnimation, targetScaleAnimation, sourceTargetScale) = config.ScaleMode switch { ScaleMode.None => (null, null, Vector2.One), ScaleMode.Scale => this.AnimateScale( @@ -289,7 +289,7 @@ private void BuildConnectedAnimationController( { var targetControlBounds = new Rect(0, 0, this.Target.ActualWidth, this.Target.ActualHeight); var targetTransformedBounds = this.Target.TransformToVisual(this.Source).TransformBounds(targetControlBounds); - var scale = targetScale; + var targetScale = sourceTargetScale; foreach (var coordinatedElement in sourceAttachedElements) { var coordinatedElementActualSize = coordinatedElement is FrameworkElement coordinatedFrameworkElement ? new Vector2((float)coordinatedFrameworkElement.ActualWidth, (float)coordinatedFrameworkElement.ActualHeight) : coordinatedElement.ActualSize; @@ -302,8 +302,8 @@ private void BuildConnectedAnimationController( sourceOpacityAnimation, sourceScaleAnimation }); - var location = coordinatedElement.TransformToVisual(this.Source).TransformPoint(translation.ToPoint()); - var targetClip = GetCoordinatedElementClip(scale, location, (coordinatedElementActualSize * scale).ToSize(), targetTransformedBounds); + var location = coordinatedElement.TransformToVisual(this.Source).TransformPoint(sourceTargetTranslation.ToPoint()); + var targetClip = GetCoordinatedElementClip(targetScale, location, (coordinatedElementActualSize * targetScale).ToSize(), targetTransformedBounds); if (targetClip.HasValue) { controller.AddAnimationGroupFor( @@ -321,7 +321,7 @@ private void BuildConnectedAnimationController( { var sourceControlBounds = new Rect(0, 0, this.Source.ActualWidth, this.Source.ActualHeight); var sourceTransformedBounds = this.Source.TransformToVisual(this.Target).TransformBounds(sourceControlBounds); - var scale = GetInverseScale(targetScale); + var targetScale = GetInverseScale(sourceTargetScale); foreach (var coordinatedElement in targetAttachedElements) { var coordinatedElementActualSize = coordinatedElement is FrameworkElement coordinatedFrameworkElement ? new Vector2((float)coordinatedFrameworkElement.ActualWidth, (float)coordinatedFrameworkElement.ActualHeight) : coordinatedElement.ActualSize; @@ -334,8 +334,8 @@ private void BuildConnectedAnimationController( targetOpacityAnimation, targetScaleAnimation }); - var location = coordinatedElement.TransformToVisual(this.Target).TransformPoint((-translation).ToPoint()); - var targetClip = GetCoordinatedElementClip(scale, location, (coordinatedElementActualSize * scale).ToSize(), sourceTransformedBounds); + var location = coordinatedElement.TransformToVisual(this.Target).TransformPoint((-sourceTargetTranslation).ToPoint()); + var targetClip = GetCoordinatedElementClip(targetScale, location, (coordinatedElementActualSize * targetScale).ToSize(), sourceTransformedBounds); if (targetClip.HasValue) { controller.AddAnimationGroupFor( @@ -365,7 +365,7 @@ private void BuildConnectedAnimationController( targetActualSize, sourceCenterPoint, targetCenterPoint, - targetScale, + sourceTargetScale, duration, easingType, easingMode, @@ -545,14 +545,14 @@ private Task AnimateIndependentElements( Vector2 targetActualSize, Vector2 sourceCenterPoint, Vector2 targetCenterPoint, - Vector2 targetScale, + Vector2 sourceTargetScale, TimeSpan duration, EasingType easingType, EasingMode easingMode, Axis? axis) { - var sourceToClip = GetConnectedElementClip(axis, targetScale, sourceActualSize, sourceCenterPoint, new Rect((-targetCenterPoint).ToPoint(), targetActualSize.ToSize())); - var targetFromClip = GetConnectedElementClip(axis, GetInverseScale(targetScale), targetActualSize, targetCenterPoint, new Rect((-sourceCenterPoint).ToPoint(), sourceActualSize.ToSize())); + var sourceToClip = GetConnectedElementClip(axis, sourceTargetScale, sourceActualSize, sourceCenterPoint, new Rect((-targetCenterPoint).ToPoint(), targetActualSize.ToSize())); + var targetFromClip = GetConnectedElementClip(axis, GetInverseScale(sourceTargetScale), targetActualSize, targetCenterPoint, new Rect((-sourceCenterPoint).ToPoint(), sourceActualSize.ToSize())); return ( sourceToClip.HasValue ? this.Clip( From a5f16e48711a6aa180f623c5f18a822e41c946d9 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Tue, 23 Aug 2022 17:53:24 +0800 Subject: [PATCH 72/94] update --- .../Helpers/TransitionHelper.Animation.cs | 38 +++++++++++++------ .../Helpers/TransitionHelper.Logic.cs | 6 +-- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index 5cf48ba02db..1844045588a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -35,9 +35,9 @@ private interface IKeyFrameAnimationGroupController { float? LastStopProgress { get; } - Task StartAsync(CancellationToken token, TimeSpan? duration, float? startProgress); + Task StartAsync(CancellationToken token, TimeSpan? duration); - Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, TimeSpan? duration, float? startProgress); + Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, TimeSpan? duration); void AddAnimationFor(UIElement target, IKeyFrameCompositionAnimationFactory factory); @@ -62,7 +62,7 @@ public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reverse target = null; var direction = reversed ? AnimationDirection.Reverse : AnimationDirection.Normal; - var keyFrames = (reversed && useReversedKeyframes && ReversedNormalizedKeyFrames is not null) ? ReversedNormalizedKeyFrames : NormalizedKeyFrames; + var keyFrames = (useReversedKeyframes && ReversedNormalizedKeyFrames is not null) ? ReversedNormalizedKeyFrames : NormalizedKeyFrames; if (typeof(T) == typeof(float)) { @@ -281,6 +281,8 @@ private sealed class KeyFrameAnimationGroupController : IKeyFrameAnimationGroupC private bool _lastInverseEasingFunction = false; + private bool _lastStartInNormalDirection = true; + public void AddAnimationFor(UIElement target, IKeyFrameCompositionAnimationFactory factory) { if (factory is null) @@ -316,25 +318,37 @@ public void AddAnimationGroupFor(UIElement target, IKeyFrameCompositionAnimation } } - public Task StartAsync(CancellationToken token, TimeSpan? duration, float? startProgress) + public Task StartAsync(CancellationToken token, TimeSpan? duration) { - var start = startProgress ?? this.LastStopProgress; + var start = this.LastStopProgress; var isInterruptedAnimation = start.HasValue; - var inverse = isInterruptedAnimation && this._lastInverseEasingFunction; - return AnimateAsync(false, false, token, inverse, duration, start); + if (isInterruptedAnimation is false) + { + this._lastStartInNormalDirection = true; + } + + var inverseEasing = isInterruptedAnimation && this._lastInverseEasingFunction; + var useReversedKeyframes = isInterruptedAnimation && !this._lastStartInNormalDirection; + return AnimateAsync(false, useReversedKeyframes, token, inverseEasing, duration, start); } - public Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, TimeSpan? duration, float? startProgress) + public Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, TimeSpan? duration) { - var start = startProgress; - if (startProgress.HasValue is false && this.LastStopProgress.HasValue) + float? start = null; + if (this.LastStopProgress.HasValue) { start = 1 - this.LastStopProgress.Value; } var isInterruptedAnimation = start.HasValue; - var inverse = (isInterruptedAnimation && this._lastInverseEasingFunction) || (!isInterruptedAnimation && inverseEasingFunction); - return AnimateAsync(true, !isInterruptedAnimation, token, inverse, duration, start); + if (isInterruptedAnimation is false) + { + this._lastStartInNormalDirection = false; + } + + var inverseEasing = (isInterruptedAnimation && this._lastInverseEasingFunction) || (!isInterruptedAnimation && inverseEasingFunction); + var useReversedKeyframes = !isInterruptedAnimation || !this._lastStartInNormalDirection; + return AnimateAsync(true, useReversedKeyframes, token, inverseEasing, duration, start); } private Task AnimateAsync(bool reversed, bool useReversedKeyframes, CancellationToken token, bool inverseEasingFunction, TimeSpan? duration, float? startProgress) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 215d1638ad6..db68bd02083 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -195,8 +195,8 @@ private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken } animationTasks.Add(reversed - ? _currentAnimationGroupController.ReverseAsync(token, this.InverseEasingFunctionWhenReversing, duration, null) - : _currentAnimationGroupController.StartAsync(token, duration, null)); + ? _currentAnimationGroupController.ReverseAsync(token, this.InverseEasingFunctionWhenReversing, duration) + : _currentAnimationGroupController.StartAsync(token, duration)); animationTasks.Add( this.AnimateIndependentElements( @@ -446,7 +446,7 @@ private Task AnimateIndependentElements( } } - return controller.StartAsync(token, null, null); + return controller.StartAsync(token, null); } private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory, Vector2) AnimateTranslation( From 38cf490d4c4c9c0b7443e95aebbcbbf2110b08c5 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Wed, 28 Sep 2022 17:37:18 +0800 Subject: [PATCH 73/94] update --- .../Helpers/TransitionHelper.Helpers.cs | 11 +++++---- .../Helpers/TransitionHelper.Logic.cs | 4 ++-- .../Helpers/TransitionHelper.cs | 23 ++++++++++--------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index b99c3bd31cc..737746c1c11 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -11,6 +11,7 @@ using Windows.UI.Composition; using Windows.UI.Xaml; using Windows.UI.Xaml.Hosting; +using Windows.UI.Xaml.Media; namespace Microsoft.Toolkit.Uwp.UI.Animations { @@ -94,16 +95,16 @@ private static void RestoreElement(UIElement animatedElement) visual.Properties.InsertVector3(TranslationPropertyName, Vector3.Zero); } - private static void IsNotNullAndIsLoaded(FrameworkElement target, string name) + private static void IsNotNullAndIsInVisualTree(FrameworkElement target, string name) { if (target is null) { throw new ArgumentNullException(name); } - if (target.IsLoaded is false) + if (VisualTreeHelper.GetParent(target) is null) { - throw new ArgumentException($"The {name} element has not been loaded yet.", name); + throw new ArgumentException($"The {name} element is not in the visual tree.", name); } } @@ -132,7 +133,7 @@ private static Thickness GetFixedThickness(Thickness thickness, double defaultVa return new Thickness(left, top, right, bottom); } - private static Thickness? GetCoordinatedElementClip(Vector2 scale, Point targetLocation, Size targetSize, Rect targetParentBounds) + private static Thickness? GetElementClip(Vector2 scale, Point targetLocation, Size targetSize, Rect targetParentBounds) { var inverseScale = GetInverseScale(scale); var targetBounds = new Rect(targetLocation, targetSize); @@ -166,7 +167,7 @@ private static Thickness GetFixedThickness(Thickness thickness, double defaultVa targetParentBounds.X = minX; } - return GetCoordinatedElementClip(scale, targetLocation.ToPoint(), targetSize, targetParentBounds); + return GetElementClip(scale, targetLocation.ToPoint(), targetSize, targetParentBounds); } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index db68bd02083..6d1fc80a931 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -303,7 +303,7 @@ private void BuildConnectedAnimationController( sourceScaleAnimation }); var location = coordinatedElement.TransformToVisual(this.Source).TransformPoint(sourceTargetTranslation.ToPoint()); - var targetClip = GetCoordinatedElementClip(targetScale, location, (coordinatedElementActualSize * targetScale).ToSize(), targetTransformedBounds); + var targetClip = GetElementClip(targetScale, location, (coordinatedElementActualSize * targetScale).ToSize(), targetTransformedBounds); if (targetClip.HasValue) { controller.AddAnimationGroupFor( @@ -335,7 +335,7 @@ private void BuildConnectedAnimationController( targetScaleAnimation }); var location = coordinatedElement.TransformToVisual(this.Target).TransformPoint((-sourceTargetTranslation).ToPoint()); - var targetClip = GetCoordinatedElementClip(targetScale, location, (coordinatedElementActualSize * targetScale).ToSize(), sourceTransformedBounds); + var targetClip = GetElementClip(targetScale, location, (coordinatedElementActualSize * targetScale).ToSize(), sourceTransformedBounds); if (targetClip.HasValue) { controller.AddAnimationGroupFor( diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 4450a53767f..19b76b69d44 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -36,6 +36,11 @@ public sealed partial class TransitionHelper private IEnumerable TargetAnimatedElements => this.targetConnectedElements.Values.Concat(this.targetIndependentElements).Concat(this.targetCoordinatedElements.SelectMany(item => item.Value)); + /// + /// Gets a value indicating whether the source and target controls are animating. + /// + public bool IsAnimating => _animateCancellationTokenSource is not null || _reverseCancellationTokenSource is not null; + /// /// Morphs from source control to target control. /// @@ -63,8 +68,8 @@ public Task StartAsync(bool forceUpdateAnimatedElements) /// A that completes when all animations have completed. public async Task StartAsync(CancellationToken token, bool forceUpdateAnimatedElements) { - IsNotNullAndIsLoaded(this.Source, nameof(this.Source)); - IsNotNullAndIsLoaded(this.Target, nameof(this.Target)); + IsNotNullAndIsInVisualTree(this.Source, nameof(this.Source)); + IsNotNullAndIsInVisualTree(this.Target, nameof(this.Target)); if (this._animateCancellationTokenSource is not null) { return; @@ -123,8 +128,8 @@ public Task ReverseAsync(bool forceUpdateAnimatedElements) /// A that completes when all animations have completed. public async Task ReverseAsync(CancellationToken token, bool forceUpdateAnimatedElements) { - IsNotNullAndIsLoaded(this.Source, nameof(this.Source)); - IsNotNullAndIsLoaded(this.Target, nameof(this.Target)); + IsNotNullAndIsInVisualTree(this.Source, nameof(this.Source)); + IsNotNullAndIsInVisualTree(this.Target, nameof(this.Target)); if (this._reverseCancellationTokenSource is not null) { return; @@ -162,15 +167,11 @@ public async Task ReverseAsync(CancellationToken token, bool forceUpdateAnimated /// Indicates whether to reset to initial state. default value is True, if it is False, it will be reset to target state. public void Reset(bool toInitialState = true) { - if (this._animateCancellationTokenSource is not null) + if (IsAnimating) { - this._animateCancellationTokenSource.Cancel(); + this._animateCancellationTokenSource?.Cancel(); this._animateCancellationTokenSource = null; - } - - if (_reverseCancellationTokenSource is not null) - { - this._reverseCancellationTokenSource.Cancel(); + this._reverseCancellationTokenSource?.Cancel(); this._reverseCancellationTokenSource = null; } From 2b219854d27d4870435677fab94ac71dd1eb5b3b Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Thu, 29 Sep 2022 17:24:19 +0800 Subject: [PATCH 74/94] add FindDescendantsWithBFSAndPruneAndPredicate --- .../Helpers/TransitionHelper.Helpers.cs | 62 ++++++++++++++++++- .../Helpers/TransitionHelper.Logic.cs | 41 +++++------- 2 files changed, 77 insertions(+), 26 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index 737746c1c11..4c178a3cb24 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -40,12 +40,70 @@ public int GetHashCode(DependencyObject obj) private static IEnumerable GetAnimatedElements(DependencyObject targetElement) { - return targetElement?.FindDescendantsOrSelf() - .Where(element => GetId(element) is not null || GetCoordinatedTarget(element) is not null || GetIsIndependent(element)) + return FindDescendantsWithBFSAndPruneAndPredicate(targetElement, IsNotVisible, IsAnimatedElement) .Distinct(new AnimatedElementComparer()) .OfType(); } + private static bool IsNotVisible(DependencyObject element) + { + if (element is not UIElement target + || target.Visibility == Visibility.Collapsed + || target.Opacity < AlmostZero) + { + return true; + } + + return false; + } + + private static bool IsAnimatedElement(DependencyObject element) + { + return GetId(element) is not null || GetCoordinatedTarget(element) is not null || GetIsIndependent(element); + } + + private static IEnumerable FindDescendantsWithBFSAndPruneAndPredicate(DependencyObject element, Func prune, Func predicate) + { + if (predicate(element)) + { + yield return element; + yield break; + } + + var searchQueue = new List(); + var childrenCount = VisualTreeHelper.GetChildrenCount(element); + for (var i = 0; i < childrenCount; i++) + { + var child = VisualTreeHelper.GetChild(element, i); + if (predicate(child)) + { + yield return child; + } + else if (prune(child) is false) + { + searchQueue.Add(child); + } + } + + for (var i = 0; i < searchQueue.Count; i++) + { + var parent = searchQueue[i]; + childrenCount = VisualTreeHelper.GetChildrenCount(parent); + for (var j = 0; j < childrenCount; j++) + { + var child = VisualTreeHelper.GetChild(parent, j); + if (predicate(child)) + { + yield return child; + } + else if (prune(child) is false) + { + searchQueue.Add(child); + } + } + } + } + private static void ToggleVisualState(UIElement target, VisualStateToggleMethod method, bool isVisible) { if (target is null) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 6d1fc80a931..33c7de913b5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -89,8 +89,8 @@ private async Task InitControlsStateAsync(bool forceUpdateAnimatedElements = fal Canvas.SetZIndex(this.IsTargetState ? this.Source : this.Target, maxZIndex); await Task.WhenAll( - this.InitControlState(this.Source, this._needUpdateSourceLayout), - this.InitControlState(this.Target, this._needUpdateTargetLayout)); + this.InitControlStateAsync(this.Source, this._needUpdateSourceLayout), + this.InitControlStateAsync(this.Target, this._needUpdateTargetLayout)); this._needUpdateSourceLayout = false; this._needUpdateTargetLayout = false; @@ -102,47 +102,39 @@ await Task.WhenAll( } } - private Task InitControlState(FrameworkElement target, bool needUpdateLayout) + private async Task InitControlStateAsync(FrameworkElement target, bool needUpdateLayout) { - var updateLayoutTask = Task.CompletedTask; if (target is null) { - return updateLayoutTask; + return; } target.IsHitTestVisible = IsHitTestVisibleWhenAnimating; - if (target.Visibility == Visibility.Collapsed) { target.Visibility = Visibility.Visible; if (needUpdateLayout) { - updateLayoutTask = UpdateControlLayout(target); + await UpdateControlLayout(target); } } - - if (target.Opacity < 0.01) + else if (target.Opacity < AlmostZero) { target.Opacity = 1; } - - var targetVisual = target.GetVisual(); - if (!targetVisual.IsVisible) + else if (target.GetVisual() is { IsVisible: false } visual) { - targetVisual.IsVisible = true; + visual.IsVisible = true; } - - return updateLayoutTask; } private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken token) { - var animationTasks = new List(3); var sourceUnpairedElements = this.sourceConnectedElements .Where(item => !this.targetConnectedElements.ContainsKey(item.Key)) .SelectMany(item => { - var result = new List(1) { item.Value }; + var result = new[] { item.Value }; if (this.sourceCoordinatedElements.TryGetValue(item.Key, out var coordinatedElements)) { return result.Concat(coordinatedElements); @@ -154,7 +146,7 @@ private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken .Where(item => !this.sourceConnectedElements.ContainsKey(item.Key)) .SelectMany(item => { - var result = new List(1) { item.Value }; + var result = new[] { item.Value }; if (this.targetCoordinatedElements.TryGetValue(item.Key, out var coordinatedElements)) { return result.Concat(coordinatedElements); @@ -194,26 +186,27 @@ private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken startTime = startProgress * duration; } - animationTasks.Add(reversed + var animationTasks = new[] + { + reversed ? _currentAnimationGroupController.ReverseAsync(token, this.InverseEasingFunctionWhenReversing, duration) - : _currentAnimationGroupController.StartAsync(token, duration)); + : _currentAnimationGroupController.StartAsync(token, duration), - animationTasks.Add( this.AnimateIndependentElements( this.sourceIndependentElements.Concat(sourceUnpairedElements), reversed, token, startTime, IndependentElementEasingType, - IndependentElementEasingMode)); - animationTasks.Add( + IndependentElementEasingMode), this.AnimateIndependentElements( this.targetIndependentElements.Concat(targetUnpairedElements), !reversed, token, startTime, IndependentElementEasingType, - IndependentElementEasingMode)); + IndependentElementEasingMode) + }; return Task.WhenAll(animationTasks); } From f7c469da2fed2152737b36558f98d0a1295c8408 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Thu, 29 Sep 2022 17:26:14 +0800 Subject: [PATCH 75/94] update --- .../Helpers/TransitionHelper.Helpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index 4c178a3cb24..4873df833f6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -20,7 +20,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// public sealed partial class TransitionHelper { - private class AnimatedElementComparer : IEqualityComparer + private sealed class AnimatedElementComparer : IEqualityComparer { public bool Equals(DependencyObject x, DependencyObject y) { From 5dc80ac1adc7ebe135c6fb1bfb797894452d756d Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 30 Sep 2022 14:17:35 +0800 Subject: [PATCH 76/94] Use Queue instead of List --- .../Helpers/TransitionHelper.Helpers.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index 4873df833f6..4f5b1d15390 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -70,7 +70,7 @@ private static IEnumerable FindDescendantsWithBFSAndPruneAndPr yield break; } - var searchQueue = new List(); + var searchQueue = new Queue(); var childrenCount = VisualTreeHelper.GetChildrenCount(element); for (var i = 0; i < childrenCount; i++) { @@ -81,13 +81,13 @@ private static IEnumerable FindDescendantsWithBFSAndPruneAndPr } else if (prune(child) is false) { - searchQueue.Add(child); + searchQueue.Enqueue(child); } } - for (var i = 0; i < searchQueue.Count; i++) + while (searchQueue.Count > 0) { - var parent = searchQueue[i]; + var parent = searchQueue.Dequeue(); childrenCount = VisualTreeHelper.GetChildrenCount(parent); for (var j = 0; j < childrenCount; j++) { @@ -98,7 +98,7 @@ private static IEnumerable FindDescendantsWithBFSAndPruneAndPr } else if (prune(child) is false) { - searchQueue.Add(child); + searchQueue.Enqueue(child); } } } From bed11036ee0132ccd190d6778fbeb88cfe8a0fc2 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 30 Sep 2022 14:56:43 +0800 Subject: [PATCH 77/94] Add AnimatedElements --- .../Helpers/TransitionHelper.Helpers.cs | 34 +++++++- .../Helpers/TransitionHelper.Logic.cs | 81 +++++-------------- .../Helpers/TransitionHelper.Properties.cs | 8 +- .../Helpers/TransitionHelper.cs | 26 +++--- 4 files changed, 70 insertions(+), 79 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index 4f5b1d15390..c07fe15b91e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -38,11 +38,41 @@ public int GetHashCode(DependencyObject obj) } } - private static IEnumerable GetAnimatedElements(DependencyObject targetElement) + private static AnimatedElements GetAnimatedElements(DependencyObject parent) { - return FindDescendantsWithBFSAndPruneAndPredicate(targetElement, IsNotVisible, IsAnimatedElement) + var animatedElements = new AnimatedElements(new(), new(), new()); + if (parent is null) + { + return null; + } + + var allAnimatedElements = FindDescendantsWithBFSAndPruneAndPredicate(parent, IsNotVisible, IsAnimatedElement) .Distinct(new AnimatedElementComparer()) .OfType(); + foreach (var item in allAnimatedElements) + { + if (GetId(item) is { } id) + { + animatedElements.ConnectedElements[id] = item; + } + else if (GetCoordinatedTarget(item) is { } targetId) + { + if (animatedElements.CoordinatedElements.ContainsKey(targetId) is false) + { + animatedElements.CoordinatedElements[targetId] = new List { item }; + } + else + { + animatedElements.CoordinatedElements[targetId].Add(item); + } + } + else + { + animatedElements.IndependentElements.Add(item); + } + } + + return animatedElements; } private static bool IsNotVisible(DependencyObject element) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 33c7de913b5..bac41015526 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -20,51 +20,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// public sealed partial class TransitionHelper { - private void UpdateSourceAnimatedElements() - { - UpdateAnimatedElements(this.Source, this.sourceConnectedElements, this.sourceCoordinatedElements, this.sourceIndependentElements); - } - - private void UpdateTargetAnimatedElements() - { - UpdateAnimatedElements(this.Target, this.targetConnectedElements, this.targetCoordinatedElements, this.targetIndependentElements); - } - - private void UpdateAnimatedElements(DependencyObject parent, IDictionary connectedElements, IDictionary> coordinatedElements, ICollection independentElements) - { - connectedElements.Clear(); - coordinatedElements.Clear(); - independentElements.Clear(); - - if (parent is null) - { - return; - } - - foreach (var item in GetAnimatedElements(parent)) - { - if (GetId(item) is { } id) - { - connectedElements[id] = item; - } - else if (GetCoordinatedTarget(item) is { } targetId) - { - if (coordinatedElements.ContainsKey(targetId) is false) - { - coordinatedElements[targetId] = new List { item }; - } - else - { - coordinatedElements[targetId].Add(item); - } - } - else - { - independentElements.Add(item); - } - } - } - private void RestoreState(bool isTargetState) { this.IsTargetState = isTargetState; @@ -72,15 +27,15 @@ private void RestoreState(bool isTargetState) { Canvas.SetZIndex(this.Source, _sourceZIndex); ToggleVisualState(this.Source, this.SourceToggleMethod, !isTargetState); + RestoreElements(this.SourceAnimatedElements.All()); } if (this.Target is not null) { Canvas.SetZIndex(this.Target, _targetZIndex); ToggleVisualState(this.Target, this.TargetToggleMethod, isTargetState); + RestoreElements(this.TargetAnimatedElements.All()); } - - RestoreElements(this.SourceAnimatedElements.Concat(this.TargetAnimatedElements)); } private async Task InitControlsStateAsync(bool forceUpdateAnimatedElements = false) @@ -97,8 +52,8 @@ await Task.WhenAll( if (forceUpdateAnimatedElements) { - this.UpdateSourceAnimatedElements(); - this.UpdateTargetAnimatedElements(); + _sourceAnimatedElements = null; + _targetAnimatedElements = null; } } @@ -130,44 +85,44 @@ private async Task InitControlStateAsync(FrameworkElement target, bool needUpdat private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken token) { - var sourceUnpairedElements = this.sourceConnectedElements - .Where(item => !this.targetConnectedElements.ContainsKey(item.Key)) + var sourceUnpairedElements = this.SourceAnimatedElements.ConnectedElements + .Where(item => !this.TargetAnimatedElements.ConnectedElements.ContainsKey(item.Key)) .SelectMany(item => { var result = new[] { item.Value }; - if (this.sourceCoordinatedElements.TryGetValue(item.Key, out var coordinatedElements)) + if (this.SourceAnimatedElements.CoordinatedElements.TryGetValue(item.Key, out var coordinatedElements)) { return result.Concat(coordinatedElements); } return result; }); - var targetUnpairedElements = this.targetConnectedElements - .Where(item => !this.sourceConnectedElements.ContainsKey(item.Key)) + var targetUnpairedElements = this.TargetAnimatedElements.ConnectedElements + .Where(item => !this.SourceAnimatedElements.ConnectedElements.ContainsKey(item.Key)) .SelectMany(item => { var result = new[] { item.Value }; - if (this.targetCoordinatedElements.TryGetValue(item.Key, out var coordinatedElements)) + if (this.TargetAnimatedElements.CoordinatedElements.TryGetValue(item.Key, out var coordinatedElements)) { return result.Concat(coordinatedElements); } return result; }); - var pairedElementKeys = this.sourceConnectedElements - .Where(item => this.targetConnectedElements.ContainsKey(item.Key)) + var pairedElementKeys = this.SourceAnimatedElements.ConnectedElements + .Where(item => this.TargetAnimatedElements.ConnectedElements.ContainsKey(item.Key)) .Select(item => item.Key); if (_currentAnimationGroupController is null) { _currentAnimationGroupController = new KeyFrameAnimationGroupController(); foreach (var key in pairedElementKeys) { - var source = this.sourceConnectedElements[key]; - var target = this.targetConnectedElements[key]; + var source = this.SourceAnimatedElements.ConnectedElements[key]; + var target = this.TargetAnimatedElements.ConnectedElements[key]; var animationConfig = this.Configs.FirstOrDefault(config => config.Id == key) ?? this.DefaultConfig; - this.sourceCoordinatedElements.TryGetValue(key, out var sourceAttachedElements); - this.targetCoordinatedElements.TryGetValue(key, out var targetAttachedElements); + this.SourceAnimatedElements.CoordinatedElements.TryGetValue(key, out var sourceAttachedElements); + this.TargetAnimatedElements.CoordinatedElements.TryGetValue(key, out var targetAttachedElements); this.BuildConnectedAnimationController( _currentAnimationGroupController, source, @@ -193,14 +148,14 @@ private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken : _currentAnimationGroupController.StartAsync(token, duration), this.AnimateIndependentElements( - this.sourceIndependentElements.Concat(sourceUnpairedElements), + this.SourceAnimatedElements.IndependentElements.Concat(sourceUnpairedElements), reversed, token, startTime, IndependentElementEasingType, IndependentElementEasingMode), this.AnimateIndependentElements( - this.targetIndependentElements.Concat(targetUnpairedElements), + this.TargetAnimatedElements.IndependentElements.Concat(targetUnpairedElements), !reversed, token, startTime, diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 5eac4391994..f10976761e4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -40,14 +40,14 @@ public FrameworkElement Source if (this._source is not null) { - RestoreElements(this.SourceAnimatedElements); + RestoreElements(this.SourceAnimatedElements.All()); } this._currentAnimationGroupController = null; this._source = value; this._sourceZIndex = value is null ? -1 : Canvas.GetZIndex(value); this._needUpdateSourceLayout = true; - this.UpdateSourceAnimatedElements(); + this._sourceAnimatedElements = null; this.Reset(true); } } @@ -71,14 +71,14 @@ public FrameworkElement Target if (this._target is not null) { - RestoreElements(this.TargetAnimatedElements); + RestoreElements(this.TargetAnimatedElements.All()); } this._currentAnimationGroupController = null; this._target = value; this._targetZIndex = value is null ? -1 : Canvas.GetZIndex(value); this._needUpdateTargetLayout = true; - this.UpdateTargetAnimatedElements(); + this._targetAnimatedElements = null; this.Reset(true); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 19b76b69d44..1c3f52eb25c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -17,24 +17,30 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations [ContentProperty(Name = nameof(Configs))] public sealed partial class TransitionHelper { - private const double AlmostZero = 0.01; - private readonly Dictionary sourceConnectedElements = new(); - private readonly Dictionary targetConnectedElements = new(); - private readonly Dictionary> sourceCoordinatedElements = new(); - private readonly Dictionary> targetCoordinatedElements = new(); - private readonly List sourceIndependentElements = new(); - private readonly List targetIndependentElements = new(); + private sealed record AnimatedElements( + Dictionary ConnectedElements, + Dictionary> CoordinatedElements, + List IndependentElements) + { + public IEnumerable All() + { + return this.ConnectedElements.Values.Concat(this.IndependentElements).Concat(this.CoordinatedElements.SelectMany(item => item.Value)); + } + } + private const double AlmostZero = 0.01; + private AnimatedElements _sourceAnimatedElements; + private AnimatedElements _targetAnimatedElements; private CancellationTokenSource _animateCancellationTokenSource; private CancellationTokenSource _reverseCancellationTokenSource; private bool _needUpdateSourceLayout; private bool _needUpdateTargetLayout; - private IKeyFrameAnimationGroupController _currentAnimationGroupController; + private AnimatedElements SourceAnimatedElements => _sourceAnimatedElements ??= GetAnimatedElements(this.Source); - private IEnumerable SourceAnimatedElements => this.sourceConnectedElements.Values.Concat(this.sourceIndependentElements).Concat(this.sourceCoordinatedElements.SelectMany(item => item.Value)); + private AnimatedElements TargetAnimatedElements => _targetAnimatedElements ??= GetAnimatedElements(this.Target); - private IEnumerable TargetAnimatedElements => this.targetConnectedElements.Values.Concat(this.targetIndependentElements).Concat(this.targetCoordinatedElements.SelectMany(item => item.Value)); + private IKeyFrameAnimationGroupController _currentAnimationGroupController; /// /// Gets a value indicating whether the source and target controls are animating. From c66fc77b1ca780dd847403ccfe17cfe4c87e48b0 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 30 Sep 2022 15:22:30 +0800 Subject: [PATCH 78/94] update --- .../Helpers/TransitionHelper.Properties.cs | 30 +++++++++++++++---- .../Helpers/TransitionHelper.cs | 25 +++++++++++----- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index f10976761e4..bd2703ca20c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -38,17 +38,26 @@ public FrameworkElement Source return; } - if (this._source is not null) + var needReset = IsAnimating || IsTargetState; + if (IsAnimating && this._source is not null) { + this.Stop(); RestoreElements(this.SourceAnimatedElements.All()); } + if (value is { IsLoaded: false } or { Visibility: Visibility.Collapsed }) + { + this._needUpdateSourceLayout = true; + } + this._currentAnimationGroupController = null; this._source = value; this._sourceZIndex = value is null ? -1 : Canvas.GetZIndex(value); - this._needUpdateSourceLayout = true; this._sourceAnimatedElements = null; - this.Reset(true); + if (needReset) + { + this.Reset(true); + } } } @@ -69,17 +78,26 @@ public FrameworkElement Target return; } - if (this._target is not null) + var needReset = IsAnimating || IsTargetState; + if (IsAnimating && this._target is not null) { + this.Stop(); RestoreElements(this.TargetAnimatedElements.All()); } + if (value is { IsLoaded: false } or { Visibility: Visibility.Collapsed }) + { + this._needUpdateTargetLayout = true; + } + this._currentAnimationGroupController = null; this._target = value; this._targetZIndex = value is null ? -1 : Canvas.GetZIndex(value); - this._needUpdateTargetLayout = true; this._targetAnimatedElements = null; - this.Reset(true); + if (needReset) + { + this.Reset(true); + } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 1c3f52eb25c..1c0af30cacd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -168,19 +168,28 @@ public async Task ReverseAsync(CancellationToken token, bool forceUpdateAnimated } /// - /// Reset to initial or target state. + /// Stop all animations. /// - /// Indicates whether to reset to initial state. default value is True, if it is False, it will be reset to target state. - public void Reset(bool toInitialState = true) + public void Stop() { - if (IsAnimating) + if (IsAnimating is false) { - this._animateCancellationTokenSource?.Cancel(); - this._animateCancellationTokenSource = null; - this._reverseCancellationTokenSource?.Cancel(); - this._reverseCancellationTokenSource = null; + return; } + this._animateCancellationTokenSource?.Cancel(); + this._animateCancellationTokenSource = null; + this._reverseCancellationTokenSource?.Cancel(); + this._reverseCancellationTokenSource = null; + } + + /// + /// Reset to initial or target state. + /// + /// Indicates whether to reset to initial state. default value is True, if it is False, it will be reset to target state. + public void Reset(bool toInitialState = true) + { + this.Stop(); this._currentAnimationGroupController = null; this.RestoreState(!toInitialState); } From d8ce6b166448f3f9b6e341326719c9b231960a10 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 30 Sep 2022 17:02:28 +0800 Subject: [PATCH 79/94] Add CustomScaleHandler to TransitionConfig --- .../Enums/ScaleMode.cs | 13 ++++++--- .../Helpers/TransitionConfig.cs | 16 +++++++++++ .../Helpers/TransitionHelper.Logic.cs | 27 +++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs index 30f75733129..1ba33780e7b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs @@ -15,20 +15,25 @@ public enum ScaleMode None, /// - /// Apply the scale changes to the horizontal and vertical directions of the UI element. + /// Apply the scaling changes to the horizontal and vertical directions of the UI element. /// Scale, /// - /// Apply the scale changes to the horizontal and vertical directions of the UI element, - /// but the value is calculated based on the changes in the horizontal direction. + /// Apply the scaling changes to the horizontal and vertical directions of the UI element, + /// but the value is calculated based on the change in the horizontal direction. /// ScaleX, /// - /// Apply scale changes to the horizontal and vertical directions of the UI element, + /// Apply scaling changes to the horizontal and vertical directions of the UI element, /// but the value is calculated based on the change in the vertical direction. /// ScaleY, + + /// + /// Apply the scaling changes calculated by using custom scale handler. + /// + Custom, } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs index cdd469f7be3..46bc18aeec2 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs @@ -3,10 +3,19 @@ // See the LICENSE file in the project root for more information. using Windows.Foundation; +using Windows.UI.Xaml; using Windows.UI.Xaml.Media.Animation; namespace Microsoft.Toolkit.Uwp.UI.Animations { + /// + /// Handler used to calculate the change in the scaling of an element when it is in transition. + /// + /// The source element. + /// The target element. + /// A value tuple whose first value represents the horizontal scaling change and whose second value represents the vertical scaling change. + public delegate (double, double) ScaleHandler(UIElement source, UIElement target); + /// /// Configuration used for the transition between UI elements. /// @@ -23,6 +32,13 @@ public class TransitionConfig /// public ScaleMode ScaleMode { get; set; } = ScaleMode.None; + /// + /// Gets or sets the custom scale handler. + /// Only works when is . + /// If this value is not set, the scale strategy will fall back to . + /// + public ScaleHandler CustomScaleHandler { get; set; } = null; + /// /// Gets or sets a value indicating whether clip animations are enabled for the target UI elements. /// diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index bac41015526..183777bd9be 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -214,6 +214,13 @@ private void BuildConnectedAnimationController( duration, easingType, easingMode), + ScaleMode.Custom => this.AnimateScaleWithScaleHandler( + source, + target, + config.CustomScaleHandler, + duration, + easingType, + easingMode), _ => (null, null, Vector2.One), }; @@ -306,6 +313,7 @@ private void BuildConnectedAnimationController( ScaleMode.Scale => null, ScaleMode.ScaleX => Axis.Y, ScaleMode.ScaleY => Axis.X, + ScaleMode.Custom => null, _ => null, }; var (sourceClipAnimationGroup, targetClipAnimationGroup) = this.AnimateClip( @@ -452,6 +460,25 @@ private Task AnimateIndependentElements( return (sourceFactory, targetFactory, scale); } + private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory, Vector2) AnimateScaleWithScaleHandler( + UIElement source, + UIElement target, + ScaleHandler handler, + TimeSpan duration, + EasingType easingType, + EasingMode easingMode) + { + if (handler is null) + { + return (null, null, Vector2.One); + } + + var (scaleX, scaleY) = handler(source, target); + var scale = new Vector2((float)scaleX, (float)scaleY); + var (sourceFactory, targetFactory) = this.AnimateScaleImp(scale, duration, easingType, easingMode); + return (sourceFactory, targetFactory, scale); + } + private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory) AnimateScaleImp( Vector2 targetScale, TimeSpan duration, From c5822bd3547e8bbb428d35f2fada46523ca3be56 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 30 Sep 2022 18:14:18 +0800 Subject: [PATCH 80/94] update --- .../Helpers/TransitionHelper.Animation.cs | 5 ++ .../Helpers/TransitionHelper.Logic.cs | 40 +++++++++- .../Helpers/TransitionHelper.cs | 79 ++----------------- 3 files changed, 52 insertions(+), 72 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index 1844045588a..02fbdf5f33a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -35,6 +35,8 @@ private interface IKeyFrameAnimationGroupController { float? LastStopProgress { get; } + AnimationDirection? CurrentDirection { get; } + Task StartAsync(CancellationToken token, TimeSpan? duration); Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, TimeSpan? duration); @@ -279,6 +281,8 @@ private sealed class KeyFrameAnimationGroupController : IKeyFrameAnimationGroupC public float? LastStopProgress { get; private set; } = null; + public AnimationDirection? CurrentDirection { get; private set; } = null; + private bool _lastInverseEasingFunction = false; private bool _lastStartInNormalDirection = true; @@ -357,6 +361,7 @@ private Task AnimateAsync(bool reversed, bool useReversedKeyframes, Cancellation List<(CompositionObject Target, string Path)> compositionAnimations = null; DateTime? animationStartTime = null; this.LastStopProgress = null; + this.CurrentDirection = reversed ? AnimationDirection.Reverse : AnimationDirection.Normal; this._lastInverseEasingFunction = inverseEasingFunction; if (this.animationFactories.Count > 0) { diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 183777bd9be..dc626dc0277 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Windows.Foundation; +using Windows.UI.Composition; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Animation; @@ -83,7 +84,44 @@ private async Task InitControlStateAsync(FrameworkElement target, bool needUpdat } } - private Task AnimateControls(TimeSpan duration, bool reversed, CancellationToken token) + private async Task AnimateControlsAsync(bool reversed, CancellationToken token, bool forceUpdateAnimatedElements) + { + IsNotNullAndIsInVisualTree(this.Source, nameof(this.Source)); + IsNotNullAndIsInVisualTree(this.Target, nameof(this.Target)); + if (IsAnimating) + { + if ((_currentAnimationGroupController?.CurrentDirection is AnimationDirection.Reverse) == reversed) + { + return; + } + else + { + this.Stop(); + } + } + else if (this.IsTargetState == !reversed) + { + return; + } + else + { + this._currentAnimationGroupController = null; + await this.InitControlsStateAsync(forceUpdateAnimatedElements); + } + + this._animationCancellationTokenSource = new CancellationTokenSource(); + var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, this._animationCancellationTokenSource.Token); + await this.AnimateControlsImpAsync(reversed ? this.ReverseDuration : this.Duration, reversed, linkedTokenSource.Token); + if (linkedTokenSource.Token.IsCancellationRequested) + { + return; + } + + this._currentAnimationGroupController = null; + this.RestoreState(!reversed); + } + + private Task AnimateControlsImpAsync(TimeSpan duration, bool reversed, CancellationToken token) { var sourceUnpairedElements = this.SourceAnimatedElements.ConnectedElements .Where(item => !this.TargetAnimatedElements.ConnectedElements.ContainsKey(item.Key)) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 1c0af30cacd..8f3f4af36d4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -31,8 +31,7 @@ public IEnumerable All() private const double AlmostZero = 0.01; private AnimatedElements _sourceAnimatedElements; private AnimatedElements _targetAnimatedElements; - private CancellationTokenSource _animateCancellationTokenSource; - private CancellationTokenSource _reverseCancellationTokenSource; + private CancellationTokenSource _animationCancellationTokenSource; private bool _needUpdateSourceLayout; private bool _needUpdateTargetLayout; @@ -45,7 +44,7 @@ public IEnumerable All() /// /// Gets a value indicating whether the source and target controls are animating. /// - public bool IsAnimating => _animateCancellationTokenSource is not null || _reverseCancellationTokenSource is not null; + public bool IsAnimating => _animationCancellationTokenSource is not null && this._currentAnimationGroupController is not null; /// /// Morphs from source control to target control. @@ -72,39 +71,9 @@ public Task StartAsync(bool forceUpdateAnimatedElements) /// The cancellation token to stop animations while they're running. /// Indicates whether to force the update of the child element list before the animation starts. /// A that completes when all animations have completed. - public async Task StartAsync(CancellationToken token, bool forceUpdateAnimatedElements) + public Task StartAsync(CancellationToken token, bool forceUpdateAnimatedElements) { - IsNotNullAndIsInVisualTree(this.Source, nameof(this.Source)); - IsNotNullAndIsInVisualTree(this.Target, nameof(this.Target)); - if (this._animateCancellationTokenSource is not null) - { - return; - } - - if (this._reverseCancellationTokenSource is not null) - { - this._reverseCancellationTokenSource.Cancel(); - } - else if (this.IsTargetState) - { - return; - } - else - { - this._currentAnimationGroupController = null; - await this.InitControlsStateAsync(forceUpdateAnimatedElements); - } - - this._animateCancellationTokenSource = new CancellationTokenSource(); - var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, this._animateCancellationTokenSource.Token); - await this.AnimateControls(this.Duration, false, linkedTokenSource.Token); - this._animateCancellationTokenSource = null; - if (linkedTokenSource.Token.IsCancellationRequested) - { - return; - } - - this.RestoreState(true); + return this.AnimateControlsAsync(false, token, forceUpdateAnimatedElements); } /// @@ -132,39 +101,9 @@ public Task ReverseAsync(bool forceUpdateAnimatedElements) /// The cancellation token to stop animations while they're running. /// Indicates whether to force the update of child elements before the animation starts. /// A that completes when all animations have completed. - public async Task ReverseAsync(CancellationToken token, bool forceUpdateAnimatedElements) + public Task ReverseAsync(CancellationToken token, bool forceUpdateAnimatedElements) { - IsNotNullAndIsInVisualTree(this.Source, nameof(this.Source)); - IsNotNullAndIsInVisualTree(this.Target, nameof(this.Target)); - if (this._reverseCancellationTokenSource is not null) - { - return; - } - - if (this._animateCancellationTokenSource is not null) - { - this._animateCancellationTokenSource.Cancel(); - } - else if (this.IsTargetState is false) - { - return; - } - else - { - this._currentAnimationGroupController = null; - await this.InitControlsStateAsync(forceUpdateAnimatedElements); - } - - this._reverseCancellationTokenSource = new CancellationTokenSource(); - var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, this._reverseCancellationTokenSource.Token); - await this.AnimateControls(this.ReverseDuration, true, linkedTokenSource.Token); - this._reverseCancellationTokenSource = null; - if (linkedTokenSource.Token.IsCancellationRequested) - { - return; - } - - this.RestoreState(false); + return this.AnimateControlsAsync(true, token, forceUpdateAnimatedElements); } /// @@ -177,10 +116,8 @@ public void Stop() return; } - this._animateCancellationTokenSource?.Cancel(); - this._animateCancellationTokenSource = null; - this._reverseCancellationTokenSource?.Cancel(); - this._reverseCancellationTokenSource = null; + this._animationCancellationTokenSource?.Cancel(); + this._animationCancellationTokenSource = null; } /// From c84b10ce7b6f4dea6f4fcb558de0afee224045f4 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 30 Sep 2022 18:15:02 +0800 Subject: [PATCH 81/94] update --- .../Helpers/TransitionHelper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index 8f3f4af36d4..de295242127 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -32,6 +32,7 @@ public IEnumerable All() private AnimatedElements _sourceAnimatedElements; private AnimatedElements _targetAnimatedElements; private CancellationTokenSource _animationCancellationTokenSource; + private IKeyFrameAnimationGroupController _currentAnimationGroupController; private bool _needUpdateSourceLayout; private bool _needUpdateTargetLayout; @@ -39,8 +40,6 @@ public IEnumerable All() private AnimatedElements TargetAnimatedElements => _targetAnimatedElements ??= GetAnimatedElements(this.Target); - private IKeyFrameAnimationGroupController _currentAnimationGroupController; - /// /// Gets a value indicating whether the source and target controls are animating. /// From 1cdf56e549a6618f138deec6b6865045555cbad8 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Fri, 30 Sep 2022 18:21:47 +0800 Subject: [PATCH 82/94] update --- .../Helpers/TransitionHelper.Helpers.cs | 5 ++++- .../Helpers/TransitionHelper.Logic.cs | 10 +++++----- .../Helpers/TransitionHelper.cs | 14 +++++++------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index c07fe15b91e..ad6368a3dee 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -40,7 +40,10 @@ public int GetHashCode(DependencyObject obj) private static AnimatedElements GetAnimatedElements(DependencyObject parent) { - var animatedElements = new AnimatedElements(new(), new(), new()); + var animatedElements = new AnimatedElements( + new Dictionary(), + new Dictionary>(), + new List()); if (parent is null) { return null; diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index dc626dc0277..7d3794ac3dd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -90,7 +90,7 @@ private async Task AnimateControlsAsync(bool reversed, CancellationToken token, IsNotNullAndIsInVisualTree(this.Target, nameof(this.Target)); if (IsAnimating) { - if ((_currentAnimationGroupController?.CurrentDirection is AnimationDirection.Reverse) == reversed) + if ((_currentAnimationGroupController.CurrentDirection is AnimationDirection.Reverse) == reversed) { return; } @@ -109,8 +109,8 @@ private async Task AnimateControlsAsync(bool reversed, CancellationToken token, await this.InitControlsStateAsync(forceUpdateAnimatedElements); } - this._animationCancellationTokenSource = new CancellationTokenSource(); - var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, this._animationCancellationTokenSource.Token); + this._currentAnimationCancellationTokenSource = new CancellationTokenSource(); + var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, this._currentAnimationCancellationTokenSource.Token); await this.AnimateControlsImpAsync(reversed ? this.ReverseDuration : this.Duration, reversed, linkedTokenSource.Token); if (linkedTokenSource.Token.IsCancellationRequested) { @@ -208,8 +208,8 @@ private void BuildConnectedAnimationController( IKeyFrameAnimationGroupController controller, UIElement source, UIElement target, - List sourceAttachedElements, - List targetAttachedElements, + IList sourceAttachedElements, + IList targetAttachedElements, TimeSpan duration, TransitionConfig config) { diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index de295242127..f0f3c5b393d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -18,9 +18,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations public sealed partial class TransitionHelper { private sealed record AnimatedElements( - Dictionary ConnectedElements, - Dictionary> CoordinatedElements, - List IndependentElements) + IDictionary ConnectedElements, + IDictionary> CoordinatedElements, + IList IndependentElements) { public IEnumerable All() { @@ -31,7 +31,7 @@ public IEnumerable All() private const double AlmostZero = 0.01; private AnimatedElements _sourceAnimatedElements; private AnimatedElements _targetAnimatedElements; - private CancellationTokenSource _animationCancellationTokenSource; + private CancellationTokenSource _currentAnimationCancellationTokenSource; private IKeyFrameAnimationGroupController _currentAnimationGroupController; private bool _needUpdateSourceLayout; private bool _needUpdateTargetLayout; @@ -43,7 +43,7 @@ public IEnumerable All() /// /// Gets a value indicating whether the source and target controls are animating. /// - public bool IsAnimating => _animationCancellationTokenSource is not null && this._currentAnimationGroupController is not null; + public bool IsAnimating => _currentAnimationCancellationTokenSource is not null && this._currentAnimationGroupController is not null; /// /// Morphs from source control to target control. @@ -115,8 +115,8 @@ public void Stop() return; } - this._animationCancellationTokenSource?.Cancel(); - this._animationCancellationTokenSource = null; + this._currentAnimationCancellationTokenSource?.Cancel(); + this._currentAnimationCancellationTokenSource = null; } /// From 9e50006470bb619e119d5090559bfcc2c1932a6c Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Sat, 8 Oct 2022 20:40:32 +0800 Subject: [PATCH 83/94] update clip logic --- .../Helpers/TransitionHelper.Helpers.cs | 53 +++++++------------ .../Helpers/TransitionHelper.Logic.cs | 37 +++++-------- 2 files changed, 33 insertions(+), 57 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index ad6368a3dee..1fcf6cfda9b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -215,50 +215,35 @@ void OnTargetLayoutUpdated(object sender, object e) private static Vector2 GetInverseScale(Vector2 scale) => new(1 / scale.X, 1 / scale.Y); - private static Thickness GetFixedThickness(Thickness thickness, double defaultValue = -4 /* -4 is used to prevent shadows from being cropped.*/) + private static Thickness GetFixedThickness(double left, double top, double right, double bottom, double defaultValue = -4 /* -4 is used to prevent shadows from being cropped.*/) { - var left = thickness.Left < AlmostZero ? defaultValue : thickness.Left; - var top = thickness.Top < AlmostZero ? defaultValue : thickness.Top; - var right = thickness.Right < AlmostZero ? defaultValue : thickness.Right; - var bottom = thickness.Bottom < AlmostZero ? defaultValue : thickness.Bottom; - return new Thickness(left, top, right, bottom); + var fixedLeft = left < AlmostZero ? defaultValue : left; + var fixedTop = top < AlmostZero ? defaultValue : top; + var fixedRight = right < AlmostZero ? defaultValue : right; + var fixedBottom = bottom < AlmostZero ? defaultValue : bottom; + return new Thickness(fixedLeft, fixedTop, fixedRight, fixedBottom); } - private static Thickness? GetElementClip(Vector2 scale, Point targetLocation, Size targetSize, Rect targetParentBounds) + private static Rect GetTransformedBounds(Vector2 initialLocation, Vector2 initialSize, Vector2 centerPoint, Vector2 targetScale) { - var inverseScale = GetInverseScale(scale); - var targetBounds = new Rect(targetLocation, targetSize); - if (targetParentBounds.Contains(targetLocation) && targetParentBounds.Contains(new Point(targetBounds.Right, targetBounds.Bottom))) - { - return null; - } - - return GetFixedThickness( - new Thickness( - (targetParentBounds.X - targetBounds.X) * inverseScale.X, - (targetParentBounds.Y - targetBounds.Y) * inverseScale.Y, - (targetBounds.Right - targetParentBounds.Right) * inverseScale.X, - (targetBounds.Bottom - targetParentBounds.Bottom) * inverseScale.X)); + var targetMatrix3x2 = Matrix3x2.CreateScale(targetScale, centerPoint); + return new Rect((initialLocation + Vector2.Transform(default, targetMatrix3x2)).ToPoint(), (initialSize * targetScale).ToSize()); } - private static Thickness? GetConnectedElementClip(Axis? axis, Vector2 scale, Vector2 actualSize, Vector2 centerPoint, Rect targetParentBounds) + private static Thickness? GetTargetClip(Vector2 initialLocation, Vector2 initialSize, Vector2 centerPoint, Vector2 targetScale, Vector2 translation, Rect targetParentBounds) { - var targetLocation = -centerPoint * scale; - var targetSize = (actualSize * scale).ToSize(); - if (axis is Axis.X) + var transformedBounds = GetTransformedBounds(initialLocation + translation, initialSize, centerPoint, targetScale); + var inverseScale = GetInverseScale(targetScale); + if (targetParentBounds.Contains(new Point(transformedBounds.Left, transformedBounds.Top)) && targetParentBounds.Contains(new Point(transformedBounds.Right, transformedBounds.Bottom))) { - var minY = Math.Min(targetParentBounds.Y, targetLocation.Y); - targetParentBounds.Height = Math.Max(targetParentBounds.Bottom, targetLocation.Y + targetSize.Height) - minY; - targetParentBounds.Y = minY; - } - else if (axis is Axis.Y) - { - var minX = Math.Min(targetParentBounds.X, targetLocation.X); - targetParentBounds.Width = Math.Max(targetParentBounds.Right, targetLocation.X + targetSize.Width) - minX; - targetParentBounds.X = minX; + return null; } - return GetElementClip(scale, targetLocation.ToPoint(), targetSize, targetParentBounds); + return GetFixedThickness( + (targetParentBounds.X - transformedBounds.X) * inverseScale.X, + (targetParentBounds.Y - transformedBounds.Y) * inverseScale.Y, + (transformedBounds.Right - targetParentBounds.Right) * inverseScale.X, + (transformedBounds.Bottom - targetParentBounds.Bottom) * inverseScale.X); } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 7d3794ac3dd..372ae723825 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -286,7 +286,8 @@ private void BuildConnectedAnimationController( foreach (var coordinatedElement in sourceAttachedElements) { var coordinatedElementActualSize = coordinatedElement is FrameworkElement coordinatedFrameworkElement ? new Vector2((float)coordinatedFrameworkElement.ActualWidth, (float)coordinatedFrameworkElement.ActualHeight) : coordinatedElement.ActualSize; - coordinatedElement.GetVisual().CenterPoint = new Vector3(coordinatedElementActualSize * config.NormalizedCenterPoint.ToVector2(), 0); + var coordinatedElementCenterPoint = coordinatedElementActualSize * config.NormalizedCenterPoint.ToVector2(); + coordinatedElement.GetVisual().CenterPoint = new Vector3(coordinatedElementCenterPoint, 0); controller.AddAnimationGroupFor( coordinatedElement, new[] @@ -295,8 +296,8 @@ private void BuildConnectedAnimationController( sourceOpacityAnimation, sourceScaleAnimation }); - var location = coordinatedElement.TransformToVisual(this.Source).TransformPoint(sourceTargetTranslation.ToPoint()); - var targetClip = GetElementClip(targetScale, location, (coordinatedElementActualSize * targetScale).ToSize(), targetTransformedBounds); + var initialLocation = coordinatedElement.TransformToVisual(this.Source).TransformPoint(default).ToVector2(); + var targetClip = GetTargetClip(initialLocation, coordinatedElementActualSize, coordinatedElementCenterPoint, targetScale, sourceTargetTranslation, targetTransformedBounds); if (targetClip.HasValue) { controller.AddAnimationGroupFor( @@ -318,7 +319,8 @@ private void BuildConnectedAnimationController( foreach (var coordinatedElement in targetAttachedElements) { var coordinatedElementActualSize = coordinatedElement is FrameworkElement coordinatedFrameworkElement ? new Vector2((float)coordinatedFrameworkElement.ActualWidth, (float)coordinatedFrameworkElement.ActualHeight) : coordinatedElement.ActualSize; - coordinatedElement.GetVisual().CenterPoint = new Vector3(coordinatedElementActualSize * config.NormalizedCenterPoint.ToVector2(), 0); + var coordinatedElementCenterPoint = coordinatedElementActualSize * config.NormalizedCenterPoint.ToVector2(); + coordinatedElement.GetVisual().CenterPoint = new Vector3(coordinatedElementCenterPoint, 0); controller.AddAnimationGroupFor( coordinatedElement, new[] @@ -327,8 +329,8 @@ private void BuildConnectedAnimationController( targetOpacityAnimation, targetScaleAnimation }); - var location = coordinatedElement.TransformToVisual(this.Target).TransformPoint((-sourceTargetTranslation).ToPoint()); - var targetClip = GetElementClip(targetScale, location, (coordinatedElementActualSize * targetScale).ToSize(), sourceTransformedBounds); + var initialLocation = coordinatedElement.TransformToVisual(this.Target).TransformPoint(default).ToVector2(); + var targetClip = GetTargetClip(initialLocation, coordinatedElementActualSize, coordinatedElementCenterPoint, targetScale, -sourceTargetTranslation, sourceTransformedBounds); if (targetClip.HasValue) { controller.AddAnimationGroupFor( @@ -345,15 +347,6 @@ private void BuildConnectedAnimationController( if (config.EnableClipAnimation) { - Axis? axis = config.ScaleMode switch - { - ScaleMode.None => null, - ScaleMode.Scale => null, - ScaleMode.ScaleX => Axis.Y, - ScaleMode.ScaleY => Axis.X, - ScaleMode.Custom => null, - _ => null, - }; var (sourceClipAnimationGroup, targetClipAnimationGroup) = this.AnimateClip( sourceActualSize, targetActualSize, @@ -362,8 +355,7 @@ private void BuildConnectedAnimationController( sourceTargetScale, duration, easingType, - easingMode, - axis); + easingMode); if (sourceClipAnimationGroup is not null) { controller.AddAnimationGroupFor(source, sourceClipAnimationGroup); @@ -561,23 +553,22 @@ private Task AnimateIndependentElements( Vector2 sourceTargetScale, TimeSpan duration, EasingType easingType, - EasingMode easingMode, - Axis? axis) + EasingMode easingMode) { - var sourceToClip = GetConnectedElementClip(axis, sourceTargetScale, sourceActualSize, sourceCenterPoint, new Rect((-targetCenterPoint).ToPoint(), targetActualSize.ToSize())); - var targetFromClip = GetConnectedElementClip(axis, GetInverseScale(sourceTargetScale), targetActualSize, targetCenterPoint, new Rect((-sourceCenterPoint).ToPoint(), sourceActualSize.ToSize())); + var sourceToClip = GetTargetClip(-sourceCenterPoint, sourceActualSize, sourceCenterPoint, sourceTargetScale, default, new Rect((-targetCenterPoint).ToPoint(), targetActualSize.ToSize())); + var targetFromClip = GetTargetClip(-targetCenterPoint, targetActualSize, targetCenterPoint, GetInverseScale(sourceTargetScale), default, new Rect((-sourceCenterPoint).ToPoint(), sourceActualSize.ToSize())); return ( sourceToClip.HasValue ? this.Clip( sourceToClip.Value, - GetFixedThickness(default), + default, duration: duration, easingType: easingType, easingMode: easingMode) : null, targetFromClip.HasValue ? this.Clip( - GetFixedThickness(default), + default, targetFromClip.Value, duration: duration, easingType: easingType, From 9e511420ba8e9b81bd2262cdc83a750e40f3e41e Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Sun, 9 Oct 2022 20:10:35 +0800 Subject: [PATCH 84/94] nullable enable & add IEasingFunctionFactory --- .../Enums/ScaleMode.cs | 2 + .../Enums/VisualStateToggleMethod.cs | 2 + .../Helpers/TransitionConfig.cs | 6 +- .../Helpers/TransitionHelper.Animation.cs | 144 +++++++++--------- .../TransitionHelper.AttachedProperty.cs | 2 + .../Helpers/TransitionHelper.Helpers.cs | 21 ++- .../Helpers/TransitionHelper.Logic.cs | 72 +++++---- .../Helpers/TransitionHelper.cs | 10 +- 8 files changed, 140 insertions(+), 119 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs index 1ba33780e7b..ecbedd5fd12 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.Toolkit.Uwp.UI.Animations { /// diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/VisualStateToggleMethod.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/VisualStateToggleMethod.cs index 3eb116ba948..686894d72c1 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/VisualStateToggleMethod.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/VisualStateToggleMethod.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.Toolkit.Uwp.UI.Animations { /// diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs index 46bc18aeec2..e29e90b5fdd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Media.Animation; @@ -24,7 +26,7 @@ public class TransitionConfig /// /// Gets or sets an id to indicate the target UI elements. /// - public string Id { get; set; } + public string? Id { get; set; } /// /// Gets or sets the scale strategy of the transition. @@ -37,7 +39,7 @@ public class TransitionConfig /// Only works when is . /// If this value is not set, the scale strategy will fall back to . /// - public ScaleHandler CustomScaleHandler { get; set; } = null; + public ScaleHandler? CustomScaleHandler { get; set; } = null; /// /// Gets or sets a value indicating whether clip animations are enabled for the target UI elements. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index 02fbdf5f33a..7abdf0f8c95 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Collections.Generic; using System.Diagnostics.Contracts; @@ -26,9 +28,14 @@ public sealed partial class TransitionHelper private const string TranslationXYPropertyName = "Translation.XY"; private const string ScaleXYPropertyName = "Scale.XY"; + private interface IEasingFunctionFactory + { + CompositionEasingFunction? GetEasingFunction(Compositor compositor, bool inverse); + } + private interface IKeyFrameCompositionAnimationFactory { - KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool useReversedKeyframes, bool inverseEasingFunction, out CompositionObject target); + KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool useReversedKeyframes, bool inverseEasingFunction, out CompositionObject? target); } private interface IKeyFrameAnimationGroupController @@ -41,9 +48,33 @@ private interface IKeyFrameAnimationGroupController Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, TimeSpan? duration); - void AddAnimationFor(UIElement target, IKeyFrameCompositionAnimationFactory factory); + void AddAnimationFor(UIElement target, IKeyFrameCompositionAnimationFactory? factory); + + void AddAnimationGroupFor(UIElement target, IKeyFrameCompositionAnimationFactory?[] factories); + } + + private sealed record EasingFunctionFactory( + EasingType Type = EasingType.Default, + EasingMode Mode = EasingMode.EaseInOut, + bool InverseCurvature = false) + : IEasingFunctionFactory + { + public CompositionEasingFunction? GetEasingFunction(Compositor compositor, bool inverse) + { + if (Type == EasingType.Linear) + { + return compositor.CreateLinearEasingFunction(); + } + + var inversed = InverseCurvature ^ inverse; + if (Type == EasingType.Default && Mode == EasingMode.EaseInOut) + { + return inversed ? compositor.CreateCubicBezierEasingFunction(new(1f, 0.06f), new(0.59f, 0.48f)) : null; + } - void AddAnimationGroupFor(UIElement target, IKeyFrameCompositionAnimationFactory[] factories); + var (a, b) = AnimationExtensions.EasingMaps[(Type, Mode)]; + return inversed ? compositor.CreateCubicBezierEasingFunction(new(1 - b.X, 1 - b.Y), new(1 - a.X, 1 - a.Y)) : compositor.CreateCubicBezierEasingFunction(a, b); + } } private sealed record KeyFrameAnimationFactory( @@ -52,14 +83,13 @@ private sealed record KeyFrameAnimationFactory( T? From, TimeSpan? Delay, TimeSpan? Duration, - EasingType? EasingType, - EasingMode? EasingMode, - Dictionary NormalizedKeyFrames, - Dictionary ReversedNormalizedKeyFrames) + IEasingFunctionFactory? EasingFunctionFactory, + Dictionary? NormalizedKeyFrames, + Dictionary? ReversedNormalizedKeyFrames) : IKeyFrameCompositionAnimationFactory where T : unmanaged { - public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool useReversedKeyframes, bool inverseEasingFunction, out CompositionObject target) + public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool useReversedKeyframes, bool inverseEasingFunction, out CompositionObject? target) { target = null; @@ -74,14 +104,14 @@ public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reverse CastToNullable(From), Delay, Duration, - GetEasingFunction(targetHint.Compositor, inverseEasingFunction, EasingType, EasingMode), + EasingFunctionFactory?.GetEasingFunction(targetHint.Compositor, inverseEasingFunction), direction: direction); if (keyFrames?.Count > 0) { foreach (var item in keyFrames) { - var (value, easingType, easingMode) = item.Value; - scalarAnimation.InsertKeyFrame(item.Key, CastTo(value), GetEasingFunction(targetHint.Compositor, inverseEasingFunction, easingType, easingMode)); + var (value, easingFunctionFactory) = item.Value; + scalarAnimation.InsertKeyFrame(item.Key, CastTo(value), easingFunctionFactory?.GetEasingFunction(targetHint.Compositor, inverseEasingFunction)); } } @@ -96,14 +126,14 @@ public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reverse CastToNullable(From), Delay, Duration, - GetEasingFunction(targetHint.Compositor, inverseEasingFunction, EasingType, EasingMode), + EasingFunctionFactory?.GetEasingFunction(targetHint.Compositor, inverseEasingFunction), direction: direction); if (keyFrames?.Count > 0) { foreach (var item in keyFrames) { - var (value, easingType, easingMode) = item.Value; - vector2Animation.InsertKeyFrame(item.Key, CastTo(value), GetEasingFunction(targetHint.Compositor, inverseEasingFunction, easingType, easingMode)); + var (value, easingFunctionFactory) = item.Value; + vector2Animation.InsertKeyFrame(item.Key, CastTo(value), easingFunctionFactory?.GetEasingFunction(targetHint.Compositor, inverseEasingFunction)); } } @@ -143,8 +173,7 @@ private sealed record ClipScalarAnimationFactory( float? From, TimeSpan? Delay, TimeSpan? Duration, - EasingType? EasingType, - EasingMode? EasingMode) + IEasingFunctionFactory? EasingFunctionFactory) : IKeyFrameCompositionAnimationFactory { public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reversed, bool useReversedKeyframes, bool inverseEasingFunction, out CompositionObject target) @@ -152,7 +181,7 @@ public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reverse var direction = reversed ? AnimationDirection.Reverse : AnimationDirection.Normal; var visual = (Visual)targetHint; var clip = visual.Clip as InsetClip ?? (InsetClip)(visual.Clip = visual.Compositor.CreateInsetClip()); - var easingFunction = GetEasingFunction(clip.Compositor, inverseEasingFunction, EasingType, EasingMode); + var easingFunction = EasingFunctionFactory?.GetEasingFunction(clip.Compositor, inverseEasingFunction); var animation = clip.Compositor.CreateScalarKeyFrameAnimation( Property, To, @@ -168,36 +197,12 @@ public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reverse } } - private static CompositionEasingFunction GetEasingFunction(Compositor compositor, bool inverse, EasingType? easingType, EasingMode? easingMode) - { - CompositionEasingFunction easingFunction = null; - if (easingType.HasValue && easingMode.HasValue) - { - if (easingType == EasingType.Default && easingMode == EasingMode.EaseInOut) - { - return inverse ? compositor.CreateCubicBezierEasingFunction(new(1f, 0.06f), new(0.59f, 0.48f)) : null; - } - - if (easingType == EasingType.Linear) - { - return compositor.CreateLinearEasingFunction(); - } - - var (a, b) = AnimationExtensions.EasingMaps[(easingType.Value, easingMode.Value)]; - - return inverse ? compositor.CreateCubicBezierEasingFunction(new(1 - b.X, 1 - b.Y), new(1 - a.X, 1 - a.Y)) : compositor.CreateCubicBezierEasingFunction(a, b); - } - - return easingFunction; - } - private IKeyFrameCompositionAnimationFactory[] Clip( Thickness to, + IEasingFunctionFactory? EasingFunctionFactory, Thickness? from = null, TimeSpan? delay = null, - TimeSpan? duration = null, - EasingType easingType = EasingType.Default, - EasingMode easingMode = EasingMode.EaseInOut) + TimeSpan? duration = null) { return new[] { @@ -207,72 +212,65 @@ private IKeyFrameCompositionAnimationFactory[] Clip( (float?)from?.Left, delay, duration, - easingType, - easingMode), + EasingFunctionFactory), new ClipScalarAnimationFactory( nameof(InsetClip.TopInset), (float)to.Top, (float?)from?.Top, delay, duration, - easingType, - easingMode), + EasingFunctionFactory), new ClipScalarAnimationFactory( nameof(InsetClip.RightInset), (float)to.Right, (float?)from?.Right, delay, duration, - easingType, - easingMode), + EasingFunctionFactory), new ClipScalarAnimationFactory( nameof(InsetClip.BottomInset), (float)to.Bottom, (float?)from?.Bottom, delay, duration, - easingType, - easingMode) + EasingFunctionFactory) }; } private IKeyFrameCompositionAnimationFactory Translation( Vector2 to, + IEasingFunctionFactory? EasingFunctionFactory, Vector2? from = null, TimeSpan? delay = null, TimeSpan? duration = null, - EasingType? easingType = null, - EasingMode? easingMode = null, - Dictionary normalizedKeyFrames = null, - Dictionary reversedNormalizedKeyFrames = null) + Dictionary? normalizedKeyFrames = null, + Dictionary? reversedNormalizedKeyFrames = null) { - return new KeyFrameAnimationFactory(TranslationXYPropertyName, to, from, delay, duration, easingType, easingMode, normalizedKeyFrames, reversedNormalizedKeyFrames); + return new KeyFrameAnimationFactory(TranslationXYPropertyName, to, from, delay, duration, EasingFunctionFactory, normalizedKeyFrames, reversedNormalizedKeyFrames); } private IKeyFrameCompositionAnimationFactory Opacity( double to, + IEasingFunctionFactory? EasingFunctionFactory, double? from = null, TimeSpan? delay = null, TimeSpan? duration = null, - EasingType? easingType = null, - EasingMode? easingMode = null, - Dictionary normalizedKeyFrames = null, - Dictionary reversedNormalizedKeyFrames = null) + Dictionary? normalizedKeyFrames = null, + Dictionary? reversedNormalizedKeyFrames = null) { - return new KeyFrameAnimationFactory(nameof(Visual.Opacity), (float)to, (float?)from, delay, duration, easingType, easingMode, normalizedKeyFrames, reversedNormalizedKeyFrames); + return new KeyFrameAnimationFactory(nameof(Visual.Opacity), (float)to, (float?)from, delay, duration, EasingFunctionFactory, normalizedKeyFrames, reversedNormalizedKeyFrames); } private IKeyFrameCompositionAnimationFactory Scale( Vector2 to, + IEasingFunctionFactory? EasingFunctionFactory, Vector2? from = null, TimeSpan? delay = null, TimeSpan? duration = null, - EasingType? easingType = null, - EasingMode? easingMode = null, - Dictionary normalizedKeyFrames = null, - Dictionary reversedNormalizedKeyFrames = null) + Dictionary? normalizedKeyFrames = null, + Dictionary? reversedNormalizedKeyFrames = null) { - return new KeyFrameAnimationFactory(ScaleXYPropertyName, to, from, delay, duration, easingType, easingMode, normalizedKeyFrames, reversedNormalizedKeyFrames); + return new KeyFrameAnimationFactory(ScaleXYPropertyName, to, from, delay, duration, EasingFunctionFactory, normalizedKeyFrames, reversedNormalizedKeyFrames); } private sealed class KeyFrameAnimationGroupController : IKeyFrameAnimationGroupController @@ -287,7 +285,7 @@ private sealed class KeyFrameAnimationGroupController : IKeyFrameAnimationGroupC private bool _lastStartInNormalDirection = true; - public void AddAnimationFor(UIElement target, IKeyFrameCompositionAnimationFactory factory) + public void AddAnimationFor(UIElement target, IKeyFrameCompositionAnimationFactory? factory) { if (factory is null) { @@ -304,7 +302,7 @@ public void AddAnimationFor(UIElement target, IKeyFrameCompositionAnimationFacto } } - public void AddAnimationGroupFor(UIElement target, IKeyFrameCompositionAnimationFactory[] factories) + public void AddAnimationGroupFor(UIElement target, IKeyFrameCompositionAnimationFactory?[] factories) { var validFactories = factories.Where(factory => factory is not null); if (validFactories.Any() is false) @@ -314,11 +312,11 @@ public void AddAnimationGroupFor(UIElement target, IKeyFrameCompositionAnimation if (animationFactories.ContainsKey(target)) { - animationFactories[target].AddRange(validFactories); + animationFactories[target].AddRange(validFactories!); } else { - animationFactories.Add(target, new List(validFactories)); + animationFactories.Add(target, new List(validFactories!)); } } @@ -357,8 +355,8 @@ public Task ReverseAsync(CancellationToken token, bool inverseEasingFunction, Ti private Task AnimateAsync(bool reversed, bool useReversedKeyframes, CancellationToken token, bool inverseEasingFunction, TimeSpan? duration, float? startProgress) { - List tasks = null; - List<(CompositionObject Target, string Path)> compositionAnimations = null; + List? tasks = null; + List<(CompositionObject Target, string Path)>? compositionAnimations = null; DateTime? animationStartTime = null; this.LastStopProgress = null; this.CurrentDirection = reversed ? AnimationDirection.Reverse : AnimationDirection.Normal; @@ -414,7 +412,7 @@ private Task StartForAsync(UIElement element, bool reversed, bool useReversedKey ElementCompositionPreview.SetIsTranslationEnabled(element, true); var visual = element.GetVisual(); var batch = visual.Compositor.CreateScopedBatch(CompositionBatchTypes.Animation); - var taskCompletionSource = new TaskCompletionSource(); + var taskCompletionSource = new TaskCompletionSource(); batch.Completed += (_, _) => taskCompletionSource.SetResult(null); foreach (var factory in factories) { diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs index 0f9a115cc94..14fb4abf04f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.AttachedProperty.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Windows.Foundation; using Windows.UI.Xaml; diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index 1fcf6cfda9b..3ab25144e0e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Collections.Generic; using System.Linq; @@ -12,6 +14,7 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Hosting; using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Media.Animation; namespace Microsoft.Toolkit.Uwp.UI.Animations { @@ -46,7 +49,7 @@ private static AnimatedElements GetAnimatedElements(DependencyObject new List()); if (parent is null) { - return null; + return animatedElements; } var allAnimatedElements = FindDescendantsWithBFSAndPruneAndPredicate(parent, IsNotVisible, IsAnimatedElement) @@ -201,7 +204,7 @@ private static void IsNotNullAndIsInVisualTree(FrameworkElement target, string n private static Task UpdateControlLayout(FrameworkElement target) { - var updateTargetLayoutTaskSource = new TaskCompletionSource(); + var updateTargetLayoutTaskSource = new TaskCompletionSource(); void OnTargetLayoutUpdated(object sender, object e) { target.LayoutUpdated -= OnTargetLayoutUpdated; @@ -245,5 +248,19 @@ private static Rect GetTransformedBounds(Vector2 initialLocation, Vector2 initia (transformedBounds.Right - targetParentBounds.Right) * inverseScale.X, (transformedBounds.Bottom - targetParentBounds.Bottom) * inverseScale.X); } + + private static readonly Dictionary<(EasingType, EasingMode, bool), IEasingFunctionFactory> EasingFunctionFactoryCache = new(); + + private static IEasingFunctionFactory GetEasingFunctionFactory(EasingType type = EasingType.Default, EasingMode mode = EasingMode.EaseInOut, bool inverse = false) + { + if (EasingFunctionFactoryCache.TryGetValue((type, mode, inverse), out var easingFunctionFactory)) + { + return easingFunctionFactory; + } + + var factory = new EasingFunctionFactory(type, mode, inverse); + EasingFunctionFactoryCache[(type, mode, inverse)] = factory; + return factory; + } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 372ae723825..c5a7732f3bd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Collections.Generic; using System.Linq; @@ -90,7 +92,7 @@ private async Task AnimateControlsAsync(bool reversed, CancellationToken token, IsNotNullAndIsInVisualTree(this.Target, nameof(this.Target)); if (IsAnimating) { - if ((_currentAnimationGroupController.CurrentDirection is AnimationDirection.Reverse) == reversed) + if ((_currentAnimationGroupController?.CurrentDirection is AnimationDirection.Reverse) == reversed) { return; } @@ -304,9 +306,8 @@ private void BuildConnectedAnimationController( coordinatedElement, this.Clip( targetClip.Value, - duration: duration, - easingType: easingType, - easingMode: easingMode)); + GetEasingFunctionFactory(easingType, easingMode), + duration: duration)); } } } @@ -337,10 +338,9 @@ private void BuildConnectedAnimationController( coordinatedElement, this.Clip( default, + GetEasingFunctionFactory(easingType, easingMode), from: targetClip.Value, - duration: duration, - easingType: easingType, - easingMode: easingMode)); + duration: duration)); } } } @@ -410,20 +410,18 @@ private Task AnimateIndependentElements( { controller.AddAnimationFor(item, this.Translation( translationTo, + GetEasingFunctionFactory(easingType, easingMode), startTime.HasValue ? null : translationFrom, useDelay, - duration: duration, - easingType: easingType, - easingMode: easingMode)); + duration: duration)); } controller.AddAnimationFor(item, this.Opacity( opacityTo, + GetEasingFunctionFactory(easingType, easingMode), startTime.HasValue ? null : opacityFrom, useDelay, - duration, - easingType: easingType, - easingMode: easingMode)); + duration)); } if (isShow) @@ -445,8 +443,8 @@ private Task AnimateIndependentElements( EasingMode easingMode) { var translation = target.TransformToVisual(source).TransformPoint(default).ToVector2() - sourceCenterPoint + targetCenterPoint; - return (this.Translation(translation, Vector2.Zero, duration: duration, easingType: easingType, easingMode: easingMode), - this.Translation(Vector2.Zero, -translation, duration: duration, easingType: easingType, easingMode: easingMode), + return (this.Translation(translation, GetEasingFunctionFactory(easingType, easingMode), Vector2.Zero, duration: duration), + this.Translation(Vector2.Zero, GetEasingFunctionFactory(easingType, easingMode), -translation, duration: duration), translation); } @@ -490,10 +488,10 @@ private Task AnimateIndependentElements( return (sourceFactory, targetFactory, scale); } - private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory, Vector2) AnimateScaleWithScaleHandler( + private (IKeyFrameCompositionAnimationFactory?, IKeyFrameCompositionAnimationFactory?, Vector2) AnimateScaleWithScaleHandler( UIElement source, UIElement target, - ScaleHandler handler, + ScaleHandler? handler, TimeSpan duration, EasingType easingType, EasingMode easingMode) @@ -515,37 +513,37 @@ private Task AnimateIndependentElements( EasingType easingType, EasingMode easingMode) { - return (this.Scale(targetScale, Vector2.One, duration: duration, easingType: easingType, easingMode: easingMode), - this.Scale(Vector2.One, GetInverseScale(targetScale), duration: duration, easingType: easingType, easingMode: easingMode)); + return (this.Scale(targetScale, GetEasingFunctionFactory(easingType, easingMode), Vector2.One, duration: duration), + this.Scale(Vector2.One, GetEasingFunctionFactory(easingType, easingMode), GetInverseScale(targetScale), duration: duration)); } private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory) AnimateOpacity(TimeSpan duration, Point opacityTransitionProgressKey) { var normalKey = (float)Math.Max(0, Math.Min(opacityTransitionProgressKey.X, 1)); var reversedKey = (float)Math.Max(0, Math.Min(1 - opacityTransitionProgressKey.Y, 1)); - var sourceNormalizedKeyFrames = new Dictionary + var sourceNormalizedKeyFrames = new Dictionary { - [normalKey] = (0, EasingType.Cubic, EasingMode.EaseIn) + [normalKey] = (0, GetEasingFunctionFactory(EasingType.Cubic, EasingMode.EaseIn)) }; - var reversedSourceNormalizedKeyFrames = new Dictionary + var reversedSourceNormalizedKeyFrames = new Dictionary { - [reversedKey] = (1, null, null), - [1] = (0, EasingType.Cubic, EasingMode.EaseOut) + [reversedKey] = (1, null), + [1] = (0, GetEasingFunctionFactory(EasingType.Cubic, EasingMode.EaseIn, true)) }; - var targetNormalizedKeyFrames = new Dictionary + var targetNormalizedKeyFrames = new Dictionary { - [normalKey] = (1, EasingType.Cubic, EasingMode.EaseOut) + [normalKey] = (1, GetEasingFunctionFactory(EasingType.Cubic, EasingMode.EaseOut)) }; - var reversedTargetNormalizedKeyFrames = new Dictionary + var reversedTargetNormalizedKeyFrames = new Dictionary { - [reversedKey] = (0, null, null), - [1] = (1, EasingType.Cubic, EasingMode.EaseIn) + [reversedKey] = (0, null), + [1] = (1, GetEasingFunctionFactory(EasingType.Cubic, EasingMode.EaseOut, true)) }; - return (this.Opacity(0, 1, duration: duration, normalizedKeyFrames: sourceNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedSourceNormalizedKeyFrames), - this.Opacity(1, 0, duration: duration, normalizedKeyFrames: targetNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedTargetNormalizedKeyFrames)); + return (this.Opacity(0, null, 1, duration: duration, normalizedKeyFrames: sourceNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedSourceNormalizedKeyFrames), + this.Opacity(1, null, 0, duration: duration, normalizedKeyFrames: targetNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedTargetNormalizedKeyFrames)); } - private (IKeyFrameCompositionAnimationFactory[], IKeyFrameCompositionAnimationFactory[]) AnimateClip( + private (IKeyFrameCompositionAnimationFactory[]?, IKeyFrameCompositionAnimationFactory[]?) AnimateClip( Vector2 sourceActualSize, Vector2 targetActualSize, Vector2 sourceCenterPoint, @@ -561,18 +559,16 @@ private Task AnimateIndependentElements( sourceToClip.HasValue ? this.Clip( sourceToClip.Value, + GetEasingFunctionFactory(easingType, easingMode), default, - duration: duration, - easingType: easingType, - easingMode: easingMode) + duration: duration) : null, targetFromClip.HasValue ? this.Clip( default, + GetEasingFunctionFactory(easingType, easingMode), targetFromClip.Value, - duration: duration, - easingType: easingType, - easingMode: easingMode) + duration: duration) : null ); } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index f0f3c5b393d..a5665a0aeb3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.Collections.Generic; using System.Linq; using System.Threading; @@ -29,10 +31,10 @@ public IEnumerable All() } private const double AlmostZero = 0.01; - private AnimatedElements _sourceAnimatedElements; - private AnimatedElements _targetAnimatedElements; - private CancellationTokenSource _currentAnimationCancellationTokenSource; - private IKeyFrameAnimationGroupController _currentAnimationGroupController; + private AnimatedElements? _sourceAnimatedElements; + private AnimatedElements? _targetAnimatedElements; + private CancellationTokenSource? _currentAnimationCancellationTokenSource; + private IKeyFrameAnimationGroupController? _currentAnimationGroupController; private bool _needUpdateSourceLayout; private bool _needUpdateTargetLayout; From 688490c91e3fae7e3909948f65c9dd163ba87722 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Sun, 9 Oct 2022 20:19:31 +0800 Subject: [PATCH 85/94] remove some useless code --- .../Helpers/TransitionHelper.Logic.cs | 14 ++++---------- .../Helpers/TransitionHelper.Properties.cs | 10 ---------- .../Helpers/TransitionHelper.cs | 2 -- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index c5a7732f3bd..ea047bf0d8f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -47,11 +47,8 @@ private async Task InitControlsStateAsync(bool forceUpdateAnimatedElements = fal Canvas.SetZIndex(this.IsTargetState ? this.Source : this.Target, maxZIndex); await Task.WhenAll( - this.InitControlStateAsync(this.Source, this._needUpdateSourceLayout), - this.InitControlStateAsync(this.Target, this._needUpdateTargetLayout)); - - this._needUpdateSourceLayout = false; - this._needUpdateTargetLayout = false; + this.InitControlStateAsync(this.Source), + this.InitControlStateAsync(this.Target)); if (forceUpdateAnimatedElements) { @@ -60,7 +57,7 @@ await Task.WhenAll( } } - private async Task InitControlStateAsync(FrameworkElement target, bool needUpdateLayout) + private async Task InitControlStateAsync(FrameworkElement target) { if (target is null) { @@ -71,10 +68,7 @@ private async Task InitControlStateAsync(FrameworkElement target, bool needUpdat if (target.Visibility == Visibility.Collapsed) { target.Visibility = Visibility.Visible; - if (needUpdateLayout) - { - await UpdateControlLayout(target); - } + await UpdateControlLayout(target); } else if (target.Opacity < AlmostZero) { diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index bd2703ca20c..07152fa9564 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -45,11 +45,6 @@ public FrameworkElement Source RestoreElements(this.SourceAnimatedElements.All()); } - if (value is { IsLoaded: false } or { Visibility: Visibility.Collapsed }) - { - this._needUpdateSourceLayout = true; - } - this._currentAnimationGroupController = null; this._source = value; this._sourceZIndex = value is null ? -1 : Canvas.GetZIndex(value); @@ -85,11 +80,6 @@ public FrameworkElement Target RestoreElements(this.TargetAnimatedElements.All()); } - if (value is { IsLoaded: false } or { Visibility: Visibility.Collapsed }) - { - this._needUpdateTargetLayout = true; - } - this._currentAnimationGroupController = null; this._target = value; this._targetZIndex = value is null ? -1 : Canvas.GetZIndex(value); diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index a5665a0aeb3..cb579ed7b4d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -35,8 +35,6 @@ public IEnumerable All() private AnimatedElements? _targetAnimatedElements; private CancellationTokenSource? _currentAnimationCancellationTokenSource; private IKeyFrameAnimationGroupController? _currentAnimationGroupController; - private bool _needUpdateSourceLayout; - private bool _needUpdateTargetLayout; private AnimatedElements SourceAnimatedElements => _sourceAnimatedElements ??= GetAnimatedElements(this.Source); From 0d251670775ef1cf18ddb9830333635c7a61a43e Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Mon, 10 Oct 2022 12:23:07 +0800 Subject: [PATCH 86/94] add sample for CustomScalingCalculator --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 1 + .../CustomTextScalingCalculator.cs | 28 +++++++++++++++++++ .../TransitionHelperPage.xaml | 5 +++- .../TransitionHelperXaml.bind | 20 +++++++++---- .../Helpers/IScalingCalculator.cs | 25 +++++++++++++++++ .../Helpers/TransitionConfig.cs | 12 ++------ .../Helpers/TransitionHelper.Logic.cs | 10 +++---- 7 files changed, 79 insertions(+), 22 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/CustomTextScalingCalculator.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Helpers/IScalingCalculator.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index d6f0fa76a12..0a2a7987c64 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -565,6 +565,7 @@ TokenizingTextBoxPage.xaml + TransitionHelperPage.xaml diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/CustomTextScalingCalculator.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/CustomTextScalingCalculator.cs new file mode 100644 index 00000000000..c6aaf815bc1 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/CustomTextScalingCalculator.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Toolkit.Uwp.UI; +using Microsoft.Toolkit.Uwp.UI.Animations.Helpers; +using Windows.Foundation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + public sealed class CustomTextScalingCalculator : IScalingCalculator + { + public Point GetScaling(UIElement source, UIElement target) + { + var sourceTextElement = source?.FindDescendantOrSelf(); + var targetTextElement = target?.FindDescendantOrSelf(); + if (sourceTextElement is not null && targetTextElement is not null) + { + var scale = targetTextElement.FontSize / sourceTextElement.FontSize; + return new Point(scale, scale); + } + + return new Point(1, 1); + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml index e97fd1fafcb..980990f5998 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperPage.xaml @@ -6,9 +6,12 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:interactions="using:Microsoft.Xaml.Interactions.Core" xmlns:interactivity="using:Microsoft.Xaml.Interactivity" + xmlns:local="using:Microsoft.Toolkit.Uwp.SampleApp.SamplePages" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind index d29d6973db5..5ed8a59087e 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/TransitionHelperXaml.bind @@ -5,6 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:interactions="using:Microsoft.Xaml.Interactions.Core" xmlns:interactivity="using:Microsoft.Xaml.Interactivity" + xmlns:local="using:Microsoft.Toolkit.Uwp.SampleApp.SamplePages" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> @@ -14,9 +15,13 @@ - + ScaleMode="Custom"> + + + + + @@ -146,10 +151,13 @@ - - Magic + FontSize="24" + TextAlignment="Center" + TextWrapping="Wrap" > + Magic is my cat's name diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/IScalingCalculator.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/IScalingCalculator.cs new file mode 100644 index 00000000000..7eb527e19de --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/IScalingCalculator.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using Windows.Foundation; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.Helpers +{ + /// + /// Defines methods to support calculating scaling changes. + /// + public interface IScalingCalculator + { + /// + /// Handler used to calculate the change in the scaling of an element when it is in transition. + /// + /// The source element. + /// The target element. + /// A whose X value represents the horizontal scaling change and whose Y represents the vertical scaling change. + Point GetScaling(UIElement source, UIElement target); + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs index e29e90b5fdd..56bacc108dd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs @@ -4,20 +4,12 @@ #nullable enable +using Microsoft.Toolkit.Uwp.UI.Animations.Helpers; using Windows.Foundation; -using Windows.UI.Xaml; using Windows.UI.Xaml.Media.Animation; namespace Microsoft.Toolkit.Uwp.UI.Animations { - /// - /// Handler used to calculate the change in the scaling of an element when it is in transition. - /// - /// The source element. - /// The target element. - /// A value tuple whose first value represents the horizontal scaling change and whose second value represents the vertical scaling change. - public delegate (double, double) ScaleHandler(UIElement source, UIElement target); - /// /// Configuration used for the transition between UI elements. /// @@ -39,7 +31,7 @@ public class TransitionConfig /// Only works when is . /// If this value is not set, the scale strategy will fall back to . /// - public ScaleHandler? CustomScaleHandler { get; set; } = null; + public IScalingCalculator? CustomScalingCalculator { get; set; } = null; /// /// Gets or sets a value indicating whether clip animations are enabled for the target UI elements. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index ea047bf0d8f..32f6276fa0a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -10,6 +10,7 @@ using System.Numerics; using System.Threading; using System.Threading.Tasks; +using Microsoft.Toolkit.Uwp.UI.Animations.Helpers; using Windows.Foundation; using Windows.UI.Composition; using Windows.UI.Xaml; @@ -251,7 +252,7 @@ private void BuildConnectedAnimationController( ScaleMode.Custom => this.AnimateScaleWithScaleHandler( source, target, - config.CustomScaleHandler, + config.CustomScalingCalculator, duration, easingType, easingMode), @@ -485,18 +486,17 @@ private Task AnimateIndependentElements( private (IKeyFrameCompositionAnimationFactory?, IKeyFrameCompositionAnimationFactory?, Vector2) AnimateScaleWithScaleHandler( UIElement source, UIElement target, - ScaleHandler? handler, + IScalingCalculator? scalingCalculator, TimeSpan duration, EasingType easingType, EasingMode easingMode) { - if (handler is null) + if (scalingCalculator is null) { return (null, null, Vector2.One); } - var (scaleX, scaleY) = handler(source, target); - var scale = new Vector2((float)scaleX, (float)scaleY); + var scale = scalingCalculator.GetScaling(source, target).ToVector2(); var (sourceFactory, targetFactory) = this.AnimateScaleImp(scale, duration, easingType, easingMode); return (sourceFactory, targetFactory, scale); } From 888567af0148ae3b4050d0e483080e9ea13e8cdb Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Mon, 10 Oct 2022 13:41:17 +0800 Subject: [PATCH 87/94] update --- .../Helpers/TransitionHelper.Animation.cs | 5 ++--- .../Helpers/TransitionHelper.Helpers.cs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index 7abdf0f8c95..409d2223914 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -56,7 +56,7 @@ private interface IKeyFrameAnimationGroupController private sealed record EasingFunctionFactory( EasingType Type = EasingType.Default, EasingMode Mode = EasingMode.EaseInOut, - bool InverseCurvature = false) + bool Inverse = false) : IEasingFunctionFactory { public CompositionEasingFunction? GetEasingFunction(Compositor compositor, bool inverse) @@ -66,7 +66,7 @@ private sealed record EasingFunctionFactory( return compositor.CreateLinearEasingFunction(); } - var inversed = InverseCurvature ^ inverse; + var inversed = Inverse ^ inverse; if (Type == EasingType.Default && Mode == EasingMode.EaseInOut) { return inversed ? compositor.CreateCubicBezierEasingFunction(new(1f, 0.06f), new(0.59f, 0.48f)) : null; @@ -192,7 +192,6 @@ public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reverse direction: direction); target = clip; - return animation; } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index 3ab25144e0e..9209419f108 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -218,7 +218,7 @@ void OnTargetLayoutUpdated(object sender, object e) private static Vector2 GetInverseScale(Vector2 scale) => new(1 / scale.X, 1 / scale.Y); - private static Thickness GetFixedThickness(double left, double top, double right, double bottom, double defaultValue = -4 /* -4 is used to prevent shadows from being cropped.*/) + private static Thickness GetFixedThickness(double left, double top, double right, double bottom, double defaultValue = 0) { var fixedLeft = left < AlmostZero ? defaultValue : left; var fixedTop = top < AlmostZero ? defaultValue : top; From 6144f05cae323d37efda0fc71a2bec572101bf9c Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Mon, 10 Oct 2022 15:33:02 +0800 Subject: [PATCH 88/94] update xml doc --- .../SamplePages/TransitionHelper/CustomTextScalingCalculator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/CustomTextScalingCalculator.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/CustomTextScalingCalculator.cs index c6aaf815bc1..2dfc79a5fed 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/CustomTextScalingCalculator.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/CustomTextScalingCalculator.cs @@ -12,6 +12,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages { public sealed class CustomTextScalingCalculator : IScalingCalculator { + /// public Point GetScaling(UIElement source, UIElement target) { var sourceTextElement = source?.FindDescendantOrSelf(); From d90482ea67162cc42c323403ad19bed3a3b3e9bf Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Mon, 10 Oct 2022 15:44:45 +0800 Subject: [PATCH 89/94] update xml doc --- Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs index ecbedd5fd12..b125652b18f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Enums/ScaleMode.cs @@ -34,7 +34,7 @@ public enum ScaleMode ScaleY, /// - /// Apply the scaling changes calculated by using custom scale handler. + /// Apply the scaling changes calculated by using custom scaling calculator. /// Custom, } From ca87ce42bb9cf63a4b6ff1e8b4b27450125d0c23 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Mon, 10 Oct 2022 16:36:01 +0800 Subject: [PATCH 90/94] update xml doc --- .../Helpers/IScalingCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/IScalingCalculator.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/IScalingCalculator.cs index 7eb527e19de..96088deca24 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/IScalingCalculator.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/IScalingCalculator.cs @@ -15,7 +15,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations.Helpers public interface IScalingCalculator { /// - /// Handler used to calculate the change in the scaling of an element when it is in transition. + /// Gets the scaling changes when the source element transitions to the target element. /// /// The source element. /// The target element. From 6c343752a479c927d0edc974ef4c4909266f6cd5 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Tue, 11 Oct 2022 13:50:01 +0800 Subject: [PATCH 91/94] fix format --- .../Helpers/TransitionHelper.Animation.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs index 409d2223914..ebc7b347f90 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Animation.cs @@ -198,7 +198,7 @@ public KeyFrameAnimation GetAnimation(CompositionObject targetHint, bool reverse private IKeyFrameCompositionAnimationFactory[] Clip( Thickness to, - IEasingFunctionFactory? EasingFunctionFactory, + IEasingFunctionFactory? easingFunctionFactory, Thickness? from = null, TimeSpan? delay = null, TimeSpan? duration = null) @@ -211,65 +211,65 @@ private IKeyFrameCompositionAnimationFactory[] Clip( (float?)from?.Left, delay, duration, - EasingFunctionFactory), + easingFunctionFactory), new ClipScalarAnimationFactory( nameof(InsetClip.TopInset), (float)to.Top, (float?)from?.Top, delay, duration, - EasingFunctionFactory), + easingFunctionFactory), new ClipScalarAnimationFactory( nameof(InsetClip.RightInset), (float)to.Right, (float?)from?.Right, delay, duration, - EasingFunctionFactory), + easingFunctionFactory), new ClipScalarAnimationFactory( nameof(InsetClip.BottomInset), (float)to.Bottom, (float?)from?.Bottom, delay, duration, - EasingFunctionFactory) + easingFunctionFactory) }; } private IKeyFrameCompositionAnimationFactory Translation( Vector2 to, - IEasingFunctionFactory? EasingFunctionFactory, + IEasingFunctionFactory? easingFunctionFactory, Vector2? from = null, TimeSpan? delay = null, TimeSpan? duration = null, Dictionary? normalizedKeyFrames = null, Dictionary? reversedNormalizedKeyFrames = null) { - return new KeyFrameAnimationFactory(TranslationXYPropertyName, to, from, delay, duration, EasingFunctionFactory, normalizedKeyFrames, reversedNormalizedKeyFrames); + return new KeyFrameAnimationFactory(TranslationXYPropertyName, to, from, delay, duration, easingFunctionFactory, normalizedKeyFrames, reversedNormalizedKeyFrames); } private IKeyFrameCompositionAnimationFactory Opacity( double to, - IEasingFunctionFactory? EasingFunctionFactory, + IEasingFunctionFactory? easingFunctionFactory, double? from = null, TimeSpan? delay = null, TimeSpan? duration = null, Dictionary? normalizedKeyFrames = null, Dictionary? reversedNormalizedKeyFrames = null) { - return new KeyFrameAnimationFactory(nameof(Visual.Opacity), (float)to, (float?)from, delay, duration, EasingFunctionFactory, normalizedKeyFrames, reversedNormalizedKeyFrames); + return new KeyFrameAnimationFactory(nameof(Visual.Opacity), (float)to, (float?)from, delay, duration, easingFunctionFactory, normalizedKeyFrames, reversedNormalizedKeyFrames); } private IKeyFrameCompositionAnimationFactory Scale( Vector2 to, - IEasingFunctionFactory? EasingFunctionFactory, + IEasingFunctionFactory? easingFunctionFactory, Vector2? from = null, TimeSpan? delay = null, TimeSpan? duration = null, Dictionary? normalizedKeyFrames = null, Dictionary? reversedNormalizedKeyFrames = null) { - return new KeyFrameAnimationFactory(ScaleXYPropertyName, to, from, delay, duration, EasingFunctionFactory, normalizedKeyFrames, reversedNormalizedKeyFrames); + return new KeyFrameAnimationFactory(ScaleXYPropertyName, to, from, delay, duration, easingFunctionFactory, normalizedKeyFrames, reversedNormalizedKeyFrames); } private sealed class KeyFrameAnimationGroupController : IKeyFrameAnimationGroupController From 7245354eb4837899ac89d44196204e425c602223 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Mon, 7 Nov 2022 15:48:47 +0800 Subject: [PATCH 92/94] fix build error --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 6 +++++- .../TransitionHelper/CustomTextScalingCalculator.cs | 8 ++++---- .../Helpers/IScalingCalculator.cs | 6 +++--- .../Helpers/TransitionConfig.cs | 2 +- .../Helpers/TransitionHelper.Logic.cs | 6 +++--- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 0a2a7987c64..9958828b7e1 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -1511,7 +1511,11 @@ Visual C++ 2015 Runtime for Universal Windows Platform Apps - + + + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Numerics.dll + + 14.0 diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/CustomTextScalingCalculator.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/CustomTextScalingCalculator.cs index 2dfc79a5fed..60d23c46da2 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/CustomTextScalingCalculator.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TransitionHelper/CustomTextScalingCalculator.cs @@ -2,9 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Numerics; using Microsoft.Toolkit.Uwp.UI; using Microsoft.Toolkit.Uwp.UI.Animations.Helpers; -using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -13,17 +13,17 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages public sealed class CustomTextScalingCalculator : IScalingCalculator { /// - public Point GetScaling(UIElement source, UIElement target) + public Vector2 GetScaling(UIElement source, UIElement target) { var sourceTextElement = source?.FindDescendantOrSelf(); var targetTextElement = target?.FindDescendantOrSelf(); if (sourceTextElement is not null && targetTextElement is not null) { var scale = targetTextElement.FontSize / sourceTextElement.FontSize; - return new Point(scale, scale); + return new Vector2((float)scale); } - return new Point(1, 1); + return new Vector2(1); } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/IScalingCalculator.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/IScalingCalculator.cs index 96088deca24..9a822d06581 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/IScalingCalculator.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/IScalingCalculator.cs @@ -4,7 +4,7 @@ #nullable enable -using Windows.Foundation; +using System.Numerics; using Windows.UI.Xaml; namespace Microsoft.Toolkit.Uwp.UI.Animations.Helpers @@ -19,7 +19,7 @@ public interface IScalingCalculator /// /// The source element. /// The target element. - /// A whose X value represents the horizontal scaling change and whose Y represents the vertical scaling change. - Point GetScaling(UIElement source, UIElement target); + /// A whose X value represents the horizontal scaling change and whose Y represents the vertical scaling change. + Vector2 GetScaling(UIElement source, UIElement target); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs index 56bacc108dd..421beba3b1b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs @@ -27,7 +27,7 @@ public class TransitionConfig public ScaleMode ScaleMode { get; set; } = ScaleMode.None; /// - /// Gets or sets the custom scale handler. + /// Gets or sets the custom scale calculator. /// Only works when is . /// If this value is not set, the scale strategy will fall back to . /// diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 32f6276fa0a..0d81f6eab33 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -249,7 +249,7 @@ private void BuildConnectedAnimationController( duration, easingType, easingMode), - ScaleMode.Custom => this.AnimateScaleWithScaleHandler( + ScaleMode.Custom => this.AnimateScaleWithScaleCalculator( source, target, config.CustomScalingCalculator, @@ -483,7 +483,7 @@ private Task AnimateIndependentElements( return (sourceFactory, targetFactory, scale); } - private (IKeyFrameCompositionAnimationFactory?, IKeyFrameCompositionAnimationFactory?, Vector2) AnimateScaleWithScaleHandler( + private (IKeyFrameCompositionAnimationFactory?, IKeyFrameCompositionAnimationFactory?, Vector2) AnimateScaleWithScaleCalculator( UIElement source, UIElement target, IScalingCalculator? scalingCalculator, @@ -496,7 +496,7 @@ private Task AnimateIndependentElements( return (null, null, Vector2.One); } - var scale = scalingCalculator.GetScaling(source, target).ToVector2(); + var scale = scalingCalculator.GetScaling(source, target); var (sourceFactory, targetFactory) = this.AnimateScaleImp(scale, duration, easingType, easingMode); return (sourceFactory, targetFactory, scale); } From 7ea2eb81cf64a5739e77b7a04c9d920837b95a3d Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Mon, 7 Nov 2022 18:47:07 +0800 Subject: [PATCH 93/94] update AnimateOpacity logic --- .../Helpers/TransitionConfig.cs | 9 ++++++ .../Helpers/TransitionHelper.Helpers.cs | 4 +++ .../Helpers/TransitionHelper.Logic.cs | 28 +++++++++++-------- .../Helpers/TransitionHelper.Properties.cs | 8 +++--- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs index 421beba3b1b..577decb6dac 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionConfig.cs @@ -57,5 +57,14 @@ public class TransitionConfig /// If this value is not set, it will fall back to the value in . /// public EasingMode? EasingMode { get; set; } = null; + + /// + /// Gets or sets the key point of opacity transition. + /// The time the keyframe of opacity from 0 to 1 or from 1 to 0 should occur at, expressed as a percentage of the animation duration. The allowed values are from (0, 0) to (1, 1). + /// .X will be used in the animation of the normal direction. + /// .Y will be used in the animation of the reverse direction. + /// If this value is not set, it will fall back to the value in . + /// + public Point? OpacityTransitionProgressKey { get; set; } = null; } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs index 9209419f108..7a8791f61c1 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Helpers.cs @@ -262,5 +262,9 @@ private static IEasingFunctionFactory GetEasingFunctionFactory(EasingType type = EasingFunctionFactoryCache[(type, mode, inverse)] = factory; return factory; } + + private static float GetOpacityTransitionStartKey(float normalizedKey, float halfTransitionNormalizedDuration = 0.1f) => Math.Clamp(normalizedKey - halfTransitionNormalizedDuration, 0, 1); + + private static float GetOpacityTransitionEndKey(float normalizedKey, float halfTransitionNormalizedDuration = 0.1f) => Math.Clamp(normalizedKey + halfTransitionNormalizedDuration, 0, 1); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs index 0d81f6eab33..30680995f0e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Logic.cs @@ -227,7 +227,7 @@ private void BuildConnectedAnimationController( duration, easingType, easingMode); - var (sourceOpacityAnimation, targetOpacityAnimation) = this.AnimateOpacity(duration, this.OpacityTransitionProgressKey); + var (sourceOpacityAnimation, targetOpacityAnimation) = this.AnimateOpacity(duration, config.OpacityTransitionProgressKey ?? this.DefaultOpacityTransitionProgressKey); var (sourceScaleAnimation, targetScaleAnimation, sourceTargetScale) = config.ScaleMode switch { ScaleMode.None => (null, null, Vector2.One), @@ -513,26 +513,32 @@ private Task AnimateIndependentElements( private (IKeyFrameCompositionAnimationFactory, IKeyFrameCompositionAnimationFactory) AnimateOpacity(TimeSpan duration, Point opacityTransitionProgressKey) { - var normalKey = (float)Math.Max(0, Math.Min(opacityTransitionProgressKey.X, 1)); - var reversedKey = (float)Math.Max(0, Math.Min(1 - opacityTransitionProgressKey.Y, 1)); + var normalKey = (float)opacityTransitionProgressKey.X; + var normalKeyForTarget = Math.Clamp(normalKey - 0.1f, 0, 1); var sourceNormalizedKeyFrames = new Dictionary { - [normalKey] = (0, GetEasingFunctionFactory(EasingType.Cubic, EasingMode.EaseIn)) + [GetOpacityTransitionStartKey(normalKey)] = (1, null), + [GetOpacityTransitionEndKey(normalKey)] = (0, GetEasingFunctionFactory(EasingType.Cubic, EasingMode.EaseIn)) }; - var reversedSourceNormalizedKeyFrames = new Dictionary + var targetNormalizedKeyFrames = new Dictionary { - [reversedKey] = (1, null), - [1] = (0, GetEasingFunctionFactory(EasingType.Cubic, EasingMode.EaseIn, true)) + [GetOpacityTransitionStartKey(normalKeyForTarget)] = (0, null), + [GetOpacityTransitionEndKey(normalKeyForTarget)] = (1, GetEasingFunctionFactory(EasingType.Cubic, EasingMode.EaseOut)) }; - var targetNormalizedKeyFrames = new Dictionary + + var reversedKey = (float)(1 - opacityTransitionProgressKey.Y); + var reversedKeyForSource = Math.Clamp(reversedKey + 0.1f, 0, 1); + var reversedSourceNormalizedKeyFrames = new Dictionary { - [normalKey] = (1, GetEasingFunctionFactory(EasingType.Cubic, EasingMode.EaseOut)) + [GetOpacityTransitionStartKey(reversedKeyForSource)] = (1, null), + [GetOpacityTransitionEndKey(reversedKeyForSource)] = (0, GetEasingFunctionFactory(EasingType.Cubic, EasingMode.EaseIn, true)) }; var reversedTargetNormalizedKeyFrames = new Dictionary { - [reversedKey] = (0, null), - [1] = (1, GetEasingFunctionFactory(EasingType.Cubic, EasingMode.EaseOut, true)) + [GetOpacityTransitionStartKey(reversedKey)] = (0, null), + [GetOpacityTransitionEndKey(reversedKey)] = (1, GetEasingFunctionFactory(EasingType.Cubic, EasingMode.EaseOut, true)), }; + return (this.Opacity(0, null, 1, duration: duration, normalizedKeyFrames: sourceNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedSourceNormalizedKeyFrames), this.Opacity(1, null, 0, duration: duration, normalizedKeyFrames: targetNormalizedKeyFrames, reversedNormalizedKeyFrames: reversedTargetNormalizedKeyFrames)); } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index 07152fa9564..f44bca11231 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -186,13 +186,13 @@ public FrameworkElement Target public Point DefaultIndependentTranslation { get; set; } = new(0, 20); /// - /// Gets or sets the key point of opacity transition. + /// Gets or sets the default key point of opacity transition. /// The time the keyframe of opacity from 0 to 1 or from 1 to 0 should occur at, expressed as a percentage of the animation duration. The allowed values are from (0, 0) to (1, 1). - /// .X will be used in the animation of the normal direction. - /// .Y will be used in the animation of the reverse direction. + /// .X will be used in the animation of the normal direction. + /// .Y will be used in the animation of the reverse direction. /// The default value is (0.3, 0.3). /// - public Point OpacityTransitionProgressKey { get; set; } = new(0.3, 0.3); + public Point DefaultOpacityTransitionProgressKey { get; set; } = new(0.3, 0.3); /// /// Gets or sets the easing function type for animation of independent or unpaired UI elements. From e8ce4dde98da66293e5c092a810b3ddef64c0248 Mon Sep 17 00:00:00 2001 From: Chaochao Huang Date: Wed, 9 Nov 2022 12:56:31 +0800 Subject: [PATCH 94/94] update --- .../Helpers/TransitionHelper.Properties.cs | 5 ----- .../Helpers/TransitionHelper.cs | 7 +++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs index f44bca11231..d10d7bcfbfc 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.Properties.cs @@ -96,11 +96,6 @@ public FrameworkElement Target /// public List Configs { get; set; } = new(); - /// - /// Gets or sets the default transition configuration. - /// - public TransitionConfig DefaultConfig { get; set; } = new(); - /// /// Gets a value indicating whether the source control has been morphed to the target control. /// The default value is false. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs index cb579ed7b4d..52f497b4c7d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Helpers/TransitionHelper.cs @@ -40,6 +40,13 @@ public IEnumerable All() private AnimatedElements TargetAnimatedElements => _targetAnimatedElements ??= GetAnimatedElements(this.Target); + private TransitionConfig DefaultConfig => new() + { + EasingMode = DefaultEasingMode, + EasingType = DefaultEasingType, + OpacityTransitionProgressKey = DefaultIndependentTranslation + }; + /// /// Gets a value indicating whether the source and target controls are animating. ///