committed by
GitHub
12 changed files with 548 additions and 53 deletions
@ -0,0 +1,123 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
using Avalonia.Data; |
||||
|
using Avalonia.Media; |
||||
|
using Avalonia.Media.Immutable; |
||||
|
|
||||
|
#nullable enable |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="SolidColorBrush"/> values.
|
||||
|
/// </summary>
|
||||
|
public class GradientBrushAnimator : Animator<IGradientBrush?> |
||||
|
{ |
||||
|
private static readonly RelativePointAnimator s_relativePointAnimator = new RelativePointAnimator(); |
||||
|
private static readonly DoubleAnimator s_doubleAnimator = new DoubleAnimator(); |
||||
|
|
||||
|
public override IGradientBrush? Interpolate(double progress, IGradientBrush? oldValue, IGradientBrush? newValue) |
||||
|
{ |
||||
|
if (oldValue is null || newValue is null) |
||||
|
{ |
||||
|
return progress >= 0.5 ? newValue : oldValue; |
||||
|
} |
||||
|
|
||||
|
switch (oldValue) |
||||
|
{ |
||||
|
case IRadialGradientBrush oldRadial when newValue is IRadialGradientBrush newRadial: |
||||
|
return new ImmutableRadialGradientBrush( |
||||
|
InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), |
||||
|
s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), |
||||
|
oldValue.SpreadMethod, |
||||
|
s_relativePointAnimator.Interpolate(progress, oldRadial.Center, newRadial.Center), |
||||
|
s_relativePointAnimator.Interpolate(progress, oldRadial.GradientOrigin, newRadial.GradientOrigin), |
||||
|
s_doubleAnimator.Interpolate(progress, oldRadial.Radius, newRadial.Radius)); |
||||
|
|
||||
|
case IConicGradientBrush oldConic when newValue is IConicGradientBrush newConic: |
||||
|
return new ImmutableConicGradientBrush( |
||||
|
InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), |
||||
|
s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), |
||||
|
oldValue.SpreadMethod, |
||||
|
s_relativePointAnimator.Interpolate(progress, oldConic.Center, newConic.Center), |
||||
|
s_doubleAnimator.Interpolate(progress, oldConic.Angle, newConic.Angle)); |
||||
|
|
||||
|
case ILinearGradientBrush oldLinear when newValue is ILinearGradientBrush newLinear: |
||||
|
return new ImmutableLinearGradientBrush( |
||||
|
InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), |
||||
|
s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), |
||||
|
oldValue.SpreadMethod, |
||||
|
s_relativePointAnimator.Interpolate(progress, oldLinear.StartPoint, newLinear.StartPoint), |
||||
|
s_relativePointAnimator.Interpolate(progress, oldLinear.EndPoint, newLinear.EndPoint)); |
||||
|
|
||||
|
default: |
||||
|
return progress >= 0.5 ? newValue : oldValue; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override IDisposable BindAnimation(Animatable control, IObservable<IGradientBrush?> instance) |
||||
|
{ |
||||
|
return control.Bind((AvaloniaProperty<IBrush?>)Property, instance, BindingPriority.Animation); |
||||
|
} |
||||
|
|
||||
|
private IReadOnlyList<ImmutableGradientStop> InterpolateStops(double progress, IReadOnlyList<IGradientStop> oldValue, IReadOnlyList<IGradientStop> newValue) |
||||
|
{ |
||||
|
var resultCount = Math.Max(oldValue.Count, newValue.Count); |
||||
|
var stops = new ImmutableGradientStop[resultCount]; |
||||
|
|
||||
|
for (int index = 0, oldIndex = 0, newIndex = 0; index < resultCount; index++) |
||||
|
{ |
||||
|
stops[index] = new ImmutableGradientStop( |
||||
|
s_doubleAnimator.Interpolate(progress, oldValue[oldIndex].Offset, newValue[newIndex].Offset), |
||||
|
ColorAnimator.InterpolateCore(progress, oldValue[oldIndex].Color, newValue[newIndex].Color)); |
||||
|
|
||||
|
if (oldIndex < oldValue.Count - 1) |
||||
|
{ |
||||
|
oldIndex++; |
||||
|
} |
||||
|
|
||||
|
if (newIndex < newValue.Count - 1) |
||||
|
{ |
||||
|
newIndex++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return stops; |
||||
|
} |
||||
|
|
||||
|
internal static IGradientBrush ConvertSolidColorBrushToGradient(IGradientBrush gradientBrush, ISolidColorBrush solidColorBrush) |
||||
|
{ |
||||
|
switch (gradientBrush) |
||||
|
{ |
||||
|
case IRadialGradientBrush oldRadial: |
||||
|
return new ImmutableRadialGradientBrush( |
||||
|
CreateStopsFromSolidColorBrush(solidColorBrush, oldRadial.GradientStops), solidColorBrush.Opacity, |
||||
|
oldRadial.SpreadMethod, oldRadial.Center, oldRadial.GradientOrigin, oldRadial.Radius); |
||||
|
|
||||
|
case IConicGradientBrush oldConic: |
||||
|
return new ImmutableConicGradientBrush( |
||||
|
CreateStopsFromSolidColorBrush(solidColorBrush, oldConic.GradientStops), solidColorBrush.Opacity, |
||||
|
oldConic.SpreadMethod, oldConic.Center, oldConic.Angle); |
||||
|
|
||||
|
case ILinearGradientBrush oldLinear: |
||||
|
return new ImmutableLinearGradientBrush( |
||||
|
CreateStopsFromSolidColorBrush(solidColorBrush, oldLinear.GradientStops), solidColorBrush.Opacity, |
||||
|
oldLinear.SpreadMethod, oldLinear.StartPoint, oldLinear.EndPoint); |
||||
|
|
||||
|
default: |
||||
|
throw new NotSupportedException($"Gradient of type {gradientBrush?.GetType()} is not supported"); |
||||
|
} |
||||
|
|
||||
|
static IReadOnlyList<ImmutableGradientStop> CreateStopsFromSolidColorBrush(ISolidColorBrush solidColorBrush, IReadOnlyList<IGradientStop> baseStops) |
||||
|
{ |
||||
|
var stops = new ImmutableGradientStop[baseStops.Count]; |
||||
|
for (int index = 0; index < baseStops.Count; index++) |
||||
|
{ |
||||
|
stops[index] = new ImmutableGradientStop(baseStops[index].Offset, solidColorBrush.Color); |
||||
|
} |
||||
|
return stops; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="RelativePoint"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class RelativePointAnimator : Animator<RelativePoint> |
||||
|
{ |
||||
|
private static readonly PointAnimator s_pointAnimator = new PointAnimator(); |
||||
|
|
||||
|
public override RelativePoint Interpolate(double progress, RelativePoint oldValue, RelativePoint newValue) |
||||
|
{ |
||||
|
if (oldValue.Unit != newValue.Unit) |
||||
|
{ |
||||
|
return progress >= 0.5 ? newValue : oldValue; |
||||
|
} |
||||
|
|
||||
|
return new RelativePoint(s_pointAnimator.Interpolate(progress, oldValue.Point, newValue.Point), oldValue.Unit); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using Avalonia.Animation.Animators; |
||||
|
|
||||
|
namespace Avalonia.Animation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="RelativePoint"/> type.
|
||||
|
/// </summary>
|
||||
|
public class RelativePointTransition : AnimatorDrivenTransition<RelativePoint, RelativePointAnimator> |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue