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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    调整窗体尺寸为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>

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

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

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

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

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

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

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

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

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

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

    接下来是触发器部分,首先启用了一个事件触发器,触发 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值越大代表速度越快,这个理解可能不是很准确,仅供参考。

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

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

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

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





    源代码和设计文件

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)-认知流文档 & 小议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. AVL树----java

                                                                                        AVL树----java AVL ...

  2. 解决Java compiler level does not match the version of the installed Java project facet.问题

    其实之前遇到过Java compiler level does not match the version of the installed Java project facet.这个问题,因为当时没 ...

  3. 2013 CSU校队选拔赛(1) 部分题解

    A: Decimal Time Limit: 1 Sec   Memory Limit: 128 MB Submit: 99   Solved: 10 [ Submit][ Status][ Web ...

  4. Introduction to Probability (三) Independence

    两个事件独立性的定义是:事件A的发生对事件B的发生毫无影响,即从A的发生与否.我们不能猜測出B是否发生. 从概率等式的表示来看就是B在A发生的情况下发生的概率等于B发生的概率本身. 进而引出了A与B同 ...

  5. cct,web技术

    基本信息 全国计算机等级考试二级教程——Web程序设计(2016年版)作    者:教育部考试中心 编出 版 社:高等教育出版社出版时间:20115-12-1 ISBN:9787040442991版 ...

  6. Mysql自增主键ID重新排序方法详解

    Mysql数据库表的自增主键ID号乱了,需要重新排列. 原理:删除原有的自增ID,重新建立新的自增ID. 1,删除原有主键: ALTER TABLE `table_name` DROP `id`; 2 ...

  7. Eclipse插件引入jar包的方法(转)

    搞了两天,终于找到解决办法了.原来  Eclipse 插件项目引入外面的jar包不能用   build path---->add external jars的方法. 先说明两个概念:类加载器,O ...

  8. 使用AjaxFileUpload.js实现文件异步上�

    ajax是无法提交文件的,所以在上传图片并预览的时候,我们常常使用Ifame的方法实现看似异步的效果.可是这样总不是非常方便的,AjaxFilleUpload.js对上面的方法进行了一个包装,使得我们 ...

  9. Go的String转码包

    https://github.com/qiniu/iconv https://github.com/djimenez/iconv-go 这是与go不相干的转码包:https://github.com/ ...

  10. [Android学习笔记]组合控件的使用

    组合控件的使用 开发过程中,多个UI控件需要协同工作,相互交互之后,才可完成一个完整的业务需求,此时可把这些控件封装成为一个整体,相互之间的交互逻辑封装其中,外部调用可无需关心内部逻辑,只需获取处理后 ...