基于WPF实现跳动的字符效果

 更新时间:2023年08月10日 08:43:18   作者:czwy  
这篇文章主要和大家介绍一个好玩但实际作用可能不太大的动画效果:跳动的字符,本文将利用WPF实现这一效果,感兴趣的小伙伴可以学习一下

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

技术要点与实现

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

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

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

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)
            {
                _storyboard.Remove(this.AssociatedObject);
                _storyboard.Children.Clear();
            }
            if (_textEffect != null)
                this.AssociatedObject.TextEffects.Remove(_textEffect);
        }
        private void AssociatedObject_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue == false)
            {
                if (_storyboard != null)
                    _storyboard.Stop(this.AssociatedObject);
            }
            else
            {
                BeginEffect(this.AssociatedObject.Text);
            }
        }
        private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
        {
            if (_textEffect == null)
            {
                this.AssociatedObject.TextEffects.Add(_textEffect = new TextEffect()
                {
                    PositionCount = 1,
                    Transform = _translateTransform = new TranslateTransform(),
                });
                NameScope.SetNameScope(this.AssociatedObject, new NameScope());
                this.AssociatedObject.RegisterName(_textEffectName = "n" + Guid.NewGuid().ToString("N"), _textEffect);
                this.AssociatedObject.RegisterName(_translateTransformName = "n" + Guid.NewGuid().ToString("N"), _translateTransform);
                if (IsEnabled)
                    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)
            {
                StopEffect();
                return;
            }
            BeginEffect(text);
        }
        private void StopEffect()
        {
            if (_storyboard != null)
            {
                _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)
                _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)
            {
                if (text[i] != emptyChar)
                {
                    lsb.Add(i);
                }
            }
            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)
            {
                frames.KeyFrames.Add(new DiscreteInt32KeyFrame()
                {
                    Value = index,
                    KeyTime = TimeSpan.FromSeconds(ii * duration),
                });
                ++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)
            {
                source._storyboard.Stop(source.AssociatedObject);
                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) =>
            {
                bool b = (bool)e.NewValue;
                var source = d as DanceCharEffectBehavior;
                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来设置,或者增加依赖属性来控制;动画是否倒退播放,是否循环播放,以及动画的速度都可以通过增加依赖属性在调用时灵活设置。

到此这篇关于基于WPF实现跳动的字符效果的文章就介绍到这了,更多相关WPF字符跳动效果内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • WPF使用DrawingContext实现绘制刻度条

    WPF使用DrawingContext实现绘制刻度条

    这篇文章主要为大家详细介绍了如何利用WPF DrawingContext实现绘制刻度条,文中的示例代码讲解详细,对我们学习或工作有一定帮助,感兴趣的小伙伴可以了解一下
    2022-09-09
  • 使用C#判断文件是否为图片的多种方法

    使用C#判断文件是否为图片的多种方法

    在开发中,我们常需要判断一个文件是否为图片,这看似简单,但要实现得既高效又严谨,需要根据不同场景选择合适的方法,本文记录了几种常见的判断方式,并推荐使用 SixLabors.ImageSharp 来替代已被淘汰的 System.Drawing,需要的朋友可以参考下
    2024-10-10
  • C#在MEF框架中实现延迟加载部件

    C#在MEF框架中实现延迟加载部件

    这篇文章介绍了C#在MEF框架中实现延迟加载部件的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • C#(4.0)不常见的语法

    C#(4.0)不常见的语法

    今天工作遇到4.0新增语法 不解 请教 遂知,简单的整理下,需要的朋友可以参考下
    2012-02-02
  • C# Winform实现波浪滚动效果

    C# Winform实现波浪滚动效果

    这篇文章主要为大家详细介绍了C# Winform实现波浪滚动效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • Unity接入百度AI实现货币识别

    Unity接入百度AI实现货币识别

    本文主要介绍了在Unity中接入百度AI,从而实现货币识别,可以返回货币的名称、代码、面值、年份信息等,感兴趣的可以跟随小编学习一下
    2022-01-01
  • C# WinForm窗体编程中处理数字的正确操作方法

    C# WinForm窗体编程中处理数字的正确操作方法

    这篇文章主要介绍了C# WinForm窗体编程中处理数字的正确操作方法,本文给出了正确示例,并解释了为什么要这么做,需要的朋友可以参考下
    2014-08-08
  • 基于Unity制作一个简易的计算器

    基于Unity制作一个简易的计算器

    今天主要和大家分享如何使用Unity制作计算器,难度中等,可以用来学习,或者当成其他项目的小组件导入。当然,也可以导出来,发布到网页端,来做一个嵌入式工具也可以。感兴趣的可以跟随小编学习一下
    2022-03-03
  • 通过C#实现在Excel单元格中写入文本、或数值

    通过C#实现在Excel单元格中写入文本、或数值

    在商业、学术和日常生活中,Excel 的使用极为普遍,本文将详细介绍如何使用免费.NET库将数据写入到 Excel 中,包括文本、数值、数组、和DataTable数据的输入,需要的朋友可以参考下
    2024-07-07
  • C# 调用腾讯即时通信 IM的示例

    C# 调用腾讯即时通信 IM的示例

    这篇文章主要介绍了C# 调用腾讯即时通信 IM的示例,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下
    2020-11-11

最新评论