WPF界面设计技巧(3)—实现不规则动画按钮

时间:2022-06-12 00:35:30

原文:WPF界面设计技巧(3)—实现不规则动画按钮

WPF界面设计技巧(3)—实现不规则动画按钮

    发布了定义WPF按钮的教程后,有朋友问能否实现不规则形状的按钮,今天我们就来讲一下不规则按钮的制作。

    不规则按钮的做法实际上和先前我们做不规则窗体的方法差不多,只是为按钮弄个不是那么方方正正的背景而已。

    我们这次沿用自定义窗体时的设计图形,设计一个动态的不规则按钮,在这个示例中我们要将先前设计的整个图形作为按钮,并让外围的圆环始终围绕中心圆形旋转,在鼠标移入时,还要产生颜色变化及发光效果。

    首先用 Microsoft Expression Design 2 打开上次的设计文件,将图层名称由“back”改为“sphericity”。

    然后选中圆环部分,按 Ctrl + X 将其剪切,新建一个图层,命名为“ring”,将圆环粘贴进该层,并把该层移动到“sphericity”层下面。

    再选中圆环部分,如图所示地移动它,将其内环贴近圆形的边缘。

    WPF界面设计技巧(3)—实现不规则动画按钮

    然后在“ring”层新创建一个圆形,填充色设为深红色,无描边。

    WPF界面设计技巧(3)—实现不规则动画按钮

    在图层面板上展开“ring”层,将新创建的圆形挪到圆环下方。

    WPF界面设计技巧(3)—实现不规则动画按钮

    参考“sphericity”层的圆形中心点坐标值,将新创建的这个圆形中心点与之重合。

    你可以先选中“sphericity”层的圆形,然后复制其X坐标值,再选中新创建的圆形,选中其X坐标值,按 Ctrl + V 粘贴以覆盖其先前值,然后再以同样的方法处理Y坐标值。

    WPF界面设计技巧(3)—实现不规则动画按钮WPF界面设计技巧(3)—实现不规则动画按钮

    当中心点重合后,隐藏“sphericity”层,等比例放大这个圆形使之边缘盖过圆环。

    WPF界面设计技巧(3)—实现不规则动画按钮

    然后将该圆形的不透明度修改为0。

    WPF界面设计技巧(3)—实现不规则动画按钮

    同时选中圆环与这个看不见的圆形,点击右键,在弹出菜单中选择“组合”。

    WPF界面设计技巧(3)—实现不规则动画按钮

    这样这个看不见的圆形就成为了圆环的旋转参照物,重新显示出“sphericity”层,你可以现在就尝试旋转一下圆环,你会看到圆环始终都会贴紧并围绕中间的圆形旋转。

    WPF界面设计技巧(3)—实现不规则动画按钮

    设计部分做完了,现在导出资源字典。

    WPF界面设计技巧(3)—实现不规则动画按钮

    打开 Microsoft Visual Studio 2008 新建一个WPF应用程序,将导出的资源字典导入解决方案。

    WPF界面设计技巧(3)—实现不规则动画按钮

    在App.xaml中添加对资源字典的引用。

    WPF界面设计技巧(3)—实现不规则动画按钮

    调整窗体尺寸为400×400,在代码视图中 <Grid> … </Grid> 标记内贴入如下代码:

Code
        <Button Height="300" Width="300" Margin="-59,-57,-18,-94" Name="button1" Cursor="Hand">
            <Button.Template>
                <ControlTemplate>
                    <!--容器-->
                    <Canvas Height="300" Width="300">
                        <!--圆环-->
                        <Rectangle x:Name="ring" Canvas.Top="0" Canvas.Left="0" Fill="{StaticResource ring}"  Height="300" Width="300" >
                            <Rectangle.RenderTransform>
                                <RotateTransform Angle="135" CenterX="150" CenterY="150"/>
                            </Rectangle.RenderTransform>
                        </Rectangle>
                        <!--圆形及文字-->
                        <Rectangle x:Name="sphericity" Canvas.Top="33" Canvas.Left="33" Fill="{StaticResource sphericity}" Height="234" Width="234">
                            <Rectangle.BitmapEffect>
                                <OuterGlowBitmapEffect GlowColor="Orange"  GlowSize="0"/>
                            </Rectangle.BitmapEffect>
                        </Rectangle>
                    </Canvas>
                    <!--触发器-->
                    <ControlTemplate.Triggers>
                        <!--载入事件触发器-->
                        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimationUsingKeyFrames Duration="0:0:0.6" RepeatBehavior="Forever" Storyboard.TargetName="ring" Storyboard.TargetProperty="(Rectangle.RenderTransform).(RotateTransform.Angle)">
                                        <DoubleAnimationUsingKeyFrames.KeyFrames>
                                            <SplineDoubleKeyFrame KeyTime="0:0:0" Value="135" KeySpline="0.5,0,0,0.5"/>
                                            <SplineDoubleKeyFrame KeyTime="0:0:0.6" Value="495" KeySpline="0,0.5,0.5,0"/>
                                        </DoubleAnimationUsingKeyFrames.KeyFrames>
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <!--鼠标移入触发器-->
                        <Trigger Property="IsMouseOver" Value="True">
                            <Trigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation To="12" Duration="0:0:0.1" Storyboard.TargetName="sphericity" Storyboard.TargetProperty="(Rectangle.BitmapEffect).(OuterGlowBitmapEffect.GlowSize)"/>
                                        <ColorAnimation To="#FFEB55" Duration="0:0:0.1" Storyboard.TargetName="sphericity" Storyboard.TargetProperty="(Rectangle.Fill).(DrawingBrush.Drawing).(DrawingGroup.Children)[0].(GeometryDrawing.Brush).(RadialGradientBrush.GradientStops)[0].(GradientStop.Color)"/>
                                        <ColorAnimation To="#FFC955" Duration="0:0:0.1" Storyboard.TargetName="sphericity" Storyboard.TargetProperty="(Rectangle.Fill).(DrawingBrush.Drawing).(DrawingGroup.Children)[0].(GeometryDrawing.Brush).(RadialGradientBrush.GradientStops)[1].(GradientStop.Color)"/>
                                        <ColorAnimation To="#D79248" Duration="0:0:0.1" Storyboard.TargetName="sphericity" Storyboard.TargetProperty="(Rectangle.Fill).(DrawingBrush.Drawing).(DrawingGroup.Children)[0].(GeometryDrawing.Brush).(RadialGradientBrush.GradientStops)[2].(GradientStop.Color)"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation Duration="0:0:0.1" Storyboard.TargetName="sphericity" Storyboard.TargetProperty="(Rectangle.BitmapEffect).(OuterGlowBitmapEffect.GlowSize)"/>
                                        <ColorAnimation Duration="0:0:0.1" Storyboard.TargetName="sphericity" Storyboard.TargetProperty="(Rectangle.Fill).(DrawingBrush.Drawing).(DrawingGroup.Children)[0].(GeometryDrawing.Brush).(RadialGradientBrush.GradientStops)[0].(GradientStop.Color)"/>
                                        <ColorAnimation Duration="0:0:0.1" Storyboard.TargetName="sphericity" Storyboard.TargetProperty="(Rectangle.Fill).(DrawingBrush.Drawing).(DrawingGroup.Children)[0].(GeometryDrawing.Brush).(RadialGradientBrush.GradientStops)[1].(GradientStop.Color)"/>
                                        <ColorAnimation Duration="0:0:0.1" Storyboard.TargetName="sphericity" Storyboard.TargetProperty="(Rectangle.Fill).(DrawingBrush.Drawing).(DrawingGroup.Children)[0].(GeometryDrawing.Brush).(RadialGradientBrush.GradientStops)[2].(GradientStop.Color)"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.ExitActions>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Button.Template>
        </Button>

还是回过头在研究代码,先编译并运行,可以看到下图这样的界面,其中的圆环在不停的转动。

WPF界面设计技巧(3)—实现不规则动画按钮

鼠标移入时圆形会变色,并且外发光:

WPF界面设计技巧(3)—实现不规则动画按钮

好了,下面只讲解一下代码中需要注意的地方,重复的知识不再累述,大家可以参考先前的两篇文章。

WPF界面设计技巧(3)—实现不规则动画按钮

Canvas 是一个简单的容器元素,它内部的元素以简单的坐标位置来描述。

其内部放置了两个矩形元素 Rectangle ,我们用 Rectangle 分别装载圆环和圆形及文字的图像。

WPF界面设计技巧(3)—实现不规则动画按钮

通过 Rectangle.RenderTransform  属性可以对 Rectangle 的外形进行转换调整,其功能类似 Photoshop 中的“*变换”,在这里使用 RotateTransform 来改变角度。

“CenterX="150" CenterY="150"”设置了旋转中心的坐标值,我们之前曾做过一个隐形的旋转参照,所以可以肯定我们的图形中心就是旋转的中心,现在我们的图片被设置为300×300大小了,所以中心坐标就是150,150。

Angle 属性指定了旋转的角度,这里我设置为135是为了让它正好旋转到下面这样的角度。

WPF界面设计技巧(3)—实现不规则动画按钮

因为我们后面将会做动画使其顺时针旋转,受地心引力的影响,顺时针旋转时这个角度会是旋转力度的一个分水岭,越过这个角度将会使运动较为吃力,而超过180度以后将会加速运动,我们可以通过动画的缓动值设定来粗略模拟这一物理现象。

WPF界面设计技巧(3)—实现不规则动画按钮

顺带提一下两个图形的尺寸设定,上面一组是圆环的,下面的是圆形及文字的,圆环的300×300是我任意设置的,我觉得这个大小当个按钮还算说的过去,下面的234×234是依据原图中的尺寸,这里的按比例缩小后,又进行了一些微调后确立的,设定好它的尺寸后,为了使它位于圆环图形中心,需要调整它在Canvas 中的顶部和左部坐标值均为33,即 (300-234)/2。

WPF界面设计技巧(3)—实现不规则动画按钮

接下来是触发器部分,首先启用了一个事件触发器,触发 FrameworkElement.Loaded 事件,我们要在程序载入完毕时就启动圆环的旋转动画,并使之一直运转。

为什么要使用 FrameworkElement.Loaded 事件?我不知道,我一直认为应该使用按钮的 Loaded 事件,可是总会看到一些BT的错误信息,导致无法正常运转,后来从Blend里学来的 FrameworkElement.Loaded ,那就用它吧,好用就得了。

这次与以往不同,我们采用了关键帧动画 DoubleAnimationUsingKeyFrames ,主要是为了达成动画的缓动和加速效果,如前所述,在这里我们要让圆环旋转起来。

“RepeatBehavior="Forever"”属性指定动画永远执行。

“SplineDoubleKeyFrame”是关键帧,这里只有两个关键帧,通过这两个关键帧,让圆环从之前设定好的135度转到495(135 + 360)度,其 KeySpline 是指示缓动曲线的贝塞尔控制点坐标值,具体设置方法得参考MSDN,我自己也晕晕乎乎的,我大体上认为这4个double数值是2组数据,即为“X1,Y1,X2,Y2”,分别代表开始时和结束时的速度,其中每组的X值越大代表速度越慢,Y值越大代表速度越快,这个理解可能不是很准确,仅供参考。

WPF界面设计技巧(3)—实现不规则动画按钮

鼠标移入事件的触发器大家应该很熟悉了,不过大家看到这里那几个动画行的后面一大段,可能都要痉挛了:

(Rectangle.Fill).(DrawingBrush.Drawing).(DrawingGroup.Children)[0].(GeometryDrawing.Brush).(RadialGradientBrush.GradientStops)[0].(GradientStop.Color)

诸如这样长的路径声明是非常恶心人的,没办法,因为我们绘制的图形比较复杂,所以只能使用复杂的路径语句来描述了,可以参考如下选取路径的方式:

WPF界面设计技巧(3)—实现不规则动画按钮

好了,别的就没什么了,我继续干活去啦。





源代码和设计文件

WPF界面设计技巧(3)—实现不规则动画按钮的更多相关文章

  1. WPF界面设计技巧(7)—模拟电梯升降的缓动动画

    原文:WPF界面设计技巧(7)-模拟电梯升降的缓动动画 如同Flash一样,WPF的亮点之一也在于其擅于表现平滑的动画效果,但以移动动画来说,仅凭简单的起始位置.目标位置,所产生的动画仍会非常生硬,这 ...

  2. WPF界面设计技巧(1)—不规则窗体图文指南

    原文:WPF界面设计技巧(1)-不规则窗体图文指南 初到园子,奉上第一篇入门级教程,请勿见笑. 以往WinForm编程中,实现不规则窗体是有一定难度的,更难的是不规则窗体的边缘抗锯齿及局部透明处理.而 ...

  3. WPF界面设计技巧(2)—自定义漂亮的按钮样式

    原文:WPF界面设计技巧(2)-自定义漂亮的按钮样式 上次做了个很酷的不规则窗体,这次我们来弄点好看的按钮出来,此次将采用纯代码来设计按钮样式,不需要 Microsoft Expression Des ...

  4. WPF界面设计技巧(5)—自定义列表项呈现内容

    原文:WPF界面设计技巧(5)-自定义列表项呈现内容 接续上次的程序,稍微改动一下原有样式,并添加一个数据模板,我们就可以达成下面这样的显示功能: 鼠标悬停于文件列表项上,会在工具提示中显示图像缩略图 ...

  5. WPF界面设计技巧(11)-认知流文档 &amp&semi; 小议WPF的野心

    原文:WPF界面设计技巧(11)-认知流文档 & 小议WPF的野心 流文档是WPF中的一种独特的文档承载格式,它的书写和呈现方式都很像HTML,它也几乎具备了HTML的绝大多数优势,并提供了更 ...

  6. WPF界面设计技巧(10)-样式的继承

    原文:WPF界面设计技巧(10)-样式的继承 PS:现在我的MailMail完工了,进入内测阶段了,终于可以腾出手来写写教程了哈,关于MailMail的介绍及内测程序索取:http://www.cnb ...

  7. WPF界面设计技巧(9)—使用UI自动化布局

    原文:WPF界面设计技巧(9)-使用UI自动化布局 最近一直没时间更新这系列文章,因为我一直在埋头编写我的第一个WPF应用程序:MailMail 今天开始编写附属的加密/解密工具,对UI自动化布局有些 ...

  8. WPF界面设计技巧(8)—自制山寨版CheckListBox

    原文:WPF界面设计技巧(8)-自制山寨版CheckListBox 近年来IT市场山寨横行啊,我们今天也来发扬一下山寨精神,搞个自制的CheckListBox出来. 喏,CheckListBox 就是 ...

  9. WPF界面设计技巧(6)—玩玩数字墨水手绘涂鸦

    原文:WPF界面设计技巧(6)-玩玩数字墨水手绘涂鸦 想让你的程序支持鼠标及手写笔涂鸦吗?只要敲入“<InkCanvas/>”这几个字符,你就会领悟什么叫“很好很强大”,今天我们来做一个手 ...

随机推荐

  1. oracle 12c中的隐含列

      Invisible Columns 使用select * from ,desc 等看不到该列, DROP TABLE tab1 PURGE; CREATE TABLE tab1 ( id NUMB ...

  2. &lbrack;改善Java代码&rsqb;适时选择不同的线程池来实现

    Java的线程池实现从最根本上来说只有两个:ThreadPoolExecutor类和ScheduledThreadPoolExecutor类,这两个类还是父子关系,但是Java为了简化并行计算,还提供 ...

  3. oracle12 pl&sol;sql

    pl/sql块介绍 介绍   块(block)是pl/sql的基本程序单元,编写pl/sql程序实际上就是编写pl/sql块,要完成相对简单的应用功能,可能只需要编写一个pl/sql块,但是如果想要实 ...

  4. 【学习】jquery&period;placeholder&period;js让IE浏览器支持html5的placeholder

    type为text或password的input,其在实际应用时,往往有一个占位符,类似这样的: 在没有html5前,一般写成value,用js实现交互,文本框获得焦点时,提示文字消失,失去焦点时,文 ...

  5. pat1121-1131

    1121 #include<cmath> #include<map> #include<iostream> #include<cstring> #inc ...

  6. 最大流:Dinic算法

    蒟蒻居然今天第一次写网络流 我太弱啦! 最大流问题有很多解法 虽然isap常数巨小 但是连dinic都写挂的本蒟蒻并不会orz 那么我们选用比较好实现的dinic来解决最大流问题 来一段定义:    ...

  7. Python 最佳实践指南 2018 学习笔记

    基础信息 版本 Python 2.7 Python 3.x Python2.7 版本在 2020 年后不再提供支持,建议新手使用 3.x 版本进行学习 实现 CPython:Python的标准实现: ...

  8. MyBatis延迟加载和缓存&lpar;4&rpar;

    一.项目创建 1.项目目录结构 2.数据库配置和上一篇的一样,这里不再描述.下面创建mybatis配置文件SqlMapConfig.xml <?xml version="1.0&quo ...

  9. iOS禁用系统休眠

    [UIApplicationsharedApplication].idleTimeDisabled= YES

  10. NOIP树上问题总结

    这几年考了好几次树上问题: NOIP2012 疫情控制(二分答案+倍增+贪心) NOIP2013 货车运输(最大生成树+倍增) NOIP2014 联合权值(勉强算作树形dp的傻逼题) NOIP2015 ...