Animation.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.CodeAnalysis;
  4. using System.Linq;
  5. using System.Reactive.Disposables;
  6. using System.Reactive.Linq;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using Avalonia.Animation.Animators;
  10. using Avalonia.Animation.Easings;
  11. using Avalonia.Data;
  12. using Avalonia.Metadata;
  13. namespace Avalonia.Animation
  14. {
  15. /// <summary>
  16. /// Tracks the progress of an animation.
  17. /// </summary>
  18. public class Animation : AvaloniaObject, IAnimation
  19. {
  20. /// <summary>
  21. /// Defines the <see cref="Duration"/> property.
  22. /// </summary>
  23. public static readonly DirectProperty<Animation, TimeSpan> DurationProperty =
  24. AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
  25. nameof(Duration),
  26. o => o._duration,
  27. (o, v) => o._duration = v);
  28. /// <summary>
  29. /// Defines the <see cref="IterationCount"/> property.
  30. /// </summary>
  31. public static readonly DirectProperty<Animation, IterationCount> IterationCountProperty =
  32. AvaloniaProperty.RegisterDirect<Animation, IterationCount>(
  33. nameof(IterationCount),
  34. o => o._iterationCount,
  35. (o, v) => o._iterationCount = v);
  36. /// <summary>
  37. /// Defines the <see cref="PlaybackDirection"/> property.
  38. /// </summary>
  39. public static readonly DirectProperty<Animation, PlaybackDirection> PlaybackDirectionProperty =
  40. AvaloniaProperty.RegisterDirect<Animation, PlaybackDirection>(
  41. nameof(PlaybackDirection),
  42. o => o._playbackDirection,
  43. (o, v) => o._playbackDirection = v);
  44. /// <summary>
  45. /// Defines the <see cref="FillMode"/> property.
  46. /// </summary>
  47. public static readonly DirectProperty<Animation, FillMode> FillModeProperty =
  48. AvaloniaProperty.RegisterDirect<Animation, FillMode>(
  49. nameof(FillMode),
  50. o => o._fillMode,
  51. (o, v) => o._fillMode = v);
  52. /// <summary>
  53. /// Defines the <see cref="Easing"/> property.
  54. /// </summary>
  55. public static readonly DirectProperty<Animation, Easing> EasingProperty =
  56. AvaloniaProperty.RegisterDirect<Animation, Easing>(
  57. nameof(Easing),
  58. o => o._easing,
  59. (o, v) => o._easing = v);
  60. /// <summary>
  61. /// Defines the <see cref="Delay"/> property.
  62. /// </summary>
  63. public static readonly DirectProperty<Animation, TimeSpan> DelayProperty =
  64. AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
  65. nameof(Delay),
  66. o => o._delay,
  67. (o, v) => o._delay = v);
  68. /// <summary>
  69. /// Defines the <see cref="DelayBetweenIterations"/> property.
  70. /// </summary>
  71. public static readonly DirectProperty<Animation, TimeSpan> DelayBetweenIterationsProperty =
  72. AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
  73. nameof(DelayBetweenIterations),
  74. o => o._delayBetweenIterations,
  75. (o, v) => o._delayBetweenIterations = v);
  76. /// <summary>
  77. /// Defines the <see cref="SpeedRatio"/> property.
  78. /// </summary>
  79. public static readonly DirectProperty<Animation, double> SpeedRatioProperty =
  80. AvaloniaProperty.RegisterDirect<Animation, double>(
  81. nameof(SpeedRatio),
  82. o => o._speedRatio,
  83. (o, v) => o._speedRatio = v,
  84. defaultBindingMode: BindingMode.TwoWay);
  85. private TimeSpan _duration;
  86. private IterationCount _iterationCount = new IterationCount(1);
  87. private PlaybackDirection _playbackDirection;
  88. private FillMode _fillMode;
  89. private Easing _easing = new LinearEasing();
  90. private TimeSpan _delay = TimeSpan.Zero;
  91. private TimeSpan _delayBetweenIterations = TimeSpan.Zero;
  92. private double _speedRatio = 1d;
  93. /// <summary>
  94. /// Gets or sets the active time of this animation.
  95. /// </summary>
  96. public TimeSpan Duration
  97. {
  98. get { return _duration; }
  99. set { SetAndRaise(DurationProperty, ref _duration, value); }
  100. }
  101. /// <summary>
  102. /// Gets or sets the repeat count for this animation.
  103. /// </summary>
  104. public IterationCount IterationCount
  105. {
  106. get { return _iterationCount; }
  107. set { SetAndRaise(IterationCountProperty, ref _iterationCount, value); }
  108. }
  109. /// <summary>
  110. /// Gets or sets the playback direction for this animation.
  111. /// </summary>
  112. public PlaybackDirection PlaybackDirection
  113. {
  114. get { return _playbackDirection; }
  115. set { SetAndRaise(PlaybackDirectionProperty, ref _playbackDirection, value); }
  116. }
  117. /// <summary>
  118. /// Gets or sets the value fill mode for this animation.
  119. /// </summary>
  120. public FillMode FillMode
  121. {
  122. get { return _fillMode; }
  123. set { SetAndRaise(FillModeProperty, ref _fillMode, value); }
  124. }
  125. /// <summary>
  126. /// Gets or sets the easing function to be used for this animation.
  127. /// </summary>
  128. public Easing Easing
  129. {
  130. get { return _easing; }
  131. set { SetAndRaise(EasingProperty, ref _easing, value); }
  132. }
  133. /// <summary>
  134. /// Gets or sets the initial delay time for this animation.
  135. /// </summary>
  136. public TimeSpan Delay
  137. {
  138. get { return _delay; }
  139. set { SetAndRaise(DelayProperty, ref _delay, value); }
  140. }
  141. /// <summary>
  142. /// Gets or sets the delay time in between iterations.
  143. /// </summary>
  144. public TimeSpan DelayBetweenIterations
  145. {
  146. get { return _delayBetweenIterations; }
  147. set { SetAndRaise(DelayBetweenIterationsProperty, ref _delayBetweenIterations, value); }
  148. }
  149. /// <summary>
  150. /// Gets or sets the speed multiple for this animation.
  151. /// </summary>
  152. public double SpeedRatio
  153. {
  154. get { return _speedRatio; }
  155. set { SetAndRaise(SpeedRatioProperty, ref _speedRatio, value); }
  156. }
  157. /// <summary>
  158. /// Gets the children of the <see cref="Animation"/>.
  159. /// </summary>
  160. [Content]
  161. public KeyFrames Children { get; } = new KeyFrames();
  162. // Store values for the Animator attached properties for IAnimationSetter objects.
  163. private static readonly Dictionary<IAnimationSetter, (Type Type, Func<IAnimator> Factory)> s_animators = new();
  164. /// <summary>
  165. /// Gets the value of the Animator attached property for a setter.
  166. /// </summary>
  167. /// <param name="setter">The animation setter.</param>
  168. /// <returns>The property animator type.</returns>
  169. public static (Type Type, Func<IAnimator> Factory)? GetAnimator(IAnimationSetter setter)
  170. {
  171. if (s_animators.TryGetValue(setter, out var type))
  172. {
  173. return type;
  174. }
  175. return null;
  176. }
  177. /// <summary>
  178. /// Sets the value of the Animator attached property for a setter.
  179. /// </summary>
  180. /// <param name="setter">The animation setter.</param>
  181. /// <param name="value">The property animator value.</param>
  182. public static void SetAnimator(IAnimationSetter setter,
  183. [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicMethods)]
  184. Type value)
  185. {
  186. s_animators[setter] = (value, () => (IAnimator)Activator.CreateInstance(value)!);
  187. }
  188. private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator, Func<IAnimator> Factory)> Animators = new()
  189. {
  190. ( prop => typeof(bool).IsAssignableFrom(prop.PropertyType), typeof(BoolAnimator), () => new BoolAnimator() ),
  191. ( prop => typeof(byte).IsAssignableFrom(prop.PropertyType), typeof(ByteAnimator), () => new ByteAnimator() ),
  192. ( prop => typeof(Int16).IsAssignableFrom(prop.PropertyType), typeof(Int16Animator), () => new Int16Animator() ),
  193. ( prop => typeof(Int32).IsAssignableFrom(prop.PropertyType), typeof(Int32Animator), () => new Int32Animator() ),
  194. ( prop => typeof(Int64).IsAssignableFrom(prop.PropertyType), typeof(Int64Animator), () => new Int64Animator() ),
  195. ( prop => typeof(UInt16).IsAssignableFrom(prop.PropertyType), typeof(UInt16Animator), () => new UInt16Animator() ),
  196. ( prop => typeof(UInt32).IsAssignableFrom(prop.PropertyType), typeof(UInt32Animator), () => new UInt32Animator() ),
  197. ( prop => typeof(UInt64).IsAssignableFrom(prop.PropertyType), typeof(UInt64Animator), () => new UInt64Animator() ),
  198. ( prop => typeof(float).IsAssignableFrom(prop.PropertyType), typeof(FloatAnimator), () => new FloatAnimator() ),
  199. ( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator), () => new DoubleAnimator() ),
  200. ( prop => typeof(decimal).IsAssignableFrom(prop.PropertyType), typeof(DecimalAnimator), () => new DecimalAnimator() ),
  201. };
  202. /// <summary>
  203. /// Registers a <see cref="Animator{T}"/> that can handle
  204. /// a value type that matches the specified condition.
  205. /// </summary>
  206. /// <param name="condition">
  207. /// The condition to which the <see cref="Animator{T}"/>
  208. /// is to be activated and used.
  209. /// </param>
  210. /// <typeparam name="TAnimator">
  211. /// The type of the animator to instantiate.
  212. /// </typeparam>
  213. public static void RegisterAnimator<TAnimator>(Func<AvaloniaProperty, bool> condition)
  214. where TAnimator : IAnimator, new()
  215. {
  216. Animators.Insert(0, (condition, typeof(TAnimator), () => new TAnimator()));
  217. }
  218. private static (Type Type, Func<IAnimator> Factory)? GetAnimatorType(AvaloniaProperty property)
  219. {
  220. foreach (var (condition, type, factory) in Animators)
  221. {
  222. if (condition(property))
  223. {
  224. return (type, factory);
  225. }
  226. }
  227. return null;
  228. }
  229. private (IList<IAnimator> Animators, IList<IDisposable> subscriptions) InterpretKeyframes(Animatable control)
  230. {
  231. var handlerList = new Dictionary<(Type type, AvaloniaProperty Property), Func<IAnimator>>();
  232. var animatorKeyFrames = new List<AnimatorKeyFrame>();
  233. var subscriptions = new List<IDisposable>();
  234. foreach (var keyframe in Children)
  235. {
  236. foreach (var setter in keyframe.Setters)
  237. {
  238. if (setter.Property is null)
  239. {
  240. throw new InvalidOperationException("No Setter property assigned.");
  241. }
  242. var handler = Animation.GetAnimator(setter) ?? GetAnimatorType(setter.Property);
  243. if (handler == null)
  244. {
  245. throw new InvalidOperationException($"No animator registered for the property {setter.Property}. Add an animator to the Animation.Animators collection that matches this property to animate it.");
  246. }
  247. var (type, factory) = handler.Value;
  248. if (!handlerList.ContainsKey((type, setter.Property)))
  249. handlerList[(type, setter.Property)] = factory;
  250. var cue = keyframe.Cue;
  251. if (keyframe.TimingMode == KeyFrameTimingMode.TimeSpan)
  252. {
  253. cue = new Cue(keyframe.KeyTime.TotalSeconds / Duration.TotalSeconds);
  254. }
  255. var newKF = new AnimatorKeyFrame(type, factory, cue, keyframe.KeySpline);
  256. subscriptions.Add(newKF.BindSetter(setter, control));
  257. animatorKeyFrames.Add(newKF);
  258. }
  259. }
  260. var newAnimatorInstances = new List<IAnimator>();
  261. foreach (var handler in handlerList)
  262. {
  263. var newInstance = handler.Value();
  264. newInstance.Property = handler.Key.Property;
  265. newAnimatorInstances.Add(newInstance);
  266. }
  267. foreach (var keyframe in animatorKeyFrames)
  268. {
  269. var animator = newAnimatorInstances.First(a => a.GetType() == keyframe.AnimatorType &&
  270. a.Property == keyframe.Property);
  271. animator.Add(keyframe);
  272. }
  273. return (newAnimatorInstances, subscriptions);
  274. }
  275. /// <inheritdoc/>
  276. public IDisposable Apply(Animatable control, IClock? clock, IObservable<bool> match, Action? onComplete)
  277. {
  278. var (animators, subscriptions) = InterpretKeyframes(control);
  279. if (animators.Count == 1)
  280. {
  281. var subscription = animators[0].Apply(this, control, clock, match, onComplete);
  282. if (subscription is not null)
  283. {
  284. subscriptions.Add(subscription);
  285. }
  286. }
  287. else
  288. {
  289. var completionTasks = onComplete != null ? new List<Task>() : null;
  290. foreach (IAnimator animator in animators)
  291. {
  292. Action? animatorOnComplete = null;
  293. if (onComplete != null)
  294. {
  295. var tcs = new TaskCompletionSource<object?>();
  296. animatorOnComplete = () => tcs.SetResult(null);
  297. completionTasks!.Add(tcs.Task);
  298. }
  299. var subscription = animator.Apply(this, control, clock, match, animatorOnComplete);
  300. if (subscription is not null)
  301. {
  302. subscriptions.Add(subscription);
  303. }
  304. }
  305. if (onComplete != null)
  306. {
  307. Task.WhenAll(completionTasks!).ContinueWith(
  308. (_, state) => ((Action)state!).Invoke(),
  309. onComplete);
  310. }
  311. }
  312. return new CompositeDisposable(subscriptions);
  313. }
  314. /// <inheritdoc/>
  315. public Task RunAsync(Animatable control, IClock? clock = null)
  316. {
  317. return RunAsync(control, clock, default);
  318. }
  319. /// <inheritdoc/>
  320. public Task RunAsync(Animatable control, IClock? clock = null, CancellationToken cancellationToken = default)
  321. {
  322. if (cancellationToken.IsCancellationRequested)
  323. {
  324. return Task.CompletedTask;
  325. }
  326. var run = new TaskCompletionSource<object?>();
  327. if (this.IterationCount == IterationCount.Infinite)
  328. run.SetException(new InvalidOperationException("Looping animations must not use the Run method."));
  329. IDisposable? subscriptions = null, cancellation = null;
  330. subscriptions = this.Apply(control, clock, Observable.Return(true), () =>
  331. {
  332. run.TrySetResult(null);
  333. subscriptions?.Dispose();
  334. cancellation?.Dispose();
  335. });
  336. cancellation = cancellationToken.Register(() =>
  337. {
  338. run.TrySetResult(null);
  339. subscriptions?.Dispose();
  340. cancellation?.Dispose();
  341. });
  342. return run.Task;
  343. }
  344. }
  345. }