Значения смещения для Windows Composition Visual неправильно инициализируются - PullRequest
0 голосов
/ 08 декабря 2018

Я использую UWP и работаю с Composition API, чтобы программно перемещать элементы по экрану, и я обнаружил, что при первом запуске приложения начальный слайд для многих элементов выполняется неправильно.При дальнейшей проверке я обнаружил, что свойства Смещения ElementVisual, которые я использую для вычисления и установки начальных и конечных положений анимации, иногда подходят ко всем нулевым значениям для значений X, Y, Z в самой первой анимации.Лучше всего я могу сказать, что все анимации этого элемента с этого момента работают правильно, пока приложение продолжает работать.

Приложение, над которым я работаю, является более сложным и интересным, но я создал упрощенное тестовое приложениечтобы продемонстрировать проблему, с которой я сталкиваюсь.

Из нового пустого проекта UWP я добавляю следующие GridView и Button в корневую сетку страницы:

        <GridView x:Name="TestGridView" HorizontalAlignment="Center" VerticalAlignment="Center">
        <GridViewItem >
            <Border Width="125" Height="125">
                <Grid>
                    <TextBlock Text="Content String 1" />
                </Grid>
            </Border>
        </GridViewItem>
        <GridViewItem>
            <Border Width="125" Height="125">
                <Grid>
                    <TextBlock Text="Content String 2" />
                </Grid>
            </Border>
        </GridViewItem>
        <GridViewItem>
            <Border Width="125" Height="125">
                <Grid>
                    <TextBlock Text="Content String 3"/>
                </Grid>
            </Border>
        </GridViewItem>
    </GridView>
    <Button Height="125" Width="125" Click="Button_Click"/>

В файле codebehind, который я добавляюследующие дополнительные операторы using, объявление переменной и этот обработчик событий:

using System.Numerics;  
using Windows.UI.Composition;
using Windows.UI.Xaml.Hosting;
using System.Diagnostics;

 Compositor compositor;

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        foreach (var element in TestGridView.ItemsPanelRoot.Children)
        {
            var slideVisual = ElementCompositionPreview.GetElementVisual(element);

            Debug.WriteLine("Element Offset: " + slideVisual.Offset);

            var slideAnimation = compositor.CreateVector3KeyFrameAnimation();
            slideAnimation.InsertKeyFrame(0f, slideVisual.Offset);
            slideAnimation.InsertKeyFrame(1f, new Vector3(slideVisual.Offset.X, slideVisual.Offset.Y + 20f, 0f));
            slideAnimation.Duration = TimeSpan.FromMilliseconds(800);

            slideVisual.StartAnimation(nameof(slideVisual.Offset), slideAnimation);
        }
    }

Я также инициализирую композитор в конструкторе MainPage ():

    public MainPage()
    {
        this.InitializeComponent();
        compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
    }

Затем запускаю проект вРежим отладки из Visual Studio.Нажатие на кнопку приводит к следующему выводу в моем «Немедленном окне» (для ясности добавлены разделительные линии в редактировании)

Element Offset: <0, 0, 0>
Element Offset: <0, 0, 0>
Element Offset: <0, 0, 0>

Element Offset: <0, 20, 0>
Element Offset: <129, 0, 0>
Element Offset: <258, 0, 0>

Element Offset: <0, 40, 0>
Element Offset: <129, 20, 0>
Element Offset: <258, 20, 0>

Element Offset: <0, 60, 0>
Element Offset: <129, 40, 0>
Element Offset: <258, 40, 0>

Обратите внимание, что первый из трех элементов анимируется на первом слайде, а два других - нет.и что все их значения смещения равны нулю.

На каждом слайде после всех трех элементов анимируются с их предыдущей позиции, как и должно, хотя побочные эффекты от анимации первого слайда сохраняются.

Я безуспешно пытался найти способ заставить элементы в нашем приложении обновить их значения смещения, чтобы начальная анимация слайдов для каждого элемента работала правильно, включая такие вещи, как вставка фиктивной анимации WarmUp непосредственно перед фактической предполагаемой анимацией, которая очень быстроговорит элементу либо о перемещении из его текущей позиции в ту же позицию, либо очень немного измененную стартовую позицию в его текущую текущую позицию.

            var warmUpAnimation = compositor.CreateVector3KeyFrameAnimation();                
            warmUpAnimation.InsertKeyFrame(0.0f, new Vector3(slideVisual.Offset.X + 1, slideVisual.Offset.Y, slideVisual.Offset.Z));
            warmUpAnimation.InsertKeyFrame(1f, slideVisual.Offset);
            warmUpAnimation.Duration = TimeSpan.FromMilliseconds(1);

Я предполагаю, что это ошибка в окнеs Composition API, но я надеюсь, что кто-то может предложить простой и эффективный обходной путь, который заставит ElementVisual корректно инициализироваться после загрузки.

Ответы [ 2 ]

0 голосов
/ 05 марта 2019

Две вещи:

1 - Как правило, НЕ используйте Смещение для анимации положения визуалов, созданных из FrameworkElement.Вместо этого используйте ручное свойство «Перевод».

Это можно сделать, вызвав следующее для каждого элемента, предпочтительно в их загруженных событиях

ElementCompositionPreview.SetIsTranslationEnabled(myFrameworkElement, true);

Затем вы измените анимацию на что-то вроде

var warmUpAnimation = compositor.CreateVector3KeyFrameAnimation();
warmUpAnimation.InsertExpressionKeyFrame(0.0f, "Vector3(this.Target.Translation.X + 100f, this.Target.Translation.Y, 0f)");
warmUpAnimation.InsertKeyFrame(1f, new Vector3(0f));
warmUpAnimation.Duration = TimeSpan.FromMilliseconds(1);

slideVisual.StartAnimation("Translation", warmUpAnimation);

Раньше это было ОЧЕНЬ очевидно в старой документации анимации Composition, но они, по-видимому, в значительной степени стерли упоминания о последних обновлениях.В целом, составление документации не очень хорошее.Я буквально не могу даже найти страницу прямо сейчас, которая объясняет, ПОЧЕМУ вы должны использовать Перевод поверх Смещения (что связано со свойствами XAML, перезаписывающими Свойства композиции).

По существу, смещение - это абсолютная позиция XY элемента, относительно которого он также является родительским, установленным механизмом рендеринга XAML. Кроме того, Translation сродни RenderTransform поверх этого - иначе XAML перезапишет смещение Композиции своими значениями.не заботясь о том, что вы вводите на уровне композиции, но у него нет «знания» свойства перевода, поэтому он никогда не перезаписывает его.Перевод всегда применяется как добавка к текущему смещению.Он также только работает для визуалов, созданных из FrameworkElements - он не нужен для любого другого типа CompositionVisual, поскольку их смещения не изменяются XAML.

2 - Это может помочь вызвать ElementCompositionPreview.GetElementVisual (...) для каждого целевого элемента, который, как вы знаете, вы собираетесь анимировать в какой-то момент в их загруженных событиях - обратите внимание, что он должен быть непосредственно на элементе, который будет анимирован.Это гарантирует, что визуальная передача будет создана и полностью готова к тому времени, когда вам это нужно.(Созданный может быть слегка асинхронным из-за особенностей работы API композиции с системным композитором)

0 голосов
/ 05 марта 2019

Я продолжаю сталкиваться с этой же проблемой при использовании API Composition всякий раз, когда использую его в приложениях UWP.По какой-то причине начальные вызовы GetElementVisual для UIElements часто не дают правильных результатов возврата.

Я прибег к выполнению теста в цикле перед первым использованием визуалов.

Как простойНапример, если вы добавили следующую функцию в приведенный выше пример:

    private bool IsCompositionVisualReady(UIElement element)
    {
        var visual = ElementCompositionPreview.GetElementVisual(element);
        if (visual.Size.X == 0 && visual.Size.Y == 0)
        {
            return false;
        }
        return true;
    }

, а затем вставили следующий цикл while в начале сегмента foreach в процедуре Button_Click и добавили «async» в обработчик кликов:

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        foreach (var element in TestGridView.ItemsPanelRoot.Children)
        {
            while (!IsCompositionVisualReady(element))
            {
                await Task.Delay(TimeSpan.FromMilliseconds(1));
            }

            var slideVisual = ElementCompositionPreview.GetElementVisual(element);

Визуальные эффекты оживляют более или менее должным образом.Тем не менее, кажется, что последние два элемента при первом проходе анимации все еще имеют небольшую задержку.

...