UWP Button添加圓角陰影(二)

原文:UWP Button添加圓角陰影(二)html

陰影

對於陰影呢,WindowsCommunityToolkit中已經有封裝好的DropShadowPanel啦,只要引用Microsoft.Toolkit.Uwp.UI.Controls這個Nuget包就可使用啦。
直接把陰影套在我們的圓角Button外面呢,會出現圓角的Button映出直角的陰影的醜陋情況。對於這種狀況確定是有處理方式的。
看DropShadowPanel的源碼,對Content的特殊類型作了處理。下面我詳細的說下。
CompositionAPI中的DropShadow這個東西,很是反人類的兩點:windows

  • 只有SpriteVisual擁有Shadow熟悉,也就是說你不能直接給Xaml元素的Visual附加陰影;
  • 給SpriteVisual設置陰影后,這個陰影是繪製在SpriteVisual上面的。
    因此呢,你去看DropShadowPanel的源碼,是ContentPresenter後面附了個什麼東西當SpriteVisual的Host,而後在Host的ChildVisual也就是SpriteVisual上面設置陰影。也就是說,這個陰影,和Content是沒毛關係的。
    那麼,官方Demo中Image、Shape、TextBlock的陰影是怎麼作到的呢?這就要提到他們三個擁有的一個方法了。說實話,這是我第一次見到多個控件擁有相同的方法,卻沒有繼承相同的接口的狀況。。。方法名叫GetAlphaMask(),能夠返回一個CompositionSurfaceBrush,Brush的內容是控件的內容填黑,其餘地方留空,給DropShadow.Mask設置成這個Brush,陰影就會按照這個輪廓去繪製。
    破案以後呢,思路就(救)出來了,按照這個方法,咱們只要給DropShadowPanel的Content設置一個Rectangle,就能得到圓角的陰影啦。
    既然上面已經有一個Rectangle當背景,這裏就再利用一下,給背景外面套個DropShadowPanel,接下來結構變成了這樣子。
<Style TargetType="Button" x:Key="CornerRadiusShadowButtonStyle">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Grid x:Name="RootGrid">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal" />
                            <VisualState x:Name="PointerOver">
                                <Storyboard>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <Storyboard>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <control:DropShadowPanel x:Name="Shadow" xmlns:control="using:Microsoft.Toolkit.Uwp.UI.Controls">
                        <Rectangle x:Name="Background"></Rectangle>
                    </control:DropShadowPanel>
                    <ContentPresenter x:Name="ContentPresenter">
                    </ContentPresenter>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

注意這裏有個小坑,DropShadowPanel的HorizontalContentAlignment默認是Left,Content裏若是隻裝Rectangle,是撐不開的。。。要作以下設置:動畫

<control:DropShadowPanel x:Name="Shadow" xmlns:control="using:Microsoft.Toolkit.Uwp.UI.Controls" HorizontalContentAlignment="Stretch">
    <Rectangle x:Name="Background" Fill="{TemplateBinding Background}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RadiusX="5" RadiusY="5" />
</control:DropShadowPanel>

接下來就是編寫VisualState了,若是要對DropShadowPanel進行有過渡的動畫,須要在DoubleAnimation中設置EnableDependentAnimation="True",否則只播放起始和終止狀態。詳情請見:情節提要動畫 - 從屬動畫和獨立動畫
注:調試過程當中熱更新DropShadowPanel的BlurRadius和Offset動畫,再播放會掉幀,從新運行一下就行了。

可是用DoubleAnimation去控制CompositionAPI中的動畫是很很差的,Composition有本身的Animation系統,咱們將會在下一篇文章中一塊兒給DropShadowPanel附加隱式(過渡)動畫。
下面是完整的代碼:spa

<Style TargetType="Button" x:Key="CornerRadiusShadowButtonStyle">
    <Setter Property="Background" Value="{ThemeResource ButtonBackground}" />
    <Setter Property="Foreground" Value="{ThemeResource ButtonForeground}" />
    <Setter Property="BorderBrush" Value="Transparent" />
    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="Padding" Value="20,10,20,10" />
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
    <Setter Property="FontWeight" Value="Normal" />
    <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
    <Setter Property="UseSystemFocusVisuals" Value="True" />
    <Setter Property="FocusVisualMargin" Value="-3" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Grid x:Name="RootGrid">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualStateGroup.Transitions>
                                <VisualTransition To="Normal" >
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="OffsetY" To="1" Duration="0:0:0.1" EnableDependentAnimation="True" />
                                        <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="BlurRadius" To="5" Duration="0:0:0.1" EnableDependentAnimation="True" />
                                    </Storyboard>
                                </VisualTransition>
                                <VisualTransition To="PointerOver" >
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="OffsetY" To="2" Duration="0:0:0.1" EnableDependentAnimation="True" />
                                        <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="BlurRadius" To="8" Duration="0:0:0.1" EnableDependentAnimation="True" />
                                    </Storyboard>
                                </VisualTransition>
                                <VisualTransition To="Pressed" >
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="OffsetY" To="3" Duration="0:0:0.1" EnableDependentAnimation="True" />
                                        <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="BlurRadius" To="12" Duration="0:0:0.1" EnableDependentAnimation="True" />
                                    </Storyboard>
                                </VisualTransition>
                            </VisualStateGroup.Transitions>
                            <VisualState x:Name="Normal" >
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="OffsetY" To="1" Duration="0" EnableDependentAnimation="True" />
                                    <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="BlurRadius" To="5" Duration="0" EnableDependentAnimation="True" />
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="PointerOver">
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="OffsetY" To="2" Duration="0" EnableDependentAnimation="True" />
                                    <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="BlurRadius" To="8" Duration="0" EnableDependentAnimation="True" />
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="OffsetY" To="3" Duration="0" EnableDependentAnimation="True" />
                                    <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="BlurRadius" To="12" Duration="0" EnableDependentAnimation="True" />
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundDisabled}" />
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <control:DropShadowPanel x:Name="Shadow" xmlns:control="using:Microsoft.Toolkit.Uwp.UI.Controls" HorizontalContentAlignment="Stretch" 
                                             BlurRadius="5" ShadowOpacity="0.8" OffsetX="1" OffsetY="1" Color="Black">
                        <Rectangle x:Name="Background" Fill="{TemplateBinding Background}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RadiusX="5" RadiusY="5" />
                    </control:DropShadowPanel>
                    <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" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
相關文章
相關標籤/搜索