经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
WPF实现跳动的字符效果
来源:cnblogs  作者:czwy  时间:2023/8/11 8:37:39  对本文有异议

本文将介绍一个好玩但实际作用可能不太大的动画效果:跳动的字符。为了提高动画效果的可重用性以及调用的灵活性,通过Behavior实现跳动的字符动画。先看下效果:

image

技术要点与实现

通过TextEffectPositionStartPositionCount属性控制应用动画效果的子字符串的起始位置以及长度,同时使用TranslateTransform设置字符纵坐标的移动变换,以实现跳动的效果。主要步骤如下:

  • 在OnAttached方法中,注册Loaded事件,在Load事件中为TextBlock添加TextEffect效果,其中PositionCount设置为1,每次只跳动一个字符。
  • 添加启动动画效果的BeginEffect方法,并创建控制子字符纵向移动变换的线性动画。然后根据字符串(剔除空字符)的长度n,创建n个关键帧,每个关键帧中把PositionStart设置为要跳动的字符在字符串中的索引
  • 在开启动画属性IsEnabled=trueTextBlock内容变化时,启动动画效果

在创建关键帧设置跳动字符位置时剔除了空字符,是为了是动画效果显得连贯

  1. public class DanceCharEffectBehavior : Behavior<TextBlock>
  2. {
  3. private TextEffect _textEffect;
  4. private string _textEffectName;
  5. private TranslateTransform _translateTransform = null;
  6. private string _translateTransformName;
  7. private Storyboard _storyboard;
  8. protected override void OnAttached()
  9. {
  10. base.OnAttached();
  11. this.AssociatedObject.Loaded += AssociatedObject_Loaded;
  12. this.AssociatedObject.Unloaded += AssociatedObject_Unloaded;
  13. this.AssociatedObject.IsVisibleChanged += AssociatedObject_IsVisibleChanged;
  14. BindingOperations.SetBinding(this, DanceCharEffectBehavior.InternalTextProperty, new Binding("Text") { Source = this.AssociatedObject });
  15. }
  16. protected override void OnDetaching()
  17. {
  18. base.OnDetaching();
  19. this.AssociatedObject.Loaded -= AssociatedObject_Loaded;
  20. this.AssociatedObject.Unloaded -= AssociatedObject_Unloaded;
  21. this.AssociatedObject.IsVisibleChanged -= AssociatedObject_IsVisibleChanged;
  22. this.ClearValue(DanceCharEffectBehavior.InternalTextProperty);
  23. if (_storyboard != null)
  24. {
  25. _storyboard.Remove(this.AssociatedObject);
  26. _storyboard.Children.Clear();
  27. }
  28. if (_textEffect != null)
  29. this.AssociatedObject.TextEffects.Remove(_textEffect);
  30. }
  31. private void AssociatedObject_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
  32. {
  33. if ((bool)e.NewValue == false)
  34. {
  35. if (_storyboard != null)
  36. _storyboard.Stop(this.AssociatedObject);
  37. }
  38. else
  39. {
  40. BeginEffect(this.AssociatedObject.Text);
  41. }
  42. }
  43. private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
  44. {
  45. if (_textEffect == null)
  46. {
  47. this.AssociatedObject.TextEffects.Add(_textEffect = new TextEffect()
  48. {
  49. PositionCount = 1,
  50. Transform = _translateTransform = new TranslateTransform(),
  51. });
  52. NameScope.SetNameScope(this.AssociatedObject, new NameScope());
  53. this.AssociatedObject.RegisterName(_textEffectName = "n" + Guid.NewGuid().ToString("N"), _textEffect);
  54. this.AssociatedObject.RegisterName(_translateTransformName = "n" + Guid.NewGuid().ToString("N"), _translateTransform);
  55. if (IsEnabled)
  56. BeginEffect(this.AssociatedObject.Text);
  57. }
  58. }
  59. private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
  60. {
  61. StopEffect();
  62. }
  63. private void SetEffect(string text)
  64. {
  65. if (string.IsNullOrEmpty(text) || this.AssociatedObject.IsLoaded == false)
  66. {
  67. StopEffect();
  68. return;
  69. }
  70. BeginEffect(text);
  71. }
  72. private void StopEffect()
  73. {
  74. if (_storyboard != null)
  75. {
  76. _storyboard.Stop(this.AssociatedObject);
  77. }
  78. }
  79. private void BeginEffect(string text)
  80. {
  81. StopEffect();
  82. int textLength = text.Length;
  83. if (textLength < 1 || _translateTransformName == null || IsEnabled == false) return;
  84. if (_storyboard == null)
  85. _storyboard = new Storyboard();
  86. double duration = 0.5d;
  87. DoubleAnimation da = new DoubleAnimation();
  88. Storyboard.SetTargetName(da, _translateTransformName);
  89. Storyboard.SetTargetProperty(da, new PropertyPath(TranslateTransform.YProperty));
  90. da.From = 0d;
  91. da.To = 10d;
  92. da.Duration = TimeSpan.FromSeconds(duration / 2d);
  93. da.RepeatBehavior = RepeatBehavior.Forever;
  94. da.AutoReverse = true;
  95. char emptyChar = ' ';
  96. List<int> lsb = new List<int>();
  97. for (int i = 0; i < textLength; ++i)
  98. {
  99. if (text[i] != emptyChar)
  100. {
  101. lsb.Add(i);
  102. }
  103. }
  104. Int32AnimationUsingKeyFrames frames = new Int32AnimationUsingKeyFrames();
  105. Storyboard.SetTargetName(frames, _textEffectName);
  106. Storyboard.SetTargetProperty(frames, new PropertyPath(TextEffect.PositionStartProperty));
  107. frames.Duration = TimeSpan.FromSeconds((lsb.Count) * duration);
  108. frames.RepeatBehavior = RepeatBehavior.Forever;
  109. frames.AutoReverse = true;
  110. int ii = 0;
  111. foreach (int index in lsb)
  112. {
  113. frames.KeyFrames.Add(new DiscreteInt32KeyFrame()
  114. {
  115. Value = index,
  116. KeyTime = TimeSpan.FromSeconds(ii * duration),
  117. });
  118. ++ii;
  119. }
  120. _storyboard.Children.Add(da);
  121. _storyboard.Children.Add(frames);
  122. _storyboard.Begin(this.AssociatedObject, true);
  123. }
  124. private string InternalText
  125. {
  126. get { return (string)GetValue(InternalTextProperty); }
  127. set { SetValue(InternalTextProperty, value); }
  128. }
  129. private static readonly DependencyProperty InternalTextProperty =
  130. DependencyProperty.Register("InternalText", typeof(string), typeof(DanceCharEffectBehavior),
  131. new PropertyMetadata(OnInternalTextChanged));
  132. private static void OnInternalTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  133. {
  134. var source = d as DanceCharEffectBehavior;
  135. if (source._storyboard != null)
  136. {
  137. source._storyboard.Stop(source.AssociatedObject);
  138. source._storyboard.Children.Clear();
  139. }
  140. source.SetEffect(e.NewValue == null ? string.Empty : e.NewValue.ToString());
  141. }
  142. public bool IsEnabled
  143. {
  144. get { return (bool)GetValue(IsEnabledProperty); }
  145. set { SetValue(IsEnabledProperty, value); }
  146. }
  147. public static readonly DependencyProperty IsEnabledProperty =
  148. DependencyProperty.Register("IsEnabled", typeof(bool), typeof(DanceCharEffectBehavior), new PropertyMetadata(true, (d, e) =>
  149. {
  150. bool b = (bool)e.NewValue;
  151. var source = d as DanceCharEffectBehavior;
  152. source.SetEffect(source.InternalText);
  153. }));
  154. }

调用的时候只需要在TextBlock添加Behavior即可,代码如下

  1. <TextBlock FontSize="20" Text="Hello">
  2. <i:Interaction.Behaviors>
  3. <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  4. </i:Interaction.Behaviors>
  5. </TextBlock>

结尾

本例中还有许多可以完善的地方,比如字符跳动的幅度可以根据实际的FontSize来设置,或者增加依赖属性来控制;动画是否倒退播放,是否循环播放,以及动画的速度都可以通过增加依赖属性在调用时灵活设置。

原文链接:https://www.cnblogs.com/czwy/p/17615551.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号