FlipNumber.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. using System;
  2. using System.ComponentModel;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Media;
  6. using System.Windows.Media.Animation;
  7. using System.Windows.Media.Media3D;
  8. using HandyControl.Data;
  9. namespace HandyControl.Controls;
  10. public class FlipNumber : Viewport3D
  11. {
  12. private bool _isLoaded;
  13. private TextBlock _page1TextDown;
  14. private TextBlock _page2TextUp;
  15. private TextBlock _page2TextDown;
  16. private TextBlock _page3TextUp;
  17. private ContainerUIElement3D _page1;
  18. private ContainerUIElement3D _page2;
  19. private ContainerUIElement3D _page3;
  20. private ContainerUIElement3D _content;
  21. private readonly AxisAngleRotation3D _pageRotation3D;
  22. private readonly DoubleAnimation _animation;
  23. private bool _isAnimating;
  24. public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
  25. nameof(CornerRadius), typeof(CornerRadius), typeof(FlipNumber), new PropertyMetadata(new CornerRadius(4)));
  26. public CornerRadius CornerRadius
  27. {
  28. get => (CornerRadius) GetValue(CornerRadiusProperty);
  29. set => SetValue(CornerRadiusProperty, value);
  30. }
  31. public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register(
  32. nameof(Background), typeof(Brush), typeof(FlipNumber), new PropertyMetadata(default(Brush)));
  33. public Brush Background
  34. {
  35. get => (Brush) GetValue(BackgroundProperty);
  36. set => SetValue(BackgroundProperty, value);
  37. }
  38. public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register(
  39. nameof(Foreground), typeof(Brush), typeof(FlipNumber), new PropertyMetadata(default(Brush)));
  40. public Brush Foreground
  41. {
  42. get => (Brush) GetValue(ForegroundProperty);
  43. set => SetValue(ForegroundProperty, value);
  44. }
  45. public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register(
  46. nameof(FontSize), typeof(double), typeof(FlipNumber), new PropertyMetadata(70.0));
  47. [TypeConverter(typeof(FontSizeConverter))]
  48. public double FontSize
  49. {
  50. get => (double) GetValue(FontSizeProperty);
  51. set => SetValue(FontSizeProperty, value);
  52. }
  53. public static readonly DependencyProperty NumberProperty = DependencyProperty.Register(
  54. nameof(Number), typeof(int), typeof(FlipNumber), new PropertyMetadata(ValueBoxes.Int0Box, OnNumberChanged));
  55. private static void OnNumberChanged(DependencyObject s, DependencyPropertyChangedEventArgs e) =>
  56. ((FlipNumber) s).OnNumberChanged();
  57. public int Number
  58. {
  59. get => (int) GetValue(NumberProperty);
  60. set => SetValue(NumberProperty, value);
  61. }
  62. public FlipNumber()
  63. {
  64. var visual3D = new ModelVisual3D
  65. {
  66. Content = new DirectionalLight()
  67. };
  68. Children.Add(visual3D);
  69. _pageRotation3D = new AxisAngleRotation3D
  70. {
  71. Angle = 0,
  72. Axis = new Vector3D(1, 0, 0)
  73. };
  74. _animation = new DoubleAnimation(0, 180, new Duration(TimeSpan.FromSeconds(0.8)))
  75. {
  76. FillBehavior = FillBehavior.Stop
  77. };
  78. _animation.Completed += Animation_Completed;
  79. Loaded += (s, e) =>
  80. {
  81. if (_isLoaded) return;
  82. _isLoaded = true;
  83. InitNumber();
  84. var transform3D = new RotateTransform3D(_pageRotation3D);
  85. _page2.Transform = transform3D;
  86. };
  87. }
  88. private void Animation_Completed(object sender, EventArgs e)
  89. {
  90. _isAnimating = false;
  91. UpdateNumber();
  92. }
  93. private void InitNumber()
  94. {
  95. _page1 = new ContainerUIElement3D();
  96. var num1 = Number > 8 ? 0 : Number + 1;
  97. var page1NumDown = CreateNumber(num1, false, out _page1TextDown);
  98. _page1.Children.Add(page1NumDown);
  99. _page2 = new ContainerUIElement3D();
  100. var page2NumUp = CreateNumber(num1, true, out _page2TextUp);
  101. var page2NumDown = CreateNumber(Number, false, out _page2TextDown);
  102. _page2.Children.Add(page2NumUp);
  103. _page2.Children.Add(page2NumDown);
  104. _page3 = new ContainerUIElement3D();
  105. var page3NumUp = CreateNumber(Number, true, out _page3TextUp);
  106. _page3.Children.Add(page3NumUp);
  107. var transform3D = new RotateTransform3D(new AxisAngleRotation3D
  108. {
  109. Angle = 180,
  110. Axis = new Vector3D(1, 0, 0)
  111. });
  112. _page3.Transform = transform3D;
  113. _content = new ContainerUIElement3D
  114. {
  115. Children =
  116. {
  117. _page1,
  118. _page2,
  119. _page3
  120. }
  121. };
  122. Children.Add(_content);
  123. }
  124. private bool CheckNull() => _page1TextDown != null && _page2TextUp != null && _page2TextDown != null && _page3TextUp != null;
  125. private void OnNumberChanged()
  126. {
  127. if (!CheckNull()) return;
  128. InitNewNumber();
  129. if (_isAnimating)
  130. {
  131. _isAnimating = false;
  132. UpdateNumber();
  133. return;
  134. }
  135. _isAnimating = true;
  136. _pageRotation3D.BeginAnimation(AxisAngleRotation3D.AngleProperty, _animation);
  137. }
  138. private void InitNewNumber()
  139. {
  140. var num1 = Number.ToString();
  141. _page1TextDown.Text = num1;
  142. _page2TextUp.Text = num1;
  143. }
  144. private void UpdateNumber()
  145. {
  146. _pageRotation3D.BeginAnimation(AxisAngleRotation3D.AngleProperty, null);
  147. _pageRotation3D.Angle = 0;
  148. var num = Number.ToString();
  149. _page2TextDown.Text = num;
  150. _page3TextUp.Text = num;
  151. _isAnimating = false;
  152. }
  153. private Viewport2DVisual3D CreateNumber(int num, bool isUp, out TextBlock textBlock)
  154. {
  155. int flag;
  156. var rotateTransform = new RotateTransform();
  157. if (isUp)
  158. {
  159. flag = -1;
  160. rotateTransform.Angle = 180;
  161. }
  162. else
  163. {
  164. flag = 1;
  165. }
  166. var halfWidth = ActualWidth / 2;
  167. var quarterWidth = ActualWidth / 4;
  168. var quarterHeight = ActualHeight / 4;
  169. var meMaterial = new DiffuseMaterial();
  170. Viewport2DVisual3D.SetIsVisualHostMaterial(meMaterial, true);
  171. textBlock = new TextBlock
  172. {
  173. RenderTransformOrigin = new Point(0.5, 0.5),
  174. Foreground = Foreground,
  175. FontSize = FontSize,
  176. VerticalAlignment = VerticalAlignment.Center,
  177. HorizontalAlignment = HorizontalAlignment.Center,
  178. TextAlignment = TextAlignment.Center,
  179. Text = num.ToString(),
  180. RenderTransform = rotateTransform,
  181. Margin = new Thickness(0, 0, 0, -quarterHeight),
  182. FontFamily = new FontFamily("Consolas")
  183. };
  184. var border = new Border
  185. {
  186. ClipToBounds = true,
  187. CornerRadius = new CornerRadius(CornerRadius.TopLeft, CornerRadius.TopRight, 0, 0),
  188. Background = Background,
  189. Width = halfWidth,
  190. Height = quarterHeight,
  191. Child = textBlock
  192. };
  193. var positions = new Point3DCollection
  194. {
  195. new(-quarterWidth * flag, quarterHeight, 0),
  196. new(-quarterWidth * flag, 0, 0),
  197. new(quarterWidth * flag, 0, 0),
  198. new(quarterWidth * flag, quarterHeight, 0)
  199. };
  200. var triangleIndices = new Int32Collection
  201. {
  202. 0,
  203. 1,
  204. 2,
  205. 0,
  206. 2,
  207. 3
  208. };
  209. var textureCoordinates = new PointCollection
  210. {
  211. new(0, 0),
  212. new(0, 1),
  213. new(1, 1),
  214. new(1, 0)
  215. };
  216. var geometry3D = new MeshGeometry3D
  217. {
  218. Positions = positions,
  219. TriangleIndices = triangleIndices,
  220. TextureCoordinates = textureCoordinates
  221. };
  222. var child = new Viewport2DVisual3D
  223. {
  224. Geometry = geometry3D,
  225. Visual = border,
  226. Material = meMaterial
  227. };
  228. return child;
  229. }
  230. }