翼度科技»论坛 编程开发 .net 查看内容

WPF实现跳动的字符效果

3

主题

3

帖子

9

积分

新手上路

Rank: 1

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

技术要点与实现

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

  • 在OnAttached方法中,注册Loaded事件,在Load事件中为TextBlock添加TextEffect效果,其中PositionCount设置为1,每次只跳动一个字符。
  • 添加启动动画效果的BeginEffect方法,并创建控制子字符纵向移动变换的线性动画。然后根据字符串(剔除空字符)的长度n,创建n个关键帧,每个关键帧中把PositionStart设置为要跳动的字符在字符串中的索引
  • 在开启动画属性IsEnabled=true和TextBlock内容变化时,启动动画效果
在创建关键帧设置跳动字符位置时剔除了空字符,是为了是动画效果显得连贯
  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. <TextBlock FontSize="20" Text="Hello">
  26.     <i:Interaction.Behaviors>
  27.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  28.     </i:Interaction.Behaviors>
  29. </TextBlock>_storyboard.Remove(this.AssociatedObject);
  30. <TextBlock FontSize="20" Text="Hello">
  31.     <i:Interaction.Behaviors>
  32.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  33.     </i:Interaction.Behaviors>
  34. </TextBlock>_storyboard.Children.Clear();
  35.             }
  36.             if (_textEffect != null)
  37. <TextBlock FontSize="20" Text="Hello">
  38.     <i:Interaction.Behaviors>
  39.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  40.     </i:Interaction.Behaviors>
  41. </TextBlock>this.AssociatedObject.TextEffects.Remove(_textEffect);
  42.         }
  43.         private void AssociatedObject_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
  44.         {
  45.             if ((bool)e.NewValue == false)
  46.             {
  47. <TextBlock FontSize="20" Text="Hello">
  48.     <i:Interaction.Behaviors>
  49.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  50.     </i:Interaction.Behaviors>
  51. </TextBlock>if (_storyboard != null)
  52. <TextBlock FontSize="20" Text="Hello">
  53.     <i:Interaction.Behaviors>
  54.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  55.     </i:Interaction.Behaviors>
  56. </TextBlock>    _storyboard.Stop(this.AssociatedObject);
  57.             }
  58.             else
  59.             {
  60. <TextBlock FontSize="20" Text="Hello">
  61.     <i:Interaction.Behaviors>
  62.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  63.     </i:Interaction.Behaviors>
  64. </TextBlock>BeginEffect(this.AssociatedObject.Text);
  65.             }
  66.         }
  67.         private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
  68.         {
  69.             if (_textEffect == null)
  70.             {
  71. <TextBlock FontSize="20" Text="Hello">
  72.     <i:Interaction.Behaviors>
  73.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  74.     </i:Interaction.Behaviors>
  75. </TextBlock>this.AssociatedObject.TextEffects.Add(_textEffect = new TextEffect()
  76. <TextBlock FontSize="20" Text="Hello">
  77.     <i:Interaction.Behaviors>
  78.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  79.     </i:Interaction.Behaviors>
  80. </TextBlock>{
  81. <TextBlock FontSize="20" Text="Hello">
  82.     <i:Interaction.Behaviors>
  83.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  84.     </i:Interaction.Behaviors>
  85. </TextBlock>    PositionCount = 1,
  86. <TextBlock FontSize="20" Text="Hello">
  87.     <i:Interaction.Behaviors>
  88.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  89.     </i:Interaction.Behaviors>
  90. </TextBlock>    Transform = _translateTransform = new TranslateTransform(),
  91. <TextBlock FontSize="20" Text="Hello">
  92.     <i:Interaction.Behaviors>
  93.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  94.     </i:Interaction.Behaviors>
  95. </TextBlock>});
  96. <TextBlock FontSize="20" Text="Hello">
  97.     <i:Interaction.Behaviors>
  98.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  99.     </i:Interaction.Behaviors>
  100. </TextBlock>NameScope.SetNameScope(this.AssociatedObject, new NameScope());
  101. <TextBlock FontSize="20" Text="Hello">
  102.     <i:Interaction.Behaviors>
  103.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  104.     </i:Interaction.Behaviors>
  105. </TextBlock>this.AssociatedObject.RegisterName(_textEffectName = "n" + Guid.NewGuid().ToString("N"), _textEffect);
  106. <TextBlock FontSize="20" Text="Hello">
  107.     <i:Interaction.Behaviors>
  108.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  109.     </i:Interaction.Behaviors>
  110. </TextBlock>this.AssociatedObject.RegisterName(_translateTransformName = "n" + Guid.NewGuid().ToString("N"), _translateTransform);
  111. <TextBlock FontSize="20" Text="Hello">
  112.     <i:Interaction.Behaviors>
  113.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  114.     </i:Interaction.Behaviors>
  115. </TextBlock>if (IsEnabled)
  116. <TextBlock FontSize="20" Text="Hello">
  117.     <i:Interaction.Behaviors>
  118.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  119.     </i:Interaction.Behaviors>
  120. </TextBlock>    BeginEffect(this.AssociatedObject.Text);
  121.             }
  122.         }
  123.         private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
  124.         {
  125.             StopEffect();
  126.         }
  127.         private void SetEffect(string text)
  128.         {
  129.             if (string.IsNullOrEmpty(text) || this.AssociatedObject.IsLoaded == false)
  130.             {
  131. <TextBlock FontSize="20" Text="Hello">
  132.     <i:Interaction.Behaviors>
  133.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  134.     </i:Interaction.Behaviors>
  135. </TextBlock>StopEffect();
  136. <TextBlock FontSize="20" Text="Hello">
  137.     <i:Interaction.Behaviors>
  138.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  139.     </i:Interaction.Behaviors>
  140. </TextBlock>return;
  141.             }
  142.             BeginEffect(text);
  143.         }
  144.         private void StopEffect()
  145.         {
  146.             if (_storyboard != null)
  147.             {
  148. <TextBlock FontSize="20" Text="Hello">
  149.     <i:Interaction.Behaviors>
  150.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  151.     </i:Interaction.Behaviors>
  152. </TextBlock>_storyboard.Stop(this.AssociatedObject);
  153.             }
  154.         }
  155.         private void BeginEffect(string text)
  156.         {
  157.             StopEffect();
  158.             int textLength = text.Length;
  159.             if (textLength < 1 || _translateTransformName == null || IsEnabled == false) return;
  160.             if (_storyboard == null)
  161. <TextBlock FontSize="20" Text="Hello">
  162.     <i:Interaction.Behaviors>
  163.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  164.     </i:Interaction.Behaviors>
  165. </TextBlock>_storyboard = new Storyboard();
  166.             double duration = 0.5d;
  167.             DoubleAnimation da = new DoubleAnimation();
  168.             Storyboard.SetTargetName(da, _translateTransformName);
  169.             Storyboard.SetTargetProperty(da, new PropertyPath(TranslateTransform.YProperty));
  170.             da.From = 0d;
  171.             da.To = 10d;
  172.             da.Duration = TimeSpan.FromSeconds(duration / 2d);
  173.             da.RepeatBehavior = RepeatBehavior.Forever;
  174.             da.AutoReverse = true;
  175.             char emptyChar = ' ';
  176.             List<int> lsb = new List<int>();
  177.             for (int i = 0; i < textLength; ++i)
  178.             {
  179. <TextBlock FontSize="20" Text="Hello">
  180.     <i:Interaction.Behaviors>
  181.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  182.     </i:Interaction.Behaviors>
  183. </TextBlock>if (text[i] != emptyChar)
  184. <TextBlock FontSize="20" Text="Hello">
  185.     <i:Interaction.Behaviors>
  186.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  187.     </i:Interaction.Behaviors>
  188. </TextBlock>{
  189. <TextBlock FontSize="20" Text="Hello">
  190.     <i:Interaction.Behaviors>
  191.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  192.     </i:Interaction.Behaviors>
  193. </TextBlock>    lsb.Add(i);
  194. <TextBlock FontSize="20" Text="Hello">
  195.     <i:Interaction.Behaviors>
  196.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  197.     </i:Interaction.Behaviors>
  198. </TextBlock>}
  199.             }
  200.             Int32AnimationUsingKeyFrames frames = new Int32AnimationUsingKeyFrames();
  201.             Storyboard.SetTargetName(frames, _textEffectName);
  202.             Storyboard.SetTargetProperty(frames, new PropertyPath(TextEffect.PositionStartProperty));
  203.             frames.Duration = TimeSpan.FromSeconds((lsb.Count) * duration);
  204.             frames.RepeatBehavior = RepeatBehavior.Forever;
  205.             frames.AutoReverse = true;
  206.             int ii = 0;
  207.             foreach (int index in lsb)
  208.             {
  209. <TextBlock FontSize="20" Text="Hello">
  210.     <i:Interaction.Behaviors>
  211.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  212.     </i:Interaction.Behaviors>
  213. </TextBlock>frames.KeyFrames.Add(new DiscreteInt32KeyFrame()
  214. <TextBlock FontSize="20" Text="Hello">
  215.     <i:Interaction.Behaviors>
  216.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  217.     </i:Interaction.Behaviors>
  218. </TextBlock>{
  219. <TextBlock FontSize="20" Text="Hello">
  220.     <i:Interaction.Behaviors>
  221.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  222.     </i:Interaction.Behaviors>
  223. </TextBlock>    Value = index,
  224. <TextBlock FontSize="20" Text="Hello">
  225.     <i:Interaction.Behaviors>
  226.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  227.     </i:Interaction.Behaviors>
  228. </TextBlock>    KeyTime = TimeSpan.FromSeconds(ii * duration),
  229. <TextBlock FontSize="20" Text="Hello">
  230.     <i:Interaction.Behaviors>
  231.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  232.     </i:Interaction.Behaviors>
  233. </TextBlock>});
  234. <TextBlock FontSize="20" Text="Hello">
  235.     <i:Interaction.Behaviors>
  236.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  237.     </i:Interaction.Behaviors>
  238. </TextBlock>++ii;
  239.             }
  240.             _storyboard.Children.Add(da);
  241.             _storyboard.Children.Add(frames);
  242.             _storyboard.Begin(this.AssociatedObject, true);
  243.         }
  244.         private string InternalText
  245.         {
  246.             get { return (string)GetValue(InternalTextProperty); }
  247.             set { SetValue(InternalTextProperty, value); }
  248.         }
  249.         private static readonly DependencyProperty InternalTextProperty =
  250.         DependencyProperty.Register("InternalText", typeof(string), typeof(DanceCharEffectBehavior),
  251.         new PropertyMetadata(OnInternalTextChanged));
  252.         private static void OnInternalTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  253.         {
  254.             var source = d as DanceCharEffectBehavior;
  255.             if (source._storyboard != null)
  256.             {
  257. <TextBlock FontSize="20" Text="Hello">
  258.     <i:Interaction.Behaviors>
  259.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  260.     </i:Interaction.Behaviors>
  261. </TextBlock>source._storyboard.Stop(source.AssociatedObject);
  262. <TextBlock FontSize="20" Text="Hello">
  263.     <i:Interaction.Behaviors>
  264.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  265.     </i:Interaction.Behaviors>
  266. </TextBlock>source._storyboard.Children.Clear();
  267.             }
  268.             source.SetEffect(e.NewValue == null ? string.Empty : e.NewValue.ToString());
  269.         }
  270.         public bool IsEnabled
  271.         {
  272.             get { return (bool)GetValue(IsEnabledProperty); }
  273.             set { SetValue(IsEnabledProperty, value); }
  274.         }
  275.         public static readonly DependencyProperty IsEnabledProperty =
  276.             DependencyProperty.Register("IsEnabled", typeof(bool), typeof(DanceCharEffectBehavior), new PropertyMetadata(true, (d, e) =>
  277.             {
  278. <TextBlock FontSize="20" Text="Hello">
  279.     <i:Interaction.Behaviors>
  280.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  281.     </i:Interaction.Behaviors>
  282. </TextBlock>bool b = (bool)e.NewValue;
  283. <TextBlock FontSize="20" Text="Hello">
  284.     <i:Interaction.Behaviors>
  285.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  286.     </i:Interaction.Behaviors>
  287. </TextBlock>var source = d as DanceCharEffectBehavior;
  288. <TextBlock FontSize="20" Text="Hello">
  289.     <i:Interaction.Behaviors>
  290.         <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
  291.     </i:Interaction.Behaviors>
  292. </TextBlock>source.SetEffect(source.InternalText);
  293.             }));
  294.     }
复制代码
调用的时候只需要在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/archive/2023/08/09/17615551.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具