Как сделать заднюю часть Windows Composition UI ElementVisual отличной от передней при повороте?

У меня есть приложение UWP XAML с серией кнопок в пользовательском интерфейсе.

Я использую API визуального слоя композиции Windows для анимации RotationAngleInDegrees одного из визуальных элементов Button.

Содержимое кнопки видно на лицевой стороне, которую я ожидал, но оно также видно (хотя и перевернуто), когда задняя сторона визуального элемента поворачивается в поле зрения.

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

Я пробовал несколько вещей, в том числе:

  • изменение шаблона кнопки для вложения предъявителя контента в родительские элементы управления границами, считая, что ограничение может закрывать переднюю часть при просмотре сзади

  • установка свойства BackfaceVisibility для Visual в CompositionBackfaceVisibility.Visible

  • поиграл с настройками непрозрачности как цвета фона кнопки, так и визуального

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

Это ограничение или ошибка в API Windows Composition? Есть ли простой обходной путь, чтобы заставить заднюю сторону визуальных элементов отображать нечто иное, чем содержимое, отображаемое спереди?

Вот основы кода поворота, который производит анимацию:

var btnVisual = ElementCompositionPreview.GetElementVisual(btn);            
var compositor = btnVisual.Compositor;

ScalarKeyFrameAnimation flipAnimation = compositor.CreateScalarKeyFrameAnimation();
flipAnimation.InsertKeyFrame(0.0f, 0);
flipAnimation.InsertKeyFrame(0.0001f, 180);
flipAnimation.InsertKeyFrame(1f, 0);
flipAnimation.Duration = TimeSpan.FromMilliseconds(800);
flipAnimation.IterationBehavior = AnimationIterationBehavior.Count;
flipAnimation.IterationCount = 1;
btnVisual.CenterPoint = new Vector3((float)(0.5 * btn.ActualWidth),(float) (0.5f * btn.ActualHeight), (float)(btn.ActualWidth/4));
btnVisual.RotationAxis = new Vector3(0.0f, 1f, 0f);

btnVisual.StartAnimation(nameof(btnVisual.RotationAngleInDegrees), flipAnimation);

Перевернутая кнопка является кнопкой xaml по умолчанию, которая изначально имела очень простой стиль:

<Style x:Key="ButtonStyle" TargetType="Button">
    <Setter Property="HorizontalAlignment" Value="Stretch" />
    <Setter Property="VerticalAlignment" Value="Stretch" />
    <Setter Property="Margin" Value="10" />
    <Setter Property="FontSize" Value="80" />
    <Setter Property="FontFamily" Value="Webdings" />
</Style>

и один из моих экспериментов был связан с изменением шаблона кнопки по умолчанию для размещения предъявителя контента в паре пограничных контейнеров:

<Style x:Key="ButtonStyle" TargetType="Button">
        <Setter Property="HorizontalAlignment" Value="Stretch" />
        <Setter Property="VerticalAlignment" Value="Stretch" />
        <Setter Property="Margin" Value="10" />
        <Setter Property="FontSize" Value="80" />
        <Setter Property="FontFamily" Value="Webdings" />
        <Setter Property="Background" Value="Silver" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Grid x:Name="RootGrid" Background="{TemplateBinding Background}">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal">
                                    <Storyboard>
                                        <PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="PointerOver">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                               Storyboard.TargetProperty="BorderBrush">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseMediumLowBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                               Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseHighBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid"
                                               Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlBackgroundBaseMediumLowBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                               Storyboard.TargetProperty="BorderBrush">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                               Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseHighBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <PointerDownThemeAnimation Storyboard.TargetName="RootGrid" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid"
                                               Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlBackgroundBaseLowBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                               Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                               Storyboard.TargetProperty="BorderBrush">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledTransparentBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Border Background="Red">
                            <Border Background="#FF62FB0A" Margin="10,10,10,10">
                                <ContentPresenter x:Name="ContentPresenter"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                Content="{TemplateBinding Content}"
                                ContentTransitions="{TemplateBinding ContentTransitions}"
                                ContentTemplate="{TemplateBinding ContentTemplate}"
                                Padding="{TemplateBinding Padding}"
                                HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                AutomationProperties.AccessibilityView="Raw"/>
                            </Border>
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>

    </Style>

в частности, часть измененная часть была

<Border Background="Red">
    <Border Background="#FF62FB0A" Margin="10,10,10,10">
        <ContentPresenter x:Name="ContentPresenter"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                Content="{TemplateBinding Content}"
                                ContentTransitions="{TemplateBinding ContentTransitions}"
                                ContentTemplate="{TemplateBinding ContentTemplate}"
                                Padding="{TemplateBinding Padding}"
                                HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                AutomationProperties.AccessibilityView="Raw"/>
    </Border>
</Border>

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

1 ответ

Решение

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

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

//Get a visual for the content
var btnContent = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(btn,0),0);
var btnContentVisual = ElementCompositionPreview.GetElementVisual(btnContent as FrameworkElement);  

var easing = compositor.CreateLinearEasingFunction();  

ScalarKeyFrameAnimation appearAnimation = compositor.CreateScalarKeyFrameAnimation();
appearAnimation.InsertKeyFrame(0.0f, 0);
appearAnimation.InsertKeyFrame(0.499999f, 0);
appearAnimation.InsertKeyFrame(0.5f, 1);
appearAnimation.InsertKeyFrame(1f, 1);
appearAnimation.Duration = TimeSpan.FromMilliseconds(800);

btnContentVisual.StartAnimation(nameof(btnContentVisual.Opacity), appearAnimation);

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

Другие вопросы по тегам