How to animate scale of TextBlock in UWP

2019-04-14 16:28发布

问题:

When I use Storyboard to zoom in TextBlock it pixelates while zooming and rerenders only on complete.

<DoubleAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)">
      <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1.5"/>

Is there a way the TextBlock is rerendered each frame?

回答1:

I found a solution though it's not about TextBlock anymore but for me it worked:

    private void CreateText(string text)
    {
        _compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
        CreateDevice();

        _spriteTextVisual = _compositor.CreateSpriteVisual();
        _spriteTextVisual.Size = new Vector2(512, 512);

        _drawingTextSurface = _graphicsDevice.CreateDrawingSurface(new Size(512, 512), DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied);

        using (var ds = CanvasComposition.CreateDrawingSession(_drawingTextSurface))
        {
            ds.Clear(Colors.Transparent);
            ds.DrawText(text, new Rect(0, 0, 512, 512), Colors.Black, new CanvasTextFormat
            {
                FontSize = 32,
                FontWeight = FontWeights.Light,
                VerticalAlignment = CanvasVerticalAlignment.Top,
                HorizontalAlignment = CanvasHorizontalAlignment.Center,
                LineSpacing = 32
            });
        }

        _surfaceTextBrush = _compositor.CreateSurfaceBrush(_drawingTextSurface);

        _spriteTextVisual.Brush = _surfaceTextBrush;

        ElementCompositionPreview.SetElementChildVisual(this, _spriteTextVisual);
    }

    private void CreateDevice()
    {
        _device = CanvasDevice.GetSharedDevice();
        _device.DeviceLost += Device_DeviceLost;

        if (_graphicsDevice == null)
        {
            _graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(_compositor, _device);
        }
        else
        {
            CanvasComposition.SetCanvasDevice(_graphicsDevice, _device);
        }
    }

    private async void Device_DeviceLost(CanvasDevice sender, object args)
    {
        _device.DeviceLost -= Device_DeviceLost;
        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, CreateDevice);
    }

Just have to make this text of max scale size.



回答2:

It seems that this is by design - see this answer by Jerry Nixon on a very similar question.

Apparently this is to ensure the animation is smooth, because rendering the font fully at each frame would be expensive.

The only way you could probably overcome this is to wrap the TextBlock inside a Viewbox element and scale the Viewbox's Height property. This will however not work automatically and you also have to add EnableDependentAnimation="True" to the DoubleAnimationUsingKeyFrames element. Unfortunatelly you will see that this approach also causes layout updates on each frame and is therefore very jarring.