CompositionPage.axaml.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Numerics;
  4. using System.Threading;
  5. using Avalonia;
  6. using Avalonia.Animation;
  7. using Avalonia.Controls;
  8. using Avalonia.Interactivity;
  9. using Avalonia.Media;
  10. using Avalonia.Media.Immutable;
  11. using Avalonia.Rendering.Composition;
  12. using Avalonia.Rendering.Composition.Animations;
  13. using Avalonia.VisualTree;
  14. using Math = System.Math;
  15. namespace ControlCatalog.Pages;
  16. public partial class CompositionPage : UserControl
  17. {
  18. private ImplicitAnimationCollection? _implicitAnimations;
  19. private CompositionCustomVisual? _customVisual;
  20. private CompositionSolidColorVisual? _solidVisual;
  21. public CompositionPage()
  22. {
  23. InitializeComponent();
  24. AttachAnimatedSolidVisual(SolidVisualHost);
  25. AttachCustomVisual(CustomVisualHost);
  26. }
  27. protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
  28. {
  29. base.OnAttachedToVisualTree(e);
  30. Items.ItemsSource = CreateColorItems();
  31. }
  32. private static List<CompositionPageColorItem> CreateColorItems()
  33. {
  34. var list = new List<CompositionPageColorItem>();
  35. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 255, 185, 0)));
  36. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 231, 72, 86)));
  37. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 0, 120, 215)));
  38. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 0, 153, 188)));
  39. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 122, 117, 116)));
  40. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 118, 118, 118)));
  41. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 255, 141, 0)));
  42. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 232, 17, 35)));
  43. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 0, 99, 177)));
  44. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 45, 125, 154)));
  45. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 93, 90, 88)));
  46. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 76, 74, 72)));
  47. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 247, 99, 12)));
  48. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 234, 0, 94)));
  49. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 142, 140, 216)));
  50. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 0, 183, 195)));
  51. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 104, 118, 138)));
  52. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 105, 121, 126)));
  53. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 202, 80, 16)));
  54. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 195, 0, 82)));
  55. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 107, 105, 214)));
  56. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 3, 131, 135)));
  57. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 81, 92, 107)));
  58. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 74, 84, 89)));
  59. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 218, 59, 1)));
  60. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 227, 0, 140)));
  61. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 135, 100, 184)));
  62. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 0, 178, 148)));
  63. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 86, 124, 115)));
  64. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 100, 124, 100)));
  65. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 239, 105, 80)));
  66. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 191, 0, 119)));
  67. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 116, 77, 169)));
  68. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 1, 133, 116)));
  69. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 72, 104, 96)));
  70. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 82, 94, 84)));
  71. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 209, 52, 56)));
  72. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 194, 57, 179)));
  73. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 177, 70, 194)));
  74. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 0, 204, 106)));
  75. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 73, 130, 5)));
  76. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 132, 117, 69)));
  77. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 255, 67, 67)));
  78. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 154, 0, 137)));
  79. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 136, 23, 152)));
  80. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 16, 137, 62)));
  81. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 16, 124, 16)));
  82. list.Add(new CompositionPageColorItem(Color.FromArgb(255, 126, 115, 95)));
  83. return list;
  84. }
  85. private void EnsureImplicitAnimations()
  86. {
  87. if (_implicitAnimations == null)
  88. {
  89. var compositor = ElementComposition.GetElementVisual(this)!.Compositor;
  90. var offsetAnimation = compositor.CreateVector3KeyFrameAnimation();
  91. offsetAnimation.Target = "Offset";
  92. offsetAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");
  93. offsetAnimation.Duration = TimeSpan.FromMilliseconds(400);
  94. var rotationAnimation = compositor.CreateScalarKeyFrameAnimation();
  95. rotationAnimation.Target = "RotationAngle";
  96. rotationAnimation.InsertKeyFrame(.5f, 0.160f);
  97. rotationAnimation.InsertKeyFrame(1f, 0f);
  98. rotationAnimation.Duration = TimeSpan.FromMilliseconds(400);
  99. var animationGroup = compositor.CreateAnimationGroup();
  100. animationGroup.Add(offsetAnimation);
  101. animationGroup.Add(rotationAnimation);
  102. _implicitAnimations = compositor.CreateImplicitAnimationCollection();
  103. _implicitAnimations["Offset"] = animationGroup;
  104. }
  105. }
  106. public static void SetEnableAnimations(Border border, bool value)
  107. {
  108. var page = border.FindAncestorOfType<CompositionPage>();
  109. if (page == null)
  110. {
  111. border.AttachedToVisualTree += delegate { SetEnableAnimations(border, true); };
  112. return;
  113. }
  114. if (ElementComposition.GetElementVisual(page) == null)
  115. return;
  116. page.EnsureImplicitAnimations();
  117. if (border.GetVisualParent() is Visual visualParent
  118. && ElementComposition.GetElementVisual(visualParent) is CompositionVisual compositionVisual)
  119. {
  120. compositionVisual.ImplicitAnimations = page._implicitAnimations;
  121. }
  122. }
  123. void AttachAnimatedSolidVisual(Visual v)
  124. {
  125. void Update()
  126. {
  127. if(_solidVisual == null)
  128. return;
  129. _solidVisual.Size = new (v.Bounds.Width / 3, v.Bounds.Height / 3);
  130. _solidVisual.Offset = new (v.Bounds.Width / 3, v.Bounds.Height / 3, 0);
  131. }
  132. v.AttachedToVisualTree += delegate
  133. {
  134. var compositor = ElementComposition.GetElementVisual(v)?.Compositor;
  135. if(compositor == null || _solidVisual?.Compositor == compositor)
  136. return;
  137. _solidVisual = compositor.CreateSolidColorVisual();
  138. ElementComposition.SetElementChildVisual(v, _solidVisual);
  139. _solidVisual.Color = Colors.Red;
  140. var animation = _solidVisual.Compositor.CreateColorKeyFrameAnimation();
  141. animation.InsertKeyFrame(0, Colors.Red);
  142. animation.InsertKeyFrame(0.5f, Colors.Blue);
  143. animation.InsertKeyFrame(1, Colors.Green);
  144. animation.Duration = TimeSpan.FromSeconds(5);
  145. animation.IterationBehavior = AnimationIterationBehavior.Forever;
  146. animation.Direction = PlaybackDirection.Alternate;
  147. _solidVisual.StartAnimation("Color", animation);
  148. _solidVisual.AnchorPoint = new (0, 0);
  149. var scale = _solidVisual.Compositor.CreateVector3KeyFrameAnimation();
  150. scale.Duration = TimeSpan.FromSeconds(5);
  151. scale.IterationBehavior = AnimationIterationBehavior.Forever;
  152. scale.InsertKeyFrame(0, new Vector3(1, 1, 0));
  153. scale.InsertKeyFrame(0.5f, new Vector3(1.5f, 1.5f, 0));
  154. scale.InsertKeyFrame(1, new Vector3(1, 1, 0));
  155. _solidVisual.StartAnimation("Scale", scale);
  156. var center =
  157. _solidVisual.Compositor.CreateExpressionAnimation(
  158. "Vector3(this.Target.Size.X * 0.5, this.Target.Size.Y * 0.5, 1)");
  159. _solidVisual.StartAnimation("CenterPoint", center);
  160. Update();
  161. };
  162. v.PropertyChanged += (_, a) =>
  163. {
  164. if (a.Property == BoundsProperty)
  165. Update();
  166. };
  167. }
  168. void AttachCustomVisual(Visual v)
  169. {
  170. void Update()
  171. {
  172. if (_customVisual == null)
  173. return;
  174. var h = (float)Math.Min(v.Bounds.Height, v.Bounds.Width / 3);
  175. _customVisual.Size = new (v.Bounds.Width, h);
  176. _customVisual.Offset = new (0, (v.Bounds.Height - h) / 2, 0);
  177. }
  178. v.AttachedToVisualTree += delegate
  179. {
  180. var compositor = ElementComposition.GetElementVisual(v)?.Compositor;
  181. if(compositor == null || _customVisual?.Compositor == compositor)
  182. return;
  183. _customVisual = compositor.CreateCustomVisual(new CustomVisualHandler());
  184. ElementComposition.SetElementChildVisual(v, _customVisual);
  185. _customVisual.SendHandlerMessage(CustomVisualHandler.StartMessage);
  186. PreciseDirtyRectsCheckboxCustomVisualChanged(this, new());
  187. Update();
  188. };
  189. v.PropertyChanged += (_, a) =>
  190. {
  191. if (a.Property == BoundsProperty)
  192. Update();
  193. };
  194. }
  195. class CustomVisualHandler : CompositionCustomVisualHandler
  196. {
  197. private TimeSpan _animationElapsed;
  198. private TimeSpan? _lastServerTime;
  199. private bool _running;
  200. private bool _preciseDirtyRects;
  201. public static readonly object StopMessage = new(),
  202. StartMessage = new(),
  203. UsePreciseDirtyRects = new(),
  204. UseNonPreciseDirtyRects = new();
  205. private List<(Point center, double size, ImmutableSolidColorBrush brush)> _ellipses = new();
  206. void UpdateRects()
  207. {
  208. if (_running)
  209. {
  210. if (_lastServerTime.HasValue) _animationElapsed += (CompositionNow - _lastServerTime.Value);
  211. _lastServerTime = CompositionNow;
  212. }
  213. _ellipses.Clear();
  214. const int cnt = 20;
  215. var maxPointSizeX = EffectiveSize.X / (cnt * 1.6);
  216. var maxPointSizeY = EffectiveSize.Y / 4;
  217. var pointSize = Math.Min(maxPointSizeX, maxPointSizeY);
  218. var animationLength = TimeSpan.FromSeconds(4);
  219. var animationStage = _animationElapsed.TotalSeconds / animationLength.TotalSeconds;
  220. var sinOffset = Math.Cos(_animationElapsed.TotalSeconds) * 1.5;
  221. for (var c = 0; c < cnt; c++)
  222. {
  223. var stage = (animationStage + (double)c / cnt) % 1;
  224. var colorStage =
  225. (animationStage + (Math.Sin(_animationElapsed.TotalSeconds * 2) + 1) / 2 + (double)c / cnt) % 1;
  226. var posX = (EffectiveSize.X + pointSize * 3) * stage - pointSize;
  227. var posY = (EffectiveSize.Y - pointSize) * (1 + Math.Sin(stage * 3.14 * 3 + sinOffset)) / 2 + pointSize / 2;
  228. var opacity = Math.Sin(stage * 3.14);
  229. _ellipses.Add((new Point(posX, posY), pointSize / 2, new ImmutableSolidColorBrush(Color.FromArgb(
  230. 255,
  231. (byte)(255 - 255 * colorStage),
  232. (byte)(255 * Math.Abs(0.5 - colorStage) * 2),
  233. (byte)(255 * colorStage)
  234. ), opacity)));
  235. }
  236. }
  237. public override void OnRender(ImmediateDrawingContext drawingContext)
  238. {
  239. if (_ellipses.Count == 0)
  240. UpdateRects();
  241. foreach(var e in _ellipses)
  242. drawingContext.DrawEllipse(e.brush, null, e.center, e.size, e.size);
  243. }
  244. public override void OnMessage(object message)
  245. {
  246. if (message == StartMessage)
  247. {
  248. _running = true;
  249. _lastServerTime = null;
  250. RegisterForNextAnimationFrameUpdate();
  251. }
  252. else if (message == StopMessage)
  253. _running = false;
  254. else if (message == UsePreciseDirtyRects)
  255. _preciseDirtyRects = true;
  256. else if (message == UseNonPreciseDirtyRects)
  257. _preciseDirtyRects = false;
  258. }
  259. void InvalidateCurrentEllipseRects()
  260. {
  261. foreach (var e in _ellipses)
  262. Invalidate(new Rect(e.center.X - e.size, e.center.Y - e.size, e.size * 2, e.size * 2));
  263. }
  264. public override void OnAnimationFrameUpdate()
  265. {
  266. if (_running)
  267. {
  268. if (_preciseDirtyRects)
  269. InvalidateCurrentEllipseRects();
  270. else
  271. Invalidate();
  272. UpdateRects();
  273. if(_preciseDirtyRects)
  274. InvalidateCurrentEllipseRects();
  275. RegisterForNextAnimationFrameUpdate();
  276. }
  277. }
  278. }
  279. private void ButtonThreadSleep(object? sender, RoutedEventArgs e)
  280. {
  281. Thread.Sleep(10000);
  282. }
  283. private void ButtonStartCustomVisual(object? sender, RoutedEventArgs e)
  284. {
  285. _customVisual?.SendHandlerMessage(CustomVisualHandler.StartMessage);
  286. }
  287. private void ButtonStopCustomVisual(object? sender, RoutedEventArgs e)
  288. {
  289. _customVisual?.SendHandlerMessage(CustomVisualHandler.StopMessage);
  290. }
  291. private void PreciseDirtyRectsCheckboxCustomVisualChanged(object sender, RoutedEventArgs e)
  292. {
  293. _customVisual?.SendHandlerMessage(PreciseDirtyRectsCheckboxCustomVisual?.IsChecked == true
  294. ? CustomVisualHandler.UsePreciseDirtyRects
  295. : CustomVisualHandler.UseNonPreciseDirtyRects);
  296. }
  297. }
  298. public class CompositionPageColorItem
  299. {
  300. public Color Color { get; private set; }
  301. public SolidColorBrush ColorBrush
  302. {
  303. get { return new SolidColorBrush(Color); }
  304. }
  305. public String ColorHexValue
  306. {
  307. get { return Color.ToString().Substring(3).ToUpperInvariant(); }
  308. }
  309. public CompositionPageColorItem(Color color)
  310. {
  311. Color = color;
  312. }
  313. }