WPF: Animation is not smooth

2019-02-25 14:38发布

问题:

I am animating a TextBlock. In 60 seconds, it increases FontSize from 8pt to 200pt. Everything is working fine, except that my animation is moving up and down a bit as the text grows. Why is this happening and is it possible to avoid this?

I have a very simple XAML file:

<Window x:Class="Timer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Width="800" 
        Height="500"
        Title="MainWindow" 
        Loaded="Window_Loaded">

    <Grid>

        <TextBlock 
            Name="TimerTextBlock" 
            HorizontalAlignment="Center" 
            VerticalAlignment="Center" 
            Text="00h : 00m : 00.000s" />

    </Grid>

</Window>

And equally simple code-behind:

public partial class MainWindow : Window
{
    private const string timerFormat = "{0:hh'h : 'mm'm : 'ss'.'fff's'}";
    private DispatcherTimer dispatcherTimer;
    private DateTime targetTime;

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        targetTime = DateTime.Now.AddSeconds(60);
        double totalTime = targetTime.Subtract(DateTime.Now).TotalMilliseconds;

        DoubleAnimation animation = new DoubleAnimation();
        animation.From = TimerTextBlock.FontSize;
        animation.To = 200;
        animation.Duration = new Duration(targetTime.Subtract(DateTime.Now));
        TimerTextBlock.BeginAnimation(TextBlock.FontSizeProperty, animation);

        dispatcherTimer = new DispatcherTimer();
        dispatcherTimer.Interval = TimeSpan.FromMilliseconds(1);
        dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
        dispatcherTimer.Start();
    }

    private void dispatcherTimer_Tick(object sender, EventArgs e)
    {
        if (DateTime.Compare(targetTime, DateTime.Now) > 0)
        {
            TimerTextBlock.Text = 
                string.Format(timerFormat, targetTime.Subtract(DateTime.Now));
        }
    }
}

Thank you for all the clarifications.

回答1:

Your vertical jumping problem is due to font rendering rounding. Specifically, WPF will avoid subpixel font height in order to enable font smoothing. One way to avoid this is to convert your text into a path geometry and then use a scale transform to animate it.

Here is an alternate version of your example without the jumping. The new XAML is:

<Grid>
    <Path Name="Path" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>

and the new code when you load the window:

SetText("");
var transform = new ScaleTransform(1, 1);
Path.LayoutTransform = transform;
var animationX = new DoubleAnimation(1, 10, new Duration(TimeSpan.FromSeconds(60)));
transform.BeginAnimation(ScaleTransform.ScaleXProperty, animationX);
var animationY = new DoubleAnimation(1, 10, new Duration(TimeSpan.FromSeconds(60)));
transform.BeginAnimation(ScaleTransform.ScaleYProperty, animationY);

and a new method to set the text that is anmiated:

private void SetText(string text)
{
    var formatted = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Lucida Console"), 12, Brushes.Black);
    Path.Data = formatted.BuildGeometry(new Point(0, 0));
    Path.Fill = Brushes.Black;
}

and you have call SetText from your timer event handler.

Note that to avoid horizontal jumpiness, you have to use a fixed-length text string and a constant-width font.



标签: wpf animation