Open SetTrend opened 3 weeks ago
That seems like a good idea.
This is indeed a common limitation in WPF and yes, it usually takes some lines of code to work around this.
If I understand you correctly your proposed property (AccelerateByPrevious
) would be used in code behind rather than XAML to adjust the duration of the animation before being started?
While this is probably useful, I think it would be great if this could be easily used in XAML.
So, there is already an IsAdditive
property that adds the current value to the animation value, in the same manner we could also have a way to affect the animation duration.
In my opinion it would be great, if there was a property called something like InterpolateDuration
that would adjust the duration of an animation depending on the chosen enum value.
There could be an option called ByPreviousAnimation
that would adjust the duration in relation of the progress of the previous animation as suggested with AccelerateByPrevious
.
There could another option called ByCurrentValue
that simply calculates the factor by something like (To - From / Current - From)
or so.
And None
(the default) to not adjust the duration.
I played a bit that idea and tried to accomplish that behavior somehow with existing functionality in WPF. I took your example code and utilized IsAdditive
together with SpeedRatio
and came to this result:
<Grid>
<Button Content="Test" Margin="50" RenderTransformOrigin="0.5,0.5">
<Button.Resources>
<local:MathConverter x:Key="MathConverter" Min="1" />
</Button.Resources>
<Button.RenderTransform>
<RotateTransform x:Name="RT" />
</Button.RenderTransform>
<Button.Triggers>
<EventTrigger RoutedEvent="Control.MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.Angle" From="0" To="360" IsAdditive="True" RepeatBehavior="Forever" Duration="0:0:2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Control.MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation SpeedRatio="{Binding ElementName=RT, Path=Angle, Converter={StaticResource MathConverter}, ConverterParameter=360/x}" Storyboard.TargetProperty="RenderTransform.Angle" To="0" Duration="0:0:2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>
And a converter for calculating the speed ratio from the current angle (360 / angle
):
public class MathConverter : IValueConverter
{
public double Min { get; set; } = double.MinValue;
public double Max { get; set; } = double.MaxValue;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var x = System.Convert.ToDouble(value);
var expression = parameter.ToString()!.Replace("x", x.ToString(CultureInfo.InvariantCulture));
var result = System.Convert.ToDouble(new DataTable().Compute(expression, ""));
return Math.Clamp(result, Min, Max);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
https://github.com/user-attachments/assets/5cc0d8d2-ba80-4378-9ec6-5de2ac4e7278
The solution is not perfect, but maybe good enough. 😋
I am certainly not a fan of the name, 'offset' would fit better than 'accelerate' (also we have e.g. in color gradients), and previous is also not very descriptive.
The storyboard could find out the source of current value is animation and adjust accordingly automatically. However, I am not sure that is a good direction anyway, because the angle can be set by several other sources, so relying on there being a previous storyboard seems fragile. Also there is acceleration and deceleration ratios and I am not sure how well these would play together.
Let's focus on the intent instead, wouldn't having something like <ReverseStoryboard />
solve the problem?
@MichaeIDietrich: Actually, the proposal was considering a dependency property. I.e., this is supposed to be used in XAML, of course:
(cannot highlight code here, hence the image)
@miloush: The name is just a basis for discussion; a token in order to avoid referring to it using half a sentence instead. – If it's called foo
in the end, I wouldn't mind 😉
Current Situation
We all know the effect that if an animation is replacing an ongoing animation, the duration time may result in unwanted sluggish animation if the previous animation has just started:
Desired Situation
I'd like to propose a new property to
System.Windows.Media.Animation.Timeline
. A property that's considering the progress amount of a previously running animation when calculating the first iteration's duration of the subsequent animation.The calculation is simple:
Just multiply the new, subsequent animation's first iteration's duration with the progress amount of the currently running animation that's being assigned to the same target property of the same target object if no
From
value is provided in the subsequent animation:The above implementation may even be used for the animation itself when it is repeating: If called at the beginning of a repetition cycle (i.e., "iteration"),
prevStoryboard
would be the currentStoryboard
itself andGetCurrentProgress()
would return1
.Proposed Property
Let's call the proposed new property
AccelerateByPreviousProperty
.The proposed new dependency property,
AccelerateByPrevious
, should specify how much of a previous animation is used in the calculation: from none to full consideration.So, I suggest
AccelerateByPreviousProperty
to be adouble
value in the range[0, 1]
, with1
being the default (or0
being the default for backward compatibility).This would change above calculation from:
… to:
Provided,
prevStoryboard.GetCurrentProgress()
would be in the range[0, 1]
andAccelerateByPrevious
would also be in the range[0, 1]
, the multiplication factor for theDuration
calculation would result in:AccelerateByPrevious
[0, 1]
0
[1, 1]
[0, 1]
0.5
[.5, 1]
[0, 1]
1
[0, 1]