Skip to content

Commit 70186fa

Browse files
committed
Fix Slow AnimatedImage #143
1 parent 0f36417 commit 70186fa

2 files changed

Lines changed: 222 additions & 49 deletions

File tree

dev/DevWinUI.Controls/Controls/Native/Others/AnimatedImage.cs

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ public partial class AnimatedImage : Control
1010
private Image _bottomImage;
1111
private Image _topImage;
1212
private Compositor _compositor;
13-
private ScalarKeyFrameAnimation? _opacityAnimation;
13+
private ScalarKeyFrameAnimation _opacityAnimation;
14+
private CompositionScopedBatch _currentBatch;
1415

1516
public Uri ImageUrl
1617
{
@@ -19,101 +20,103 @@ public Uri ImageUrl
1920
}
2021

2122
public static readonly DependencyProperty ImageUrlProperty =
22-
DependencyProperty.Register(nameof(ImageUrl), typeof(Uri), typeof(AnimatedImage), new PropertyMetadata(null, OnImageSourceChanged));
23+
DependencyProperty.Register(nameof(ImageUrl), typeof(Uri), typeof(AnimatedImage), new PropertyMetadata(null, OnImageChanged));
2324

2425
public BitmapImage ImageSource
2526
{
26-
get { return (BitmapImage)GetValue(ImageSourceProperty); }
27-
set { SetValue(ImageSourceProperty, value); }
27+
get => (BitmapImage)GetValue(ImageSourceProperty);
28+
set => SetValue(ImageSourceProperty, value);
2829
}
2930

3031
public static readonly DependencyProperty ImageSourceProperty =
31-
DependencyProperty.Register(nameof(ImageSource), typeof(BitmapImage), typeof(AnimatedImage), new PropertyMetadata(null, OnImageSourceChanged));
32+
DependencyProperty.Register(nameof(ImageSource), typeof(BitmapImage), typeof(AnimatedImage), new PropertyMetadata(null, OnImageChanged));
3233

3334
public Stretch Stretch
3435
{
35-
get { return (Stretch)GetValue(StretchProperty); }
36-
set { SetValue(StretchProperty, value); }
36+
get => (Stretch)GetValue(StretchProperty);
37+
set => SetValue(StretchProperty, value);
3738
}
3839

3940
public static readonly DependencyProperty StretchProperty =
4041
DependencyProperty.Register(nameof(Stretch), typeof(Stretch), typeof(AnimatedImage), new PropertyMetadata(Stretch.UniformToFill));
4142

43+
public TimeSpan OpacityAnimationDuration
44+
{
45+
get { return (TimeSpan)GetValue(OpacityAnimationDurationProperty); }
46+
set { SetValue(OpacityAnimationDurationProperty, value); }
47+
}
48+
49+
public static readonly DependencyProperty OpacityAnimationDurationProperty =
50+
DependencyProperty.Register(nameof(OpacityAnimationDuration), typeof(TimeSpan), typeof(AnimatedImage), new PropertyMetadata(TimeSpan.FromMilliseconds(600)));
51+
4252
public AnimatedImage()
4353
{
44-
this.DefaultStyleKey = typeof(AnimatedImage);
54+
DefaultStyleKey = typeof(AnimatedImage);
4555
}
4656

4757
protected override void OnApplyTemplate()
4858
{
4959
base.OnApplyTemplate();
5060

51-
_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
52-
5361
_bottomImage = GetTemplateChild(PART_BottomImage) as Image;
5462
_topImage = GetTemplateChild(PART_TopImage) as Image;
5563

56-
InitAnimations();
64+
if (_bottomImage == null || _topImage == null)
65+
return;
5766

58-
OnIsImageChanged();
67+
_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
68+
69+
InitAnimation();
70+
UpdateImage();
5971
}
60-
private static void OnImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
72+
73+
private static void OnImageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
6174
{
62-
if (d is AnimatedImage control)
63-
{
64-
control.OnIsImageChanged();
65-
}
75+
((AnimatedImage)d).UpdateImage();
6676
}
6777

68-
private void InitAnimations()
78+
private void InitAnimation()
6979
{
7080
_opacityAnimation = _compositor.CreateScalarKeyFrameAnimation();
71-
_opacityAnimation.InsertKeyFrame(0.0f, 1.0f);
72-
_opacityAnimation.InsertKeyFrame(1.0f, 0.0f);
73-
_opacityAnimation.Duration = TimeSpan.FromMilliseconds(800);
81+
_opacityAnimation.InsertKeyFrame(0f, 1f);
82+
_opacityAnimation.InsertKeyFrame(1f, 0f);
83+
_opacityAnimation.Duration = OpacityAnimationDuration;
7484
}
7585

76-
private void OnIsImageChanged()
86+
private void UpdateImage()
7787
{
7888
if (_bottomImage == null || _topImage == null)
7989
return;
8090

81-
BitmapImage bitmapImage = null;
91+
ImageSource newSource = ImageSource;
8292

83-
if (ImageSource != null)
84-
{
85-
bitmapImage = ImageSource;
86-
}
87-
else if (ImageUrl != null)
88-
{
89-
bitmapImage = new BitmapImage(ImageUrl);
90-
}
91-
else
92-
{
93+
if (newSource == null && ImageUrl != null)
94+
newSource = new BitmapImage(ImageUrl);
95+
96+
if (newSource == null)
9397
return;
94-
}
9598

96-
_bottomImage.Source = bitmapImage;
99+
if (_bottomImage.Source == newSource)
100+
return;
97101

98-
_bottomImage.Opacity = 1;
102+
_bottomImage.Source = newSource;
99103

100104
var topVisual = ElementCompositionPreview.GetElementVisual(_topImage);
101-
topVisual.Opacity = 1.0f;
105+
106+
_currentBatch?.Dispose();
107+
108+
topVisual.Opacity = 1f;
109+
110+
_currentBatch = _compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
102111

103112
topVisual.StartAnimation("Opacity", _opacityAnimation);
104113

105-
// Set new image after fade-out
106-
_ = Task.Delay(800).ContinueWith(_ =>
114+
_currentBatch.Completed += (s, e) =>
107115
{
108-
DispatcherQueue.TryEnqueue(() =>
109-
{
110-
try
111-
{
112-
_topImage.Source = bitmapImage;
113-
topVisual.Opacity = 1.0f;
114-
}
115-
catch { }
116-
});
117-
});
116+
_topImage.Source = newSource;
117+
topVisual.Opacity = 1f;
118+
};
119+
120+
_currentBatch.End();
118121
}
119122
}

0 commit comments

Comments
 (0)