我有一个uwp-xaml应用程序,UI中有一系列按钮。
我正在使用Windows Composition Visual Layer API为按钮的一个视觉效果的RotationAngleIngrees设置动画。
按钮的内容在正面是可见的,这是我所期望的,但当视觉的背面旋转到视图中时,它也是可见的(尽管相反(。
所需的结果是,按钮看起来类似于一张被翻转的扑克牌,其中内容从正面可见,但当卡片被翻转时被隐藏。
我尝试了很多东西,包括:
-
修改按钮模板以将内容呈现器嵌套在父边界控件中,认为从背面查看时,包含可能会遮挡前面
-
将Visual的BackfaceVisibility属性设置为CompositionBackfaceVisible。可见
-
篡改了按钮背景颜色和视觉的不透明度设置
但是,视觉元素看起来仍然是透明的,并且内容显示在旋转视觉的两面上。
这是Windows Composition api中的限制还是错误?有没有一个简单的解决方法可以让视觉效果的背面显示与正面显示的内容不同的内容?
以下是生成动画的旋转代码的基础:
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>
这有助于我确认,将按钮内容放在边框容器内并不会阻止Visual在旋转时被透视。
我已经找到了一个解决方案,但当涉及到正面物品的3D可见性而不是通过父母的背面显示时,我仍然欢迎任何使构图视觉效果尊重其包含性的解决方案。
我想出的解决办法是伸手抓住视觉内容,然后设置其不透明度的动画,使其仅在旋转的一半标记处可见。我还添加了一个线性放松,这似乎有助于儿童视觉在正确的时间出现。
//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);
注意:我删除了带有额外Borders的自定义模板,这就是为什么上面的解决方法只需要使用VisualTreeHelper深入两个级别就可以获得内容呈现器,这样我就可以检索它的视觉效果。