整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

.NET CORE(C#) WPF 值得推荐的动画菜单设计

信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏。

.NET CORE(C#) WPF 值得推荐的动画菜单设计

阅读导航

  1. 本文背景
  2. 代码实现
  3. 本文参考
  4. 源码

1. 本文背景

YouTube上老外的一个设计,站长觉得不错,分享给大家作为参考,抽屉菜单的动画做的非常不错。

运行起始界面:



站长运行操作一遍,录制了动画大家看看:



2. 代码实现

使用 .NET CORE 3.1 创建名为 “AnimatedMenu” 的WPF模板项目,添加1个Nuget库:MaterialDesignThemes,版本为最新预览版3.1.0-ci948。

解决方案主要文件目录组织结构:

  • AnimatedMenu
  • App.xaml
  • MainWindow.xaml
  • MainWindow.xaml.cs

2.1 引入样式

文件【App.xaml】,在 StartupUri 中设置启动的视图【MainWindow.xaml】,并在【Application.Resources】节点增加 MaterialDesignThemes库的样式文件:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Dark.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.Blue.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Blue.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

2.2 演示窗体

文件【MainWindow.xaml】,布局代码、动画代码都在此文件中,源码如下:

<Window x:Class="AnimatedMenu.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" MouseLeftButtonDown="MoveWindow_MouseLeftButtonDown"
        Height="600" Width="1024" WindowStyle="None" WindowStartupLocation="CenterScreen">
    <WindowChrome.WindowChrome>
        <WindowChrome CaptionHeight="0"/>
    </WindowChrome.WindowChrome>
    <Window.Resources>
        <Storyboard x:Key="OpenMenu">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="GridMain">
                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="250"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="GridMain">
                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="50"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="StackPanelMenu">
                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="250"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="listViewItem">
                <EasingDoubleKeyFrame KeyTime="0" Value="-250"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-250"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="listViewItem1">
                <EasingDoubleKeyFrame KeyTime="0" Value="-250"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-250"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.9" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="listViewItem2">
                <EasingDoubleKeyFrame KeyTime="0" Value="-250"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-250"/>
                <EasingDoubleKeyFrame KeyTime="0:0:1.1" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="listViewItem3">
                <EasingDoubleKeyFrame KeyTime="0" Value="-250"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-250"/>
                <EasingDoubleKeyFrame KeyTime="0:0:1.3" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="listViewItem4">
                <EasingDoubleKeyFrame KeyTime="0" Value="-250"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-250"/>
                <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="button">
                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
                <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="button">
                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
                <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
        <Storyboard x:Key="CloseMenu">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="GridMain">
                <EasingDoubleKeyFrame KeyTime="0" Value="250"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="GridMain">
                <EasingDoubleKeyFrame KeyTime="0" Value="50"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="StackPanelMenu">
                <EasingDoubleKeyFrame KeyTime="0" Value="250"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="ButtonOpenMenu">
            <BeginStoryboard Storyboard="{StaticResource OpenMenu}"/>
        </EventTrigger>
        <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="ButtonCloseMenu">
            <BeginStoryboard x:Name="CloseMenu_BeginStoryboard" Storyboard="{StaticResource CloseMenu}"/>
        </EventTrigger>
    </Window.Triggers>
    <Grid Background="#FF3580BF">
        <StackPanel x:Name="StackPanelMenu" Width="250" HorizontalAlignment="Left" Margin="-250 0 0 0" RenderTransformOrigin="0.5,0.5">
            <StackPanel.RenderTransform>
                <TransformGroup>
                    <ScaleTransform/>
                    <SkewTransform/>
                    <RotateTransform/>
                    <TranslateTransform/>
                </TransformGroup>
            </StackPanel.RenderTransform>
            <StackPanel Orientation="Horizontal" VerticalAlignment="Top" Height="100" HorizontalAlignment="Center">
                <Button Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" Background="{x:Null}" BorderBrush="{x:Null}" Padding="0" Width="50" Height="50" Margin="10">
                    <materialDesign:PackIcon Kind="Settings" Width="40" Height="40"/>
                </Button>
                <Button x:Name="button" Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" BorderBrush="{x:Null}" Padding="0" Width="80" Height="80" Margin="10" RenderTransformOrigin="0.5,0.5">
                    <Button.RenderTransform>
                        <TransformGroup>
                            <ScaleTransform/>
                            <SkewTransform/>
                            <RotateTransform/>
                            <TranslateTransform/>
                        </TransformGroup>
                    </Button.RenderTransform>
                    <Button.Background>
                        <ImageBrush ImageSource="https://img.dotnet9.com/logo.png" Stretch="UniformToFill"/>
                    </Button.Background>
                </Button>
                <Button Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" Background="{x:Null}" BorderBrush="{x:Null}" Padding="0" Width="50" Height="50" Margin="10">
                    <materialDesign:PackIcon Kind="InformationOutline" Width="40" Height="40"/>
                </Button>
            </StackPanel>
            <ListView>
                <ListViewItem x:Name="listViewItem" Height="60" RenderTransformOrigin="0.5,0.5">
                    <ListViewItem.RenderTransform>
                        <TransformGroup>
                            <ScaleTransform/>
                            <SkewTransform/>
                            <RotateTransform/>
                            <TranslateTransform/>
                        </TransformGroup>
                    </ListViewItem.RenderTransform>
                    <StackPanel Orientation="Horizontal" Margin="10 0">
                        <materialDesign:PackIcon Kind="Home" Width="30" Height="30" VerticalAlignment="Center" Margin="5"/>
                        <TextBlock Text="主页" Margin="10" VerticalAlignment="Center"/>
                    </StackPanel>
                </ListViewItem>
                <ListViewItem x:Name="listViewItem1" Height="60" RenderTransformOrigin="0.5,0.5">
                    <ListViewItem.RenderTransform>
                        <TransformGroup>
                            <ScaleTransform/>
                            <SkewTransform/>
                            <RotateTransform/>
                            <TranslateTransform/>
                        </TransformGroup>
                    </ListViewItem.RenderTransform>
                    <StackPanel Orientation="Horizontal" Margin="10 0">
                        <materialDesign:PackIcon Kind="AccountSearch" Width="30" Height="30" VerticalAlignment="Center" Margin="5"/>
                        <TextBlock Text="搜索" Margin="10" VerticalAlignment="Center"/>
                    </StackPanel>
                </ListViewItem>
                <ListViewItem x:Name="listViewItem2" Height="60" RenderTransformOrigin="0.5,0.5">
                    <ListViewItem.RenderTransform>
                        <TransformGroup>
                            <ScaleTransform/>
                            <SkewTransform/>
                            <RotateTransform/>
                            <TranslateTransform/>
                        </TransformGroup>
                    </ListViewItem.RenderTransform>
                    <StackPanel Orientation="Horizontal" Margin="10 0">
                        <materialDesign:PackIcon Kind="Wechat" Width="30" Height="30" VerticalAlignment="Center" Margin="5"/>
                        <TextBlock Text="微信" Margin="10" VerticalAlignment="Center"/>
                    </StackPanel>
                </ListViewItem>
                <ListViewItem x:Name="listViewItem3" Height="60" RenderTransformOrigin="0.5,0.5">
                    <ListViewItem.RenderTransform>
                        <TransformGroup>
                            <ScaleTransform/>
                            <SkewTransform/>
                            <RotateTransform/>
                            <TranslateTransform/>
                        </TransformGroup>
                    </ListViewItem.RenderTransform>
                    <StackPanel Orientation="Horizontal" Margin="10 0">
                        <materialDesign:PackIcon Kind="Qqchat" Width="30" Height="30" VerticalAlignment="Center" Margin="5"/>
                        <TextBlock Text="QQ" Margin="10" VerticalAlignment="Center"/>
                    </StackPanel>
                </ListViewItem>
                <ListViewItem x:Name="listViewItem4" Height="60" RenderTransformOrigin="0.5,0.5">
                    <ListViewItem.RenderTransform>
                        <TransformGroup>
                            <ScaleTransform/>
                            <SkewTransform/>
                            <RotateTransform/>
                            <TranslateTransform/>
                        </TransformGroup>
                    </ListViewItem.RenderTransform>
                    <StackPanel Orientation="Horizontal" Margin="10 0">
                        <materialDesign:PackIcon Kind="Facebook" Width="30" Height="30" VerticalAlignment="Center" Margin="5"/>
                        <TextBlock Text="脸书" Margin="10" VerticalAlignment="Center"/>
                    </StackPanel>
                </ListViewItem>
            </ListView>
        </StackPanel>
        <Grid x:Name="GridMain" Background="#FFFBFBFB" Width="1024" RenderTransformOrigin="0.5,0.5">
            <Grid.RenderTransform>
                <TransformGroup>
                    <ScaleTransform/>
                    <SkewTransform/>
                    <RotateTransform/>
                    <TranslateTransform/>
                </TransformGroup>
            </Grid.RenderTransform>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="250"/>
            </Grid.ColumnDefinitions>

            <Grid Grid.Column="1" Background="#FF3580BF">
                <Image Height="150" VerticalAlignment="Top" Source="https://dotnet9.com/wp-content/uploads/2017/04/About-Header.jpg" Stretch="UniformToFill"/>
                <Ellipse Height="100" Width="100" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="20 100" Stroke="White">
                    <Ellipse.Fill>
                        <ImageBrush ImageSource="https://img.dotnet9.com/logo.png" Stretch="UniformToFill"/>
                    </Ellipse.Fill>
                </Ellipse>
                <TextBlock Text="Dotnet9" Foreground="White" FontSize="28" FontFamily="Nirmala UI Semilight" Margin="10 100" VerticalAlignment="Top"/>
                <StackPanel Margin="0 150">
                    <Grid Height="60" Margin="20 50 20 0">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="30"/>
                            <RowDefinition Height="30"/>
                        </Grid.RowDefinitions>
                        <TextBlock Text="追随者" VerticalAlignment="Bottom" Foreground="#FFFBFBFB" Margin="5,0,5,5"/>
                        <TextBlock Text="1.5K" VerticalAlignment="Top" Foreground="#FFFBFBFB" Grid.Row="1" Margin="10 0"/>

                        <TextBlock Text="跟随" VerticalAlignment="Bottom" Foreground="#FFFBFBFB" Margin="5,0,5,5" Grid.Column="1"/>
                        <TextBlock Text="2.3K" VerticalAlignment="Top" Foreground="#FFFBFBFB" Grid.Row="1" Margin="10 0" Grid.Column="1"/>
                    </Grid>

                    <TextBlock TextWrapping="Wrap" Margin="10" Foreground="#FFFBFBFB" FontSize="14">
                        <Run Text="网名:沙漠尽头的狼"/>
                        <LineBreak/>
                        <LineBreak/>
                        <Run Text="网站:https://dotnet9.com"/>
                        <LineBreak/>
                        <Run Text="    Dotnet9的博客 - 一个热衷于互联网分享精神的个人博客站点"/>
                        <LineBreak/>
                        <LineBreak/>
                        <Run Text="座右铭:时间如流水,只能流去不流回。"/>
                    </TextBlock>
                </StackPanel>
            </Grid>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="40"/>
                    <RowDefinition Height="50"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>

                <Button x:Name="ButtonCloseMenu" Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" Width="30" Height="30" Padding="0" Background="{x:Null}" BorderBrush="{x:Null}" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5" Click="ButtonCloseMenu_Click" Visibility="Collapsed">
                    <materialDesign:PackIcon Kind="Menu" Foreground="#FF3580BF"/>
                </Button>
                <Button x:Name="ButtonOpenMenu" Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" Width="30" Height="30" Padding="0" Background="{x:Null}" BorderBrush="{x:Null}" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5" Click="ButtonOpenMenu_Click">
                    <materialDesign:PackIcon Kind="Menu" Foreground="#FF3580BF"/>
                </Button>


                <TextBlock Text="照片" Foreground="#FF3580BF" FontSize="30" FontWeight="Bold" Margin="5" Grid.Row="1"/>

                <Grid Margin="5" Grid.Row="2" Grid.Column="0">
                    <Grid.Effect>
                        <DropShadowEffect BlurRadius="20" Color="#FFEEEEEE" ShadowDepth="1"/>
                    </Grid.Effect>
                    <Image Source="https://dotnet9.com/wp-content/uploads/2019/12/wpf.png" Stretch="UniformToFill"/>
                    <StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                        <materialDesign:PackIcon Kind="Heart" Foreground="#FFFBFBFB"/>
                        <TextBlock Text="25" Foreground="#FFFBFBFB"/>
                    </StackPanel>
                </Grid>
                <Grid Margin="5" Grid.Row="2" Grid.Column="1">
                    <Grid.Effect>
                        <DropShadowEffect BlurRadius="20" Color="#FFEEEEEE" ShadowDepth="1"/>
                    </Grid.Effect>
                    <Image Source="https://dotnet9.com/wp-content/uploads/2019/12/winform.png" Stretch="UniformToFill"/>
                    <StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                        <materialDesign:PackIcon Kind="Heart" Foreground="#FFFBFBFB"/>
                        <TextBlock Text="50" Foreground="#FFFBFBFB"/>
                    </StackPanel>
                </Grid>
                <Grid Margin="5" Grid.Row="2" Grid.Column="2">
                    <Grid.Effect>
                        <DropShadowEffect BlurRadius="20" Color="#FFEEEEEE" ShadowDepth="1"/>
                    </Grid.Effect>
                    <Image Source="https://dotnet9.com/wp-content/uploads/2019/12/asp-net-core.png" Stretch="UniformToFill"/>
                    <StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                        <materialDesign:PackIcon Kind="Heart" Foreground="#FFFBFBFB"/>
                        <TextBlock Text="18" Foreground="#FFFBFBFB"/>
                    </StackPanel>
                </Grid>
                <Grid Margin="5" Grid.Row="3" Grid.Column="0">
                    <Grid.Effect>
                        <DropShadowEffect BlurRadius="20" Color="#FFEEEEEE" ShadowDepth="1"/>
                    </Grid.Effect>
                    <Image Source="https://img.dotnet9.com/Xamarin.Forms.png" Stretch="UniformToFill"/>
                    <StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                        <materialDesign:PackIcon Kind="Heart" Foreground="#FFFBFBFB"/>
                        <TextBlock Text="32" Foreground="#FFFBFBFB"/>
                    </StackPanel>
                </Grid>
                <Grid Margin="5" Grid.Row="3" Grid.Column="1">
                    <Grid.Effect>
                        <DropShadowEffect BlurRadius="20" Color="#FFEEEEEE" ShadowDepth="1"/>
                    </Grid.Effect>
                    <Image Source="https://dotnet9.com/wp-content/uploads/2019/12/front-end.png" Stretch="UniformToFill"/>
                    <StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                        <materialDesign:PackIcon Kind="Heart" Foreground="#FFFBFBFB"/>
                        <TextBlock Text="32" Foreground="#FFFBFBFB"/>
                    </StackPanel>
                </Grid>
            </Grid>
        </Grid>
        <StackPanel Orientation="Horizontal" VerticalAlignment="Top" Height="40" HorizontalAlignment="Right" Margin="10">
            <Button Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" Width="30" Height="30" Padding="0" Background="{x:Null}" BorderBrush="{x:Null}">
                <materialDesign:PackIcon Kind="Bell"/>
            </Button>
            <Button x:Name="ButtonClose" Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" Width="30" Height="30" Padding="0" Background="{x:Null}" BorderBrush="{x:Null}" Click="ButtonClose_Click">
                <materialDesign:PackIcon Kind="Close"/>
            </Button>
        </StackPanel>
    </Grid>
</Window>

简单说明下:

  1. "StackPanelMenu" 作为左侧菜单容器,默认是显示在窗体外,距离窗体左边缘-250,点击左上角菜单按钮图标可控制此容器的显示与隐藏,注:菜单开关由两个按钮组成 "ButtonOpenMenu" 和 "ButtonCloseMenu"。
  2. 左侧菜单项使用 "ListView" 进行布局,实际开发需要运用模板,使用MVVM做成动态菜单,方便扩展。
  3. 中间的5张演示照片,也和2类似。直接使用Grid进行的布局,实际上都需要做成模板。
  4. 抽屉菜单动画见 Window.Resouces 中的动画代码,展开抽屉菜单动画是 "OpenMenu",左侧菜单向右、向下移动,右侧展示区域及个人信息概况界面同时也是向右、向下移动;关闭抽屉菜单动画是 "CloseMenu",动画移动方向与展开时相反(说的是废话),这段动画代码值得好好学习、复用。

文件【MainWindow.xaml.cs】,后台关闭窗体、抽屉菜单按钮切换、窗体移动等事件处理:

private void ButtonClose_Click(object sender, RoutedEventArgs e)
{
    Application.Current.Shutdown();
}

private void ButtonOpenMenu_Click(object sender, RoutedEventArgs e)
{
    ButtonOpenMenu.Visibility = Visibility.Collapsed;
    ButtonCloseMenu.Visibility = Visibility.Visible;
}

private void ButtonCloseMenu_Click(object sender, RoutedEventArgs e)
{
    ButtonOpenMenu.Visibility = Visibility.Visible;
    ButtonCloseMenu.Visibility = Visibility.Collapsed;
}

private void MoveWindow_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    DragMove();
}

代码已全部奉上...

3.本文参考

  1. 视频一:C# WPF Material Design UI: Animated Menu,配套源码:AnimatedMenu1。
  2. C# WPF开源控件库《MaterialDesignInXAML》

4.源码

效果图实现代码在文中已经全部给出,站长方便演示,文中的图片使用的本站外链图片,代码可直接Copy,按解决方案目录组织代码文件即可运行。

演示Demo下载


除非注明,文章均由 Dotnet9 整理发布,欢迎转载。转载请注明本文地址:https://dotnet9.com/7669.html欢迎扫描下方二维码关注 Dotnet9 的微信公众号,本站会及时推送最新技术文章


时间如流水,只能流去不流回!

点击《【阅读原文】》,本站还有更多技术类文章等着您哦!!!

itbook 是一款产品文档构建工具,也可以用于构建个人博客,默认情况下电脑端访问时左侧菜单是展开状态,可偏偏有人想要实现默认折叠效果,于是诞生了这篇文章!

善良的我选择帮助别人

可能是网上关于 Gitbook 的教程相对来说有些落后,加上写文章时分享了不少关于 ,因此关注我的粉丝好友中有不少是来源于 Gitbook.

所以上个月有个好友问我能不能配置 Gitbook 默认折叠的效果,心里有些犯难,作为 gitbook 的忠实粉丝,我都不知道 gitbook 还有这方面的配置?!

但是,善良的我总是有求必应,不忍心拒绝小白用户,于是我便抱着试一试的心态开始研究一下如何默认折叠?

当然,解决问题前还是要先复现一下问题,然后在命令行中熟练敲入了 gitbook serve 命令来启动本地服务器,为了排除缓存等影响,特意打开了 Chrome 浏览器的无痕模式,果不其然默认左侧菜单是展开的!

不能复现的问题都不是我的问题,拒绝解决此类问题,搞不好是你自己环境搭建问题呢!

蓦然回首官方文档已走

问题复现后就要开始寻求解决之道,虽然印象中并没有相关配置,但是难保记忆混乱遗漏了某些配置项,所以还是先看看官方文档怎么说的吧!

但是,当你在浏览器中输入 gitbook 官方文档 时,并找不到想象中的官方文档而是新版官网,不信你自己去搜一下,肯定是新版官网.

当你自以为找到了官网时,点击进去查看文档部分,很遗憾,这是新版文档并不是老版文档,你还会继续百度一下寻求可用链接期待找到官方文档.

为了节省宝贵时间,这里推荐访问个人维护的 gitbook 文档

目前提供了中英文两个语言版本的文档,相信可以满足大多数用户的需求了,选择任意一种语言后点击进入翻阅相关设置.

实际上,官方文档也并没有什么用,因为根本就没有提到过如何更改相关配置使其默认折叠而非展开状态.

官方不再维护旧版文档,费尽心机找到旧版文档也无济于事,因为并没有提及到相关配置,所以猜测很可能并未提供有关配置项!

百度一下你就知道了吗

俗话说:"互联网上绝大多数问题别人都已经遇到过并提供了解决方案,我们唯一要做的就是找到它!"

这也是面向搜索编程的核心思想,遇到默认折叠问题应该也不会例外,那就搜索一下吧!

虽然百度搜索出现了一些相关文章,但是却不是我们想要的效果,大多数是基于 gitbook 插件实现的目录折叠效果,并不是默认折叠左侧菜单效果.

不管是换关键词重新搜索还是谷歌搜索,均未发现有关默认折叠左侧菜单的解决方案,难不成面向搜索失败了,要做解决问题的第一人吗?!

多次重复搜索操作均为找到解决方案,由此可见真的很少有人想要默认折叠左侧菜单,我也是很佩服提出该问题的小伙伴骨骼惊奇啊!

自力更生找寻蛛丝马迹

既然依靠别人无法解决问题,那么只能自力更生独自解决问题,是时候考验真正的技术了!

为了排除无关干扰,不能再用自己的 gitbook 项目了,毕竟文件太多不方便后续调试,那么不妨重新创建一个测试项目.

  • 创建测试项目
  • 初始化测试项目
  • 启动测试项目

虽然一片空白,并没有什么实质性内容,但是大道至简,对于我们复现并测试问题来说,足够了!

打开 Chrome 浏览器并按下 F12 开启调试模式,鼠标选中左侧的 Elements 元素选项卡并点亮左侧的小鼠标,然后在页面上找到左侧图标按钮,于是选中元素高亮了.

单独摘录 Html 关键代码如下:

稍微熟悉前端的小伙伴可能很轻松就能明白 a 标签的 class 属性表示的含义,见名知意,可以这么解释:

  • btn 应该是控制外观的样式,表现得像是按钮效果.
  • pull-left 应该是控制元素的位置,拉倒左边.
  • js-toolbar-action 应该是控制元素的行为,js 工具栏行为动作.

由此可见,点击该图标实现左侧菜单折叠/展开效果应该是 .js-toolbar-action 在起作用,也就是说某一段 js 肯定是针对该 class进行了监听!

此时,点击右侧的 Event Listeners 选项卡查看该元素已监听的 click 事件,定位到是哪一个具体的 js 文件在起作用.

果不其然,元素上存在 click 点击事件监听并且发现执行监听的逻辑代码出现在 theme.js 文件,点击进入文件查看具体内容.

压缩后的 js 代码不具备可读性,点击左下方的 {} 图标可以进行代码格式化,但是可能不是单纯的压缩而是进行了丑化或者混淆代码之类的逻辑,格式化后的代码仍然不可读!

终于发现了蛛丝马迹,修改的代码逻辑就隐藏在 theme.js 文件中,只要找到相关源码重新编译输出 theme.js文件并替换应该就能实现默认折叠效果!

不要担心黎明前的黑暗

根据目前已掌握的线索,可以肯定的是有用线索主要有两个:

  • 监听元素 .js-toolbar-action
  • 输出文件 theme.js

一个是源码文件,另一个是输出文件,想要在庞大的 gitbook 项目中迅速定位到相关代码逻辑,个人能力有限,并不熟悉前端开发调试流程,因此采用最简单粗暴傻瓜式搜索方式进行排查!

如果读者对于现代前端开发流程比较属性的话,大概过一遍项目结构应该就可以调试定位问题了,用不着像我这样傻瓜式搜索排查!

  • 查看当前 gitbook 版本
  • 找到 gitbook 安装位置

gitbook 一般安装在 ~/.gitbook/versions/3.2.3 目录,其中 ~ 表示用户家目录.

选择一款熟悉的编辑器并打开 Gitbook 安装目录,这里以 sublime 编辑器为例,选中项目后右键全局搜索关键字 js-toolbar-action 期望找到相关源码文件.

全局搜索后主要出现两个文件包含 js-toolbar-action 关键字,一个是输出文件 theme.js ,另一个是源码文件 toolbar.js .

可想而知,源码文件肯定是经过编译处理后统一打包输出,因此不仅仅要找到源码文件还要掌握如何编译.

定位到当前 gitbook 目录后借助全局搜索功能定位到具体的文件路径,起作用的是 gitbook-plugin-theme-default 项目,其实这就是 Gitbook 的默认主题.

源码在哪

/Users/snowdreams1006/.gitbook/versions/3.2.3/node_modules/gitbook-plugin-theme-default/src/js/theme/toolbar.js :

粗略看一下,上述代码是实现触发左侧图标折叠/展开菜单的逻辑实现,这里只是具体实现还不知道谁是使用者,也就是说这种逻辑是在哪里调用的?

只能继续顺藤摸瓜,往上翻看,根据基本开发常识,在该文件的同级目录中存在如下文件,其中的 index.js 应该就是入口文件:

打开 index.js 文件,根据注释我们可以看到 init() 函数是入门函数,其中 sidebar.init() 和 sidebar.toggle() 函数无不说明 sidebar.js 和 toolbar.js 关系密切,完全有理由猜想 sidebar.js 是 toolbar.js 的使用者!

打开 sidebar.js 文件并查看 init() 初始化函数和 toggle() 触发函数,可以验证我们的猜想,这里就是控制中心!

: 非手机端初始化上次状态,默认展开侧边栏,如果是手机端则折叠侧边栏.其中 toggleSidebar() 接收两个参数,第一次参数表示是展开还是折叠,第二个参数暂不可知.

「: 第一个参数确实表示状态而第二个参数表示是否有动画效果,不用看具体代码逻辑而是看注释就能猜出大概逻辑了.

通过上述分析,我们可以得知 init() 初始化函数决定了默认行为是折叠还是展开,同时 gitbook.storage.set('sidebar', isOpen()) 和 gitbook.storage.get('sidebar', true) 应该是设置和获取是否展开菜单的标志!

由此,如果想要默认折叠左侧菜单,那么只需要设置成 gitbook.storage.set('sidebar', false) 应该就会生效!

如何编译

说干就干,于是乎在 init() 函数插入 gitbook.storage.set('sidebar', false) 默认折叠逻辑,接着看一下是否需要重新编译才能生效?

接着切换到测试项目再次运行 gitbook serve 启动本地服务器,发现并没有任何变化,很有可能改变源码文件需要重新编译才会生效或者说更改的源码项目也没有生效?

: 该源码文件所在的项目是 gitbook-plugin-theme-default ,根据 gitbook 插件命名规范我们知道,gitbook-plugin-* 一般是功能性插件,这一类的插件有 gitbook-plugin-readmore 阅读更多插件和 gitbook-plugin-copyright版权保护插件等等.

但是如果插件名以 gitbook-plugin-theme 开头的话,这一类插件就是主题插件,比如 gitbook-plugin-theme-default 就是默认主题.

除此之外,只要遵守该命名规则的插件引入时无需添加 gitbook-plugin- 前缀,可以直接在 gitbook.json 文件中引入剩余的简称作为插件名.

摘录自 Gitbook 项目的配置文件,可以佐证上述规则的正确性.

作为普通的 nodejs 包,开发规范规定了 package.json 提供了插件的配置信息,而 Gitbook 插件除了是标准的 nodejs 包之外还有自己的约束,主要体现在提供了 gitbook 节点属性:

默认主题仅仅提供了两个配置项,分别是 styles 样式文件位置和 showLevel 是否显示层级配置.

再一次验证了猜想的正确性,真的需要修改源码才能实现默认折叠左侧菜单的效果,紧着继续在 package.json 中找到项目源码的托管地址,看一下有没有提供二次开发文档.

令人遗憾的是,项目介绍空空如也,除了一张主题预览图,别的什么都没有?!

既然没有二次开发文档,那就看看项目源码有没有别的蛛丝马迹教我们如何编译?

绕了这么多,其实还不是因为比较菜,人家都提供给源码都不会编译,留下来没有技术的眼泪!

视角再一次切换到源码目录,除了 js 和 less 目录外,竟然还有一个 build.sh 构建脚本!

这一刻,仿佛看到了九点钟升起的太阳,未来是你们的也是我们的!

这一段脚本中除了看不懂 browserify,uglifyjs,lessc -clean-css 命令外,剩下部分都很简单,大致是编译源码文件并输出到 _assets 目录.

编译 js 的命令主要有以下两条,而我们关心的 theme.js 仅涉及到一条,除此之外没有任何别的依赖,这一点非常好!

接下来的重点就是如何运行 browserify src/js/theme/index.js | uglifyjs -mc > _assets/website/theme.js 命令了!

摇身一变重新编译源码

browserify src/js/theme/index.js | uglifyjs -mc > _assets/website/theme.js

百度一下 browserify

再一次打开熟悉的浏览器输入关键字 browserify 后出现一系列相关文章,很好奇为啥排名第一个都不会是官网呢?不管怎么样,找到 browserify 的 github 项目地址也是不错的!

这里并不关心 browserify 到底是什么,只在乎如何安装基本环境而已!

如果是 mac 电脑,全局安装需要管理员权限,应该运行 sudo npm install -g browserify ,如果嫌弃安装速度慢也可以运行 cnpm install -g browserify ,前提是已安装 cnpm 命令.

谷歌一下 uglifyjs

不吹不黑,少走一点弯路,直接就找到了 github 项目网址,同样的也不关心项目介绍,直接翻看如何安装部分.

重新编译 others

涉及到 browserify src/js/theme/index.js | uglifyjs -mc > _assets/website/theme.js 命令的两个插件均已安装完毕,理所应当开始重新编译源码了,但是竟然报错了?

当出现报错时,开始怀疑人生,难道推论不正确,难道环境没有安装成功吗,为啥提示找不到 mousetrap 模块?

算了吧,与其费尽心思猜测为啥无法加载 mousetrap 模块,不如继续安装剩余依赖,最大可能性排除环境问题.

那就先把 src/build.sh 构建脚本涉及到的其他命令全部安装一遍,然后再试一下吧!

除了编译 Js 的命令外,还有编译 Css 的命令,关于构建脚本 build.sh 的其他内容就是基本的复制粘贴之类的操作了.

这里省略面向搜索编程的中间过程,安装命令如下:

当我再一次运行构建脚本时,满心期待会编译成功,没想到现实再一次打脸,这时候错误更多了呢,真的是没想到!

那就继续扩大安装环境范围,这时候对整个 gitbook-plugin-theme-default 进行 npm install 安装相关依赖,这一次会发生什么情况呢?

让我们拭目以待!

命令行没有了乱七八糟的输出,世界变得安静了!

linux 命令行哲学告诉我们,没有消息就是好消息,全部安装项目环境后再次运行 src/build.sh 脚本命令行瞬间安静了!

怀着忐忑不安的心,切换到测试项目运行 gitbook serve 命令后,那一瞬间,感觉世界都静止了,奇迹就这么发生了?

终于成功了,实现默认折叠效果了吗?

为了验证是否成功实现默认折叠失效,做一次反向测试,既然默认折叠左侧菜单设置的是 false,如果设置成 true 的话,默认应该是展开状态.

重新编译后再次启动本地测试项目,如果是展开状态,那就说明成功不是偶然而是靠技巧和努力!

  • 重新编译源码

/Users/snowdreams1006/.gitbook/versions/3.2.3/node_modules/gitbook-plugin-theme-default

  • 启动本地项目

/Users/snowdreams1006/Documents/workspace/test

苦心人天不负,不是昙花一现的巧合而是货真价实的现实,就这么实现了默认折叠左侧菜单功能!

懒人直达以及回顾总结

如果你是 Gitbook 普通用户或者懒得折腾,那么推荐你直接替换掉 theme.js 文件:

  • 查看正在使用的 gitbook 版本信息
  • 打开正在使用的 gitbook 安装位置
  • 新文件替换掉原来的 _assets/website/theme.js 文件
  • 切换到测试项目验证默认折叠是否已生效

如果你不怕麻烦,喜欢折腾,那么不妨体验一下如何重新编译源码文件.

  • 查看正在使用的 gitbook 版本信息
  • 打开正在使用的 gitbook 安装位置
  • 安装 theme-default 默认主题项目所需依赖
  • 安装 build.sh 构建脚本所需依赖
  • 运行 build.sh 构建脚本重新编译
  • 切换到测试项目验证默认折叠是否已生效

值得注意的是,实现默认折叠左侧菜单功能仅仅需要添加一行代码,但是也很有可能和项目中已引入插件存在冲突,毕竟 sidebar 的状态也可以被未知代码所更改!Copy// Prepare sidebar: state and toggle buttonfunction init() { // Close sidebar as default state gitbook.storage.set('sidebar', false); // Open sidebar as default state // gitbook.storage.set('sidebar', true); // Init last state if not mobile if (!platform.isMobile()) { toggleSidebar(gitbook.storage.get('sidebar', true), false); } // Close sidebar after clicking a link on mobile $(document).on('click', '.book-summary li.chapter a', function(e) { if (platform.isMobile()) toggleSidebar(false, false); });}

最后希望本文对你有所帮助,面向搜索编程变得不可用时,自力更生也未尝不可,如果大家在使用 Gitbook 中遇到任何问题,欢迎留言评论告诉我,当然我也不一定保证解决,万一哪天心血来潮翻看一下源码就解决了呢!

家好,今天给大家介绍一款,JavaScript实现的多功能侧边导航菜单源码(图1)。送给大家哦,获取方式在本文末尾。

图1

可以菜单在左边弹出,也可以设置菜单在右边弹出(图2)

图2

图2

可以设置为子菜单重叠样式(图3)

图3

可以设置为扩展样式(图4)

图4

可以设置为直接展开样式(图5)

图5

自适应各种分辨率(图6)

图6

本模板编码:10138,需要的朋友,点击下面的链接后,搜索10138,即可获取。

「链接」