相关文章推荐
悲伤的拖把  ·  一款软件无法正常打开,疑似WPF工作异常,是 ...·  3 周前    · 
高大的高山  ·  【wpf】ListView 和 ...·  1 周前    · 
气宇轩昂的春卷  ·  WPF ListView关闭选择开发者社区·  1 周前    · 
沉稳的杨桃  ·  Pyppeteer与selenium的区别及 ...·  2 年前    · 
神勇威武的红茶  ·  Oracle字符串缓冲区太小怎么处理? - ...·  2 年前    · 
听话的牛肉面  ·  关于Holder持有者类对象_databuf ...·  2 年前    · 
老实的灭火器  ·  svn switch' does not ...·  2 年前    · 
Code  ›  WPF实现跳动的字符效果开发者社区
wpf
https://cloud.tencent.com/developer/article/2349162
豁达的斑马
1 年前
czwy

WPF实现跳动的字符效果

腾讯云
开发者社区
文档 建议反馈 控制台
首页
学习
活动
专区
工具
TVP
最新优惠活动
文章/答案/技术大牛
发布
首页
学习
活动
专区
工具
TVP 最新优惠活动
返回腾讯云官网
czwy
首页
学习
活动
专区
工具
TVP 最新优惠活动
返回腾讯云官网
社区首页 > 专栏 > WPF实现跳动的字符效果

WPF实现跳动的字符效果

作者头像
czwy
发布 于 2023-10-22 15:07:42
163 0
发布 于 2023-10-22 15:07:42
举报
文章被收录于专栏: czwy的博客 czwy的博客

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

image
image

技术要点与实现

通过 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)
            _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);
            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);
 
推荐文章
悲伤的拖把  ·  一款软件无法正常打开,疑似WPF工作异常,是不是.NET框架的问题? - Microsoft Q&A
3 周前
高大的高山  ·  【wpf】ListView 和 ItemsControl 的一点区别 - 宋桓公
1 周前
气宇轩昂的春卷  ·  WPF ListView关闭选择开发者社区
1 周前
沉稳的杨桃  ·  Pyppeteer与selenium的区别及示例-腾讯云开发者社区-腾讯云
2 年前
神勇威武的红茶  ·  Oracle字符串缓冲区太小怎么处理? - 墨天轮问答
2 年前
听话的牛肉面  ·  关于Holder持有者类对象_databufferholder撖寡情_itzyjr的博客-CSDN博客
2 年前
老实的灭火器  ·  svn switch' does not support switching a working copy to a different repository-掘金
2 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号