|
本文将介绍一个好玩但实际作用可能不太大的动画效果:跳动的字符。为了提高动画效果的可重用性以及调用的灵活性,通过Behavior实现跳动的字符动画。先看下效果:
技术要点与实现
通过TextEffect的PositionStart和PositionCount属性控制应用动画效果的子字符串的起始位置以及长度,同时使用TranslateTransform设置字符纵坐标的移动变换,以实现跳动的效果。主要步骤如下:
- 在OnAttached方法中,注册Loaded事件,在Load事件中为TextBlock添加TextEffect效果,其中PositionCount设置为1,每次只跳动一个字符。
- 添加启动动画效果的BeginEffect方法,并创建控制子字符纵向移动变换的线性动画。然后根据字符串(剔除空字符)的长度n,创建n个关键帧,每个关键帧中把PositionStart设置为要跳动的字符在字符串中的索引
- 在开启动画属性IsEnabled=true和TextBlock内容变化时,启动动画效果
在创建关键帧设置跳动字符位置时剔除了空字符,是为了是动画效果显得连贯
- public class DanceCharEffectBehavior : Behavior<TextBlock>
- {
- private TextEffect _textEffect;
- private string _textEffectName;
- private TranslateTransform _translateTransform = null;
- private string _translateTransformName;
- private Storyboard _storyboard;
- protected override void OnAttached()
- {
- base.OnAttached();
- this.AssociatedObject.Loaded += AssociatedObject_Loaded;
- this.AssociatedObject.Unloaded += AssociatedObject_Unloaded;
- this.AssociatedObject.IsVisibleChanged += AssociatedObject_IsVisibleChanged;
- BindingOperations.SetBinding(this, DanceCharEffectBehavior.InternalTextProperty, new Binding("Text") { Source = this.AssociatedObject });
- }
- protected override void OnDetaching()
- {
- base.OnDetaching();
- this.AssociatedObject.Loaded -= AssociatedObject_Loaded;
- this.AssociatedObject.Unloaded -= AssociatedObject_Unloaded;
- this.AssociatedObject.IsVisibleChanged -= AssociatedObject_IsVisibleChanged;
- this.ClearValue(DanceCharEffectBehavior.InternalTextProperty);
- if (_storyboard != null)
- {
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>_storyboard.Remove(this.AssociatedObject);
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>_storyboard.Children.Clear();
- }
- if (_textEffect != null)
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>this.AssociatedObject.TextEffects.Remove(_textEffect);
- }
- private void AssociatedObject_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
- {
- if ((bool)e.NewValue == false)
- {
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>if (_storyboard != null)
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock> _storyboard.Stop(this.AssociatedObject);
- }
- else
- {
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>BeginEffect(this.AssociatedObject.Text);
- }
- }
- private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
- {
- if (_textEffect == null)
- {
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>this.AssociatedObject.TextEffects.Add(_textEffect = new TextEffect()
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>{
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock> PositionCount = 1,
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock> Transform = _translateTransform = new TranslateTransform(),
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>});
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>NameScope.SetNameScope(this.AssociatedObject, new NameScope());
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>this.AssociatedObject.RegisterName(_textEffectName = "n" + Guid.NewGuid().ToString("N"), _textEffect);
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>this.AssociatedObject.RegisterName(_translateTransformName = "n" + Guid.NewGuid().ToString("N"), _translateTransform);
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>if (IsEnabled)
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock> BeginEffect(this.AssociatedObject.Text);
- }
- }
- private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
- {
- StopEffect();
- }
- private void SetEffect(string text)
- {
- if (string.IsNullOrEmpty(text) || this.AssociatedObject.IsLoaded == false)
- {
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>StopEffect();
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>return;
- }
- BeginEffect(text);
- }
- private void StopEffect()
- {
- if (_storyboard != null)
- {
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>_storyboard.Stop(this.AssociatedObject);
- }
- }
- private void BeginEffect(string text)
- {
- StopEffect();
- int textLength = text.Length;
- if (textLength < 1 || _translateTransformName == null || IsEnabled == false) return;
- if (_storyboard == null)
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>_storyboard = new Storyboard();
- double duration = 0.5d;
- DoubleAnimation da = new DoubleAnimation();
- Storyboard.SetTargetName(da, _translateTransformName);
- Storyboard.SetTargetProperty(da, new PropertyPath(TranslateTransform.YProperty));
- da.From = 0d;
- da.To = 10d;
- da.Duration = TimeSpan.FromSeconds(duration / 2d);
- da.RepeatBehavior = RepeatBehavior.Forever;
- da.AutoReverse = true;
- char emptyChar = ' ';
- List<int> lsb = new List<int>();
- for (int i = 0; i < textLength; ++i)
- {
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>if (text[i] != emptyChar)
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>{
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock> lsb.Add(i);
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>}
- }
- Int32AnimationUsingKeyFrames frames = new Int32AnimationUsingKeyFrames();
- Storyboard.SetTargetName(frames, _textEffectName);
- Storyboard.SetTargetProperty(frames, new PropertyPath(TextEffect.PositionStartProperty));
- frames.Duration = TimeSpan.FromSeconds((lsb.Count) * duration);
- frames.RepeatBehavior = RepeatBehavior.Forever;
- frames.AutoReverse = true;
- int ii = 0;
- foreach (int index in lsb)
- {
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>frames.KeyFrames.Add(new DiscreteInt32KeyFrame()
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>{
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock> Value = index,
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock> KeyTime = TimeSpan.FromSeconds(ii * duration),
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>});
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>++ii;
- }
- _storyboard.Children.Add(da);
- _storyboard.Children.Add(frames);
- _storyboard.Begin(this.AssociatedObject, true);
- }
- private string InternalText
- {
- get { return (string)GetValue(InternalTextProperty); }
- set { SetValue(InternalTextProperty, value); }
- }
- private static readonly DependencyProperty InternalTextProperty =
- DependencyProperty.Register("InternalText", typeof(string), typeof(DanceCharEffectBehavior),
- new PropertyMetadata(OnInternalTextChanged));
- private static void OnInternalTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- var source = d as DanceCharEffectBehavior;
- if (source._storyboard != null)
- {
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>source._storyboard.Stop(source.AssociatedObject);
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>source._storyboard.Children.Clear();
- }
- source.SetEffect(e.NewValue == null ? string.Empty : e.NewValue.ToString());
- }
- public bool IsEnabled
- {
- get { return (bool)GetValue(IsEnabledProperty); }
- set { SetValue(IsEnabledProperty, value); }
- }
- public static readonly DependencyProperty IsEnabledProperty =
- DependencyProperty.Register("IsEnabled", typeof(bool), typeof(DanceCharEffectBehavior), new PropertyMetadata(true, (d, e) =>
- {
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>bool b = (bool)e.NewValue;
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>var source = d as DanceCharEffectBehavior;
- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>source.SetEffect(source.InternalText);
- }));
- }
复制代码 调用的时候只需要在TextBlock添加Behavior即可,代码如下- <TextBlock FontSize="20" Text="Hello">
- <i:Interaction.Behaviors>
- <local:DanceCharEffectBehavior x:Name="titleEffect" IsEnabled="True" />
- </i:Interaction.Behaviors>
- </TextBlock>
复制代码 结尾
本例中还有许多可以完善的地方,比如字符跳动的幅度可以根据实际的FontSize来设置,或者增加依赖属性来控制;动画是否倒退播放,是否循环播放,以及动画的速度都可以通过增加依赖属性在调用时灵活设置。
来源:https://www.cnblogs.com/czwy/archive/2023/08/09/17615551.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|