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

简介: 原文:WPF界面设计技巧(7)—模拟电梯升降的缓动动画    如同Flash一样,WPF的亮点之一也在于其擅于表现平滑的动画效果,但以移动动画来说,仅凭简单的起始位置、目标位置,所产生的动画仍会非常生硬,这种动画忽略了移动开始时的加速过程与移动结束时的减速过程。
原文: WPF界面设计技巧(7)—模拟电梯升降的缓动动画

 

 

如同Flash一样,WPF的亮点之一也在于其擅于表现平滑的动画效果,但以移动动画来说,仅凭简单的起始位置、目标位置,所产生的动画仍会非常生硬,这种动画忽略了移动开始时的加速过程与移动结束时的减速过程。

 

WPF在关键帧动画中提供了样条内插(Spline)型的关键帧,用以控制变化的速率曲线,但这东西实在有些复杂,且不够形象化,我研究很久也没明白如何实现“缓入——缓出”的效果,随后我从一本经典牛X却鲜有人知的过时的FlashMX教程中提取了一个缓动函数,我们将用这个函数来较真实地模拟电梯的升降行为。

 

至于那本牛X的书,我以后会为大家介绍,我个人认为,那本书应当作为平面动画编程的必修经典,而它却被粗烂地印刷,并一直摆在书店里不引人注目的位置。

 

进入正题:

 

首先在界面设计器中添加一个 Rectangle ,用以代表直升电梯,然后添加4 RadioButton 代表几个楼层的呼叫按钮。

 

稍加美化,即为下图所示:

 

 

 

RadioButton 的样式直接用来当电梯按钮,略显生硬,我们用下面的代码来美化一下它:

 

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
        <Style TargetType="RadioButton">
            
<Setter Property="Foreground" Value="#ADB7BD"/>
            
<Setter Property="FontSize" Value="32"/>
            
<Setter Property="Cursor" Value="Hand"/>
            
<Setter Property="Template">
                
<Setter.Value>
                    
<ControlTemplate TargetType="RadioButton">
                        
<ContentPresenter/>
                        
<ControlTemplate.Triggers>
                            
<Trigger Property="IsChecked" Value="True">
                                
<Trigger.EnterActions>
                                    
<BeginStoryboard>
                                        
<Storyboard>
                                            
<ColorAnimation To="#BD5E00" Duration="0:0:0.3" BeginTime="0:0:0.1" Storyboard.TargetProperty="(Button.Foreground).(SolidColorBrush.Color)"/> 
                                        
</Storyboard>
                                    
</BeginStoryboard>
                                
</Trigger.EnterActions>
                                
<Trigger.ExitActions>
                                    
<BeginStoryboard>
                                        
<Storyboard>
                                            
<ColorAnimation Duration="0:0:0.2" BeginTime="0:0:0.2" Storyboard.TargetProperty="(Button.Foreground).(SolidColorBrush.Color)"/>
                                        
</Storyboard>
                                    
</BeginStoryboard>
                                
</Trigger.ExitActions>
                            
</Trigger>
                        
</ControlTemplate.Triggers>
                    
</ControlTemplate>
                
</Setter.Value>
            
</Setter>
        
</Style>

 

 

现在就比较帅了:

 

 

 

接下来为所有 RadioButton 添加统一的事件处理函数:

 

 

 

至此界面部分的全部代码如下,需要注意的是,所有元素都需要手动调整一下它们在Grid中的对齐方位,将其设为 Left  Top。要知道,设计器会在你拖动它们的时候为其胡乱改变对其方位,这会使你的元素没有统一的定位标准,导致几乎没法用代码统一操控它们的位置。

 

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
<Window x:Class="缓动动画.Window1"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    Title
="Window1" Height="398" Width="381" x:Name="window" Background="{StaticResource back}" Loaded="window_Loaded">
    
<Window.Resources>
        
<Style TargetType="RadioButton">
            
<Setter Property="Foreground" Value="#ADB7BD"/>
            
<Setter Property="FontSize" Value="32"/>
            
<Setter Property="Cursor" Value="Hand"/>
            
<Setter Property="Template">
                
<Setter.Value>
                    
<ControlTemplate TargetType="RadioButton">
                        
<ContentPresenter/>
                        
<ControlTemplate.Triggers>
                            
<Trigger Property="IsChecked" Value="True">
                                
<Trigger.EnterActions>
                                    
<BeginStoryboard>
                                        
<Storyboard>
                                            
<ColorAnimation To="#BD5E00" Duration="0:0:0.3" BeginTime="0:0:0.1" Storyboard.TargetProperty="(Button.Foreground).(SolidColorBrush.Color)"/> 
                                        
</Storyboard>
                                    
</BeginStoryboard>
                                
</Trigger.EnterActions>
                                
<Trigger.ExitActions>
                                    
<BeginStoryboard>
                                        
<Storyboard>
                                            
<ColorAnimation Duration="0:0:0.2" BeginTime="0:0:0.2" Storyboard.TargetProperty="(Button.Foreground).(SolidColorBrush.Color)"/>
                                        
</Storyboard>
                                    
</BeginStoryboard>
                                
</Trigger.ExitActions>
                            
</Trigger>
                        
</ControlTemplate.Triggers>
                    
</ControlTemplate>
                
</Setter.Value>
            
</Setter>
        
</Style>
    
</Window.Resources>
    
<Grid>
        
<Rectangle Fill="{StaticResource box}" Margin="45,265,0,0" Name="rectangle1" HorizontalAlignment="Left" Width="50" Height="50" VerticalAlignment="Top" />
        
<RadioButton Checked="RadioButton_Checked" HorizontalAlignment="Left" Margin="123,205,0,0" VerticalAlignment="Top">1F</RadioButton>
        
<RadioButton Checked="RadioButton_Checked" HorizontalAlignment="Left" Margin="123,125,0,0" VerticalAlignment="Top">2F</RadioButton>
        
<RadioButton Checked="RadioButton_Checked" HorizontalAlignment="Left" Margin="123,45,0,0" VerticalAlignment="Top">3F</RadioButton>
        
<RadioButton Checked="RadioButton_Checked" HorizontalAlignment="Left" Margin="123,285,0,0" VerticalAlignment="Top" IsChecked="True">B1</RadioButton>
    
</Grid>
</Window>

 

在后台书写事件处理函数代码:

 

 

 

 

请不要惊讶我使用中文命名函数,假如你看过我自己写的程序的源代码,你就会对此保持沉默。

 

这就是传说中的中文函数“开始升降”:

 

 

 

在这个函数中,我们创建了一个 Thickness 的关键帧动画,Thickness 通常用来代表一个元素的上下左右4边,比如 Border 四个边的粗细就是用 Thickness 描述的,而这里的 Margin 属性也是 Thickness 类型。

 

一些要点我写在了图里,这里就不累述了。

 

“缓动计算”,是的,又一个神奇的中文函数,你可以在下面完整的源码中看看它是如何运算的,至少我是对它的内容毫无兴趣。

 

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Animation;

namespace 缓动动画
img_405b18b4b6584ae338e0f6ecaf736533.gifimg_1c53668bcee393edac0d7b3b3daff1ae.gif
{
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif    
/**//// <summary>
    
/// Window1.xaml 的交互逻辑
    
/// </summary>

    public partial class Window1 : Window
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif    
{
        
public Window1()
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
            InitializeComponent();
        }


        
private void window_Loaded(object sender, RoutedEventArgs e)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{

        }


        Storyboard s 
= new Storyboard();

        
public double 缓动计算(TimeSpan 总时间, TimeSpan 现在时间, double 初始值, double 变动量)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
            var t 
= 现在时间.TotalSeconds /( 总时间.TotalSeconds / 2);
            
if (t < 1return (变动量 / 2 * Math.Pow(t, 5+ 初始值);
            
return (变动量 / 2 * (Math.Pow(t - 25+ 2+ 初始值);
        }


        
private void RadioButton_Checked(object sender, RoutedEventArgs e)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
            开始升降((sender 
as RadioButton).Margin.Top - 5);
        }


        
public void 开始升降(double 目标高度)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
            ThicknessAnimationUsingKeyFrames d 
= new ThicknessAnimationUsingKeyFrames();
            d.Duration 
= new Duration(TimeSpan.FromSeconds(Math.Abs((目标高度-rectangle1.Margin.Top)/80)));
            
for (int i = 0; i < 100; i++)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
{
                var t 
= new Thickness();
                t.Left 
= rectangle1.Margin.Left;
                t.Right 
= rectangle1.Margin.Right;
                t.Bottom 
= rectangle1.Margin.Bottom;
                t.Top
=缓动计算(d.Duration.TimeSpan, TimeSpan.FromSeconds(d.Duration.TimeSpan.TotalSeconds / 100 * i), rectangle1.Margin.Top, 目标高度-rectangle1.Margin.Top);
                d.KeyFrames.Add(
new LinearThicknessKeyFrame(t, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(d.Duration.TimeSpan.TotalSeconds / 100 * i))));
            }

            s.Children.Add(d);
            Storyboard.SetTargetName(d, rectangle1.Name);
            Storyboard.SetTargetProperty(d, 
new PropertyPath(Rectangle.MarginProperty));
            s.Begin(rectangle1);
        }

    }

}

 

 

现在编译运行吧,随便点击一个楼层,你将会看到电梯平缓的起步,然后平缓的停靠在你所选的楼层上。

 

 

 

 

当然,即使是这样具有缓动效果的电梯,乘客也是很难生还的,但至少会比生硬的上飞下坠要好很多啦。

 

源代码下载

目录
相关文章
|
14天前
|
C#
WPF —— 动画缩放变换
`ScaleTransform`用于二维x-y坐标系中对象的缩放,可沿X或Y轴调整。在故事板中,通过RenderTransform.ScaleX和ScaleY属性控制缩放。示例代码展示了如何设置按钮的RenderTransformOrigin、Background等属性,并通过LayoutTransform应用ScaleTransform。当鼠标进入按钮时,EventTrigger启动DoubleAnimation实现X和Y轴的缩放动画。最后,展示了如何将动画集成到自定义按钮样式中。
12 0
|
7月前
|
C#
WPF技术之动画系列-上下运动
本例子展现动画小球上下循环运动
125 0
|
API C# 索引
WPF中的动画——(一)基本概念
原文:WPF中的动画——(一)基本概念 WPF的一个特点就是支持动画,我们可以非常容易的实现漂亮大方的界面。首先,我们来复习一下动画的基本概念。计算机中的动画一般是定格动画,也称之为逐帧动画,它通过每帧不同的图像连续播放,从而欺骗眼和脑产生动画效果。
1036 0
|
C# 测试技术
WPF中的动画——(二)From/To/By 动画
原文:WPF中的动画——(二)From/To/By 动画 我们所实现的的动画中,很大一部分是让一个属性在起始值和结束值之间变化,例如,我在前文中实现的改变宽度的动画:     var widthAnimation = new DoubleAnimation()    {        From = 0,        To = 320,        Duration = TimeSpan.
970 0
|
算法 C#
WPF中的动画——(五)关键帧动画
原文:WPF中的动画——(五)关键帧动画 与 From/To/By 动画类似,关键帧动画以也可以以动画形式显示目标属性值。 和From/To/By 动画不同的是, From/To/By 动画只能控制在两个状态之间变化,而关键帧动画则可以在多个状态之间变化,例如,对于前面那个改变按钮宽度的例子,如果我们要实现如下效果: 在2秒时将宽度从 0变为350 在7秒时将宽度变为50 在9秒的时候将其宽度变为200 虽然我们可以用三个From/To/By 动画组合实现类似效果,但是这样一来麻烦,二来要感知动画完成事件,不方便在XAML中使用。
1330 0
|
C#
WPF中的动画——(四)缓动函数
原文:WPF中的动画——(四)缓动函数 缓动函数可以通过一系列公式模拟一些物理效果,如实地弹跳或其行为如同在弹簧上一样。它们一般应用在From/To/By动画上,可以使得其动画更加平滑。     var widthAnimation = new DoubleAnimation()    {    ...
933 0
|
C#
WPF中的动画——(三)时间线(TimeLine)
原文:WPF中的动画——(三)时间线(TimeLine) 时间线(TimeLine)表示时间段。 它提供的属性可以让控制该时间段的长度、开始时间、重复次数、该时间段内时间进度的快慢等等。在WPF中内置了如下几种TimeLine: AnimationTimeline :前面已经介绍过,主要用于属性的过渡,这种是最常见的动画。
1420 0
|
15天前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
|
4月前
|
C#
浅谈WPF之装饰器实现控件锚点
使用过visio的都知道,在绘制流程图时,当选择或鼠标移动到控件时,都会在控件的四周出现锚点,以便于修改大小,移动位置,或连接线等,那此功能是如何实现的呢?在WPF开发中,想要在控件四周实现锚点,可以通过装饰器来实现,今天通过一个简单的小例子,简述如何在WPF开发中,应用装饰器,仅供学习分享使用,如有不足之处,还请指正。
65 1
|
8月前
|
C# Windows
WPF技术之图形系列Polygon控件
WPF Polygon是Windows Presentation Foundation (WPF)框架中的一个标记元素,用于绘制多边形形状。它可以通过设置多个点的坐标来定义多边形的形状,可以绘制任意复杂度的多边形。
456 0