• WPF 文本动画 文字BaseLine 字体 行高计算说明


    1.摘要

    在wpf里,显示文字我们一般用textblock或者label控件,而有时候需要显示一些文本滚动效果,比如:Led屏幕,文字自动滚屏。

    这时候如果是wpf客户端的话,用textblock的时候文字绘制的效率很慢,且画面会卡顿现象,为了解决这个问题,本文将对文本显示及文本动画进行优化,达到流畅效果。

    2.自定义简单文本控件

    普通动画,如doubleAnimation或者使用计时器然后改变textblock的offsetx,这两种方式本质是定时刷新,而每次wpf的textblock的位置改变,都会出发render事件,render事件内部进行了一些列操作,这些操作比如字体计算,字体布局等很消耗ui性能,因此造成了卡顿现象。所以,我们自定义简单的text显示控件,来替代复杂的原生文本控件。

    为了显示我们需要继承UIElement类,但是我们还要控制文本的移动和位置(因为本文是通过rendertransform控制的),所以基类我们选择了frameworkElement,如下图

    public class RenderedTextControl : FrameworkElement

    控件有了后,想要显示,需要实现两个重写,即:

    •  protected override void OnRender(DrawingContext drawingContext)
    • protected override Size MeasureOverride(Size constraint)

    顾名思义,OnRender里面需要对DrawingContext进行操作,进行绘制。MeasureOverride是计算控件的大小。

    在绘制文本的时候,为了能够精确控制文本位置,我们以文本中间点为文本基准点。因为显示文本的时候大部分是居中显示的。

    这里就需要提一下wpf中的文本几个点了:FontSize、FontFamily.Baseline、FontFamily.LineSpacing。如图

    • 默认文本是基线左下角点为0点。
    • f到g之间表示文本的显示区域大小,比如等于一个grid或一个window的高度
    • 正常文本大小:ad,(wpf是点坐标,计算文本大小时候不直接用FontSize)
    • 文本的基线BaseLine:ac=FongSize*BaseLine,distance between the baseline and the character cell top.(https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.fontfamily.baseline?view=netframework-4.6.1)
    • 文本的线间隔lineSpacing:ab+cd=FontSize*LineSpacing
    • 文本中线:e

    因此为了将文本的初始坐标点(左下角,即c的位置)移动到中间,需要将文本向上移动ec的距离(中线到基线的距离),之后再移动到fg之间(居中)即可。

    计算文本偏移量代码如下,:

     private Point GetTextLocation()
            {
                //基线距离=baseline
                var baseline = _formattedText.Baseline;
    
                //基线剧中,移动量1=-(x-0.5*行高)
                var offset1 = -(baseline - 0.5 * LineHeight);
                //中线 到 基线的距离=移动量2=字体大小*字体基线-0.5*字体大小*字体行距
                var offset2 = FontSize * _typeface.FontFamily.Baseline -
                              0.5 * FontSize * _typeface.FontFamily.LineSpacing;
                //总移动量=移动量1+移动量2
                var textLocation = new Point(0, offset1 + offset2);
                textLocation.X += DrawOffsetX;
                textLocation.Y += DrawOffsetY;
                return textLocation;
            }

    有了偏移量,绘制代码就简单了:

    protected override void OnRender(DrawingContext drawingContext)
            {
                if (!string.IsNullOrEmpty(Text))
                {
                    var textLocation = GetTextLocation();
                    drawingContext.DrawText(_formattedText, textLocation);
                    //   InvalidateMeasure();
                }
            }

    计算控件大小代码也很简单

    protected override Size MeasureOverride(Size constraint)
            {
                if (_formattedText != null)
                {
                    var wid = _formattedText.WidthIncludingTrailingWhitespace;
    
                    return new Size(wid, _formattedText.Height);
                }
    
                return new Size();
            }

    3.帧动画

    • 使用CompositionTarget.Rendering += CompositionTarget_Rendering;,此方法可以在界面更新之前进行处理文本,这样就保证了文本的刷新跟ui刷新的同步问题,经测试刷新率能达到60帧以上。
    • 为了形成一个完整的动画(比如只有一行字),我们需要将文本乘以2(这样就有了两行字,能形成首和尾)
    • 为了取得不足一段文字,我们将文字跟整行长度取余数。
    • 动画连续,需要在端点进行移动量重置
    • 动画停顿,需要在时间上进行判断
    • 整个文本,放在List<string>格式里面处理,便于添加及查找(如一行数据代表一项或者一组数据代表一项,多个项就是列表)

    4.效果及下载源码

    效果图如下:

     源码地址:https://files.cnblogs.com/files/lizhijian/2020-9-7-TextPlayer.rar

  • 相关阅读:
    passwd: Have exhausted maximum number of retries for service
    将单个文件上传到多机器工具
    leetcode-Jump game II
    LeetCode--Combination Sum --ZZ
    Leetcode- Find Minimum in Rotated Sorted Array-ZZ
    leetcode-permutation sequence
    leetcode-next permutation
    LeetCode-Subsets ZZ
    leetcode-Restore IP Addresses-ZZ
    leetcode-palindrome partitioning-ZZ
  • 原文地址:https://www.cnblogs.com/congqiandehoulai/p/13629757.html
Copyright © 2020-2023  润新知