有幾十個wpf設計和動畫的項目

有幾十個wpf設計和動畫的項目

這是一個 wpf 項目,用於創作及收集一些好玩的設計和動畫。目前已有數十個 demo,部分 demo 有相關博客居間詳細的實現步驟和原理

最后更新 2022/4/17 下午5:51
dino.c
预计阅读 9 分钟
分类
WPF
专题
wpf ui設計
标签
.NET WPF wpf ui設計 動畫 設計

Wpf Design And Animation Lab

這是一個 wpf 項目,用於創作及收集一些好玩的設計和動畫。目前已有數十個 demo,部分 demo 有相關博客居間詳細的實現步驟和原理:

设计和动画 - dino.c - 博客园

通過這些博客,你將會了解到如何實現一些酷酷的 wpf 動畫和設計,以及一些 wpf 的技術細節。

1. 已實現的設計和動畫

1.1使用三種方式實現弧形進度條

image

實現弧形進度條的方案有很多種,通過用 path 和 arcsegment、arc、ellipse 這三個方案,可以了解各種 shape 的基本用法。

1.2僅用 rectangle 實現圓柱形進度條

image

圓柱形進度條不難實現,不過有趣的是上圖完全由代表矩形的 rectangle 組成,這稍微有點反直覺。

首先我们需要重温一些基础知识:Rectangle 显示带圆角的矩形。用 RadiusXRadiusY 可分别指定用于使矩形的角变圆的椭圆的 X 轴和 Y 轴半径。

如下面這個例子,可以看到radiusx="50" radiusy="20"的 rectangle 的圓角和width="100" height="40"的 ellipse (x 軸半徑 50,y 軸半徑 20)完全重合在一起。

<Rectangle  Height="100"
            Width="100"
            Fill="#FF7E9EC0"
            Stroke="#FFFF0EC4"
            StrokeThickness="5"
            RadiusX="50"
            RadiusY="20" />
<Ellipse HorizontalAlignment="Left"
         VerticalAlignment="Top"
         StrokeThickness="5"
         Stroke="Yellow"
         Fill="Red"
         Width="100"
         Height="40"
         Opacity="0.5" />

image

現在我們把上面的 rectangle 拉長,就成了圓柱的基本形狀;反過來將它壓扁,就成了圓柱的截面。再把它們設置成半透明的,就成了圓柱形進度條的背景:

image

<Grid.Resources>
   <Style TargetType="Rectangle">
       <Setter Property="Fill" Value="#36a8e2" />
       <Setter Property="RadiusX" Value="25" />
       <Setter Property="RadiusY" Value="5" />
   </Style>
</Grid.Resources>
<Rectangle Opacity="0.2" />
<Rectangle Height="10"
          VerticalAlignment="Top"
          Opacity="0.1" />

之後再添加一層半透明的漸變,以及另一個截面,就完成了圓柱形的進度條。

1.3玩玩彩虹文字及動畫

用 ItemsControl 拆分文字实现彩虹文字是一个很好玩的方案,因为可以对每个文字做不同的变形和动画,实现很多种玩法。首先,因为 string 是个集合,其实它可以用作 ItemsControl 的 ItemsSource。但在 Xaml 上直接写 ItemsSource="somestring"` 会报错,可以用 ContentControl 包装一下,写成这样:

<ContentControl Content="ItemsControl" >
    <ContentControl.Template>
        <ControlTemplate TargetType="ContentControl">
            <ItemsControl ItemsSource="{TemplateBinding Content}" >
            </ItemsControl>
        </ControlTemplate>
    </ContentControl.Template>
</ContentControl>

然後設置 itemscontrol 的 itemspanel,讓內容橫向排列;設置 datatemplate,讓拆分後的字符顯示在 textblock 上:

<ItemsControl ItemsSource="{TemplateBinding Content}" >
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

接下來,為了讓每個字符顯示不同的顏色,需要實現一個 collection 類並在 xaml 上實例化它,將用到的顏色放進去:

<common:RepeatCollection x:Key="RepeatCollection">
    <SolidColorBrush>#4a0e68</SolidColorBrush>
    <SolidColorBrush>#b62223</SolidColorBrush>
    <SolidColorBrush>#fdd70c</SolidColorBrush>
    <SolidColorBrush>#f16704</SolidColorBrush>
    <SolidColorBrush>#69982d</SolidColorBrush>
    <SolidColorBrush>#0075a5</SolidColorBrush>
    <SolidColorBrush>#0b0045</SolidColorBrush>
</common:RepeatCollection>

這個 repeatcollection 的代碼如下,它其實是個循環隊列,每次調用 next 的 getter 方法就拿下一個元素(叫 circlecollection 會不會好些?):

public class RepeatCollection : Collection<object>
{
    private int _offset;

    public object Next
    {
        get
        {
            if (this.Count == 0)
                return null;

            var result = this[_offset];
            _offset++;
            if (_offset > this.Count - 1)
                _offset = 0;

            return result;
        }
    }
}

最後,textblock 的 foreground 綁定到集合的 next 屬性,實現每一個 textblock 都使用不同的顏色:

<TextBlock Foreground="{Binding Next, Source={StaticResource RepeatCollection}}" Text="{Binding}" />

image

修改一下上面的代碼,就可以實現彩虹文字的動畫:

image

1.4製作一個彩虹按鈕

將 lineargradientbrush 應用在文字上,文字就變成了彩虹色。如果兩個 gradientstop 之間 color 相同就不會發生漸變,如果兩個 gradientstop 之間 offset 就會馬上變。利用這種手法,再加上我使用了等寬字體,所以可以製造出每個字顏色不一樣的彩虹文字:

<LinearGradientBrush x:Name="RainbowBrush" StartPoint="0,0.5" EndPoint="1,.5">
    <GradientStop x:Name="G1" Offset="0" Color="#65b849" />
    <GradientStop x:Name="G2" Offset=".166" Color="#65b849" />
    <GradientStop x:Name="G3" Offset=".166" Color="#f7b423" />
    <GradientStop x:Name="G4" Offset=".3333" Color="#f7b423" />
    <GradientStop x:Name="G5" Offset="0.3333" Color="#f58122" />
    <GradientStop x:Name="G6" Offset="0.5" Color="#f58122" />
    <GradientStop x:Name="G7" Offset="0.5" Color=" #f8f8f8" />
    <GradientStop x:Name="G8" Offset="0.5" Color=" #f8f8f8" />
    <GradientStop x:Name="G9" Offset="0.50" Color="#de3a3c" />
    <GradientStop x:Name="G10" Offset="0.666" Color="#de3a3c" />
    <GradientStop x:Name="G11" Offset="0.666" Color="#943f96" />
    <GradientStop x:Name="G12" Offset="0.8633" Color="#943f96" />
    <GradientStop x:Name="G13" Offset="0.8633" Color="#009fd9" />
    <GradientStop x:Name="G14" Offset="01" Color="#009fd9" />
</LinearGradientBrush>

image

在 mouseover 的 storyboard 里控制 lineargradientbrush 改變方向。有兩種方式可以改變它的方向,其中一種是用 pointanimation 改變 startpoint 和 endpoint,另一種是用 doubleanimation 直接改變 lineargradientbrush.relativetransform。後一種的寫法如下:

<Storyboard>
    <DoubleAnimation Storyboard.TargetName="textBlock"
                     Storyboard.TargetProperty="(TextBlock.Foreground).(Brush.RelativeTransform).(RotateTransform.Angle)"
                     To="90"
                     Duration="0:0:0.5">
        <DoubleAnimation.EasingFunction>
            <QuarticEase EasingMode="EaseOut" />
        </DoubleAnimation.EasingFunction>
    </DoubleAnimation>
</Storyboard>


<LinearGradientBrush x:Name="RainbowBrush" StartPoint="0,0.5" EndPoint="1,.5">
    <LinearGradientBrush.RelativeTransform>
        <RotateTransform Angle="0" CenterX="0.5" CenterY="0.5" />
    </LinearGradientBrush.RelativeTransform>

運行起來的效果就是將所有顏色旋轉 90 度,看起來就像以前的 apple 的 logo 配色。在上面的 lineargradientbrush 里,我偷偷藏了兩個白色的 gradientstop (名為 g6 和 g7 那兩個),它們的 offset 都是 0.5,處於正中間的位置。在按鈕的 pressed 狀態中,用 doubleanimation 將它們前後的所有 gradientstop 的 offset 都設置為 0 或 1,效果是將所有顏色向兩邊推。因為現在旋轉了 90 度,所以實際上是向上下兩個方向推:

image

1.5實現兩個任天堂 switch 的加載動畫

image

image

用拆分文字和 timespanincreaser 的方案,實現了兩個在任天堂 switch 中最常見的動畫。

1.6使用 shazzam shader editor 編寫一個 lighten effect

在上面的動畫裡為了實現不同亮度的 grid,使用了一個 lightenconverter 類,但是它只能處理 solidcolorbrush。為了可以應用在更多場合,接下來自己寫一個 effect 來實現相同 lighten 的效果。

image

1.7實現 wpf 的 inner shadow

在 wpf 中,我們通常用 dropshadow 做陰影效果,但都是做外陰影。內陰影(inner shadow)的話其實也不是不可以,就是有些曲折。實現內陰影的方案有幾種,其中我最喜歡用另一個元素的 visualbrush 來做 opacitymask 的方案。

<Grid Width="100"
      Height="100"
      Margin="10">
    <Rectangle x:Name="Rectangle2"
               Fill="White"
               RadiusX="8"
               RadiusY="8" />
    <Border Margin="0">
        <Border.Effect>
            <DropShadowEffect BlurRadius="8" ShadowDepth="0" />
        </Border.Effect>
        <ContentControl HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        Content="OpacityMask" />
    </Border>
    <Grid.OpacityMask>
        <VisualBrush Stretch="None" Visual="{Binding ElementName=Rectangle2}" />
    </Grid.OpacityMask>
</Grid>

但這樣做出來的陰影都不會太粗,如果需要更大更粗的內陰影,可以使用一個負數的 margin 配合同樣粗細的 borderthickness 實現。以 opacitymask 的方案為例,用下面的代碼可以做個又粗又大的內陰影:

private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    ShadowElement.Margin = new Thickness(-e.NewValue);
    ShadowElement.BorderThickness = new Thickness(e.NewValue);
    (ShadowElement.Effect as DropShadowEffect).BlurRadius = e.NewValue * 2;
}

image

1.8用 opacitymask 模仿 uwp 的 text shimmer 動畫

image

在 uwp 的 windows composition samples 中有一個 text shimmer 動畫,它用於展示如何使用 composition light。這個動畫很簡單,就是用 pointlight 從左到右掃過一行文字。雖然 wpf 沒有 composition light,但要玩這個簡單的動畫任然沒問題,就是用 opacitymask 模仿一下而已。

RadialGradientBrush 代表一个圆形的渐变画刷,在这里我们要关心它的三个属性:

radiusx/radiusy: 圓形的水平/垂直半徑。 center: 圓形的最外圍的中心。 gradientorigin: 漸變開始的二位焦點的位置。

這三個屬性的作用可以參考下圖:

image

用一個 radialgradientbrush 作為 opacitymask 讓 textblock 從中心點向外漸漸變得透明:

<TextBlock HorizontalAlignment="Center"
           VerticalAlignment="Center"
           FontFamily="SegoeUI"
           FontSize="100"
           FontWeight="Thin"
           Foreground="DimGray"
           Text="Text Shimmer">
    <TextBlock.OpacityMask>
        <RadialGradientBrush x:Name="Brush" Center=".5,.5" GradientOrigin=".5,.5" RadiusX=".43" RadiusY="2">
            <GradientStop Color="Black" />
            <GradientStop Offset=".5" Color="#6000" />
            <GradientStop Offset="1" Color="#2000" />
        </RadialGradientBrush>
    </TextBlock.OpacityMask>
</TextBlock>

然後對 center 和 gradientorigin 做 pointanimation,實現 opacitymask 的水平移動,就可以模仿出 pointlight 掃過的效果:

<PointAnimation RepeatBehavior="Forever"
                Storyboard.TargetName="Brush"
                Storyboard.TargetProperty="Center"
                From="-2,.5"
                To="3,.5"
                Duration="0:0:3.3" />
<PointAnimation RepeatBehavior="Forever"
                Storyboard.TargetName="Brush"
                Storyboard.TargetProperty="GradientOrigin"
                From="-2,.5"
                To="3,.5"
                Duration="0:0:3.3" />

1.9抄一個 css3 實現的按鈕

image

抄一個 css3 實現的按鈕,順便熟習下 css3。

1.10用 effect 實現線條光影效果

image

為了實現這個效果我用到這些知識和技巧:

  • segoe fluent 圖標字體
  • 在 blend 中創建 path
  • 計算 path 的長途
  • path 的邊框動畫
  • visualstudio 的設計時數據支持
  • 自定義 effect

2. License

The project is released under MIT License.

3. uwp 的版本

另外,我有另一個用於玩 uwp 動畫的項目:

https://github.com/DinoChan/uwp_design_and_animation_lab

image

轉載自 github

作者:dino.c

倉庫地址:https://github.com/DinoChan/wpf_design_and_animation_lab

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2025/1/26

wpf 藉助自定義 xml 文件實現國際化

本文詳細居間了在wpf程式中使用自定義xml文件實現國際化的方法,包括安裝必備nuget包、動態獲取語言列表、動態切換語言、在代碼和xaml界面中使用翻譯字符串等內容,同時提供了源碼連結,幫助開發者輕鬆實現wpf應用的國際化。

继续阅读