2

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.

Kara
  • 6,115
  • 16
  • 50
  • 57
Boris
  • 9,986
  • 34
  • 110
  • 147
  • I concluded that it's definitely the problem with 'pixel splitting'. It makes sense to chop a bit, as the control gets in a situation where it has to be drawn somewhere in between two pixels. But, is there a way to avoid this somehow? – Boris Dec 30 '10 at 02:45
  • I've been searching for answers on the internet and I came across some "MatrixAnimations". What is that, could it do the trick? (And if yes, how?) – Boris Dec 30 '10 at 02:53

1 Answers1

3

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.

Rick Sladkey
  • 33,988
  • 6
  • 71
  • 95
  • + 1 Very nice. The text is now animated smoothly, however, there is slight aliasing on the text as it grows. Is this expected? – Tim Lloyd Dec 30 '10 at 21:41
  • Sub-pixel anti-aliasing is essential for the smoothness of the animation so it's a trade-off: crisp text or smooth animation. – Rick Sladkey Dec 31 '10 at 02:14
  • Nice one. Too bad you are limited to the fixed-length text string and a constant-width fonts though, but there's no other way to center align the text. – Boris Dec 31 '10 at 02:19
  • Alternatively, if the text doesn't change during the animation, any string and any font will do (or changes slowly). – Rick Sladkey Dec 31 '10 at 02:23