在2010年之前,我都是用Blend创建动画,添加触发器实现自动动画,后来写成代码创建的方式。如今Blend已经集成到Visual Studio安装镜像中了,最新的VS2015安装,Blend的操作界面已经十分接近VS,难怪有人吐槽Win10 Insider Preview(10025之前版本)的图标设计都是程序员搞出来的——这靠近VS的界面是怎么回事,不是应该更接近于Photoshop、Flash什么的吗?

如果你们公司没有大牛设计师,估计是不太可能使用Blend的。

这是一个点击Show或者Hide按钮使页面中蓝色Grid淡出或者淡入显示的例子。创建两个Storyboard,在2秒位置分别设置蓝色Grid的Opacity为100%和0%。然后在按钮的Click事件处理代码中找到Storyboard资源并执行。

        private void Show_Click(object sender, RoutedEventArgs e)
{
((Storyboard)(this.Resources["ShowGrid"])).Begin();
} private void Hide_Click(object sender, RoutedEventArgs e)
{
((Storyboard)(this.Resources["HideGrid"])).Begin();
}

作为码农的封装癖好,为了更方便的调用代码动画,我写了UIElement的扩展方法,来扩充界面元素的动画调用。

grid.PlayFadeMoveAnimation(TimeSpan.FromMilliseconds(), destOpacity: , fromX: , destX: ,easingFunctionForMove: new CircleEase())
.Completed += (ss, se) =>{/*动画播放完成*/};

PlayFadeMoveAnimation扩展方法就是播放淡入淡出并且做移动动画的效果。当时受到了JQuery的“连续方法调用”的影响,这个方法会返回个Storyboard。

上面代码的解释是grid立即播放动画效果:在800ms里,使grid的Opacity变成1(淡入效果),并且水平坐标从50移动到0(右侧50距离开始移动到原始位置),移动时候使用默认的CircleEase函数效果。

例子

1.闪烁

grid.PlayTwinklingAnimation(new[] { new TimeSpan(0, 0, 1), new TimeSpan(0, 0, 1) }, new[] { 0.1, 1.0 }, RepeatBehavior.Forever);

2.淡入淡出

grid.PlayFadeAnimation(TimeSpan.FromMilliseconds(500), 1);

grid.PlayFadeAnimation(TimeSpan.FromMilliseconds(500), 0);

3.淡入淡出平移

grid.PlayFadeMoveAnimation(TimeSpan.FromMilliseconds(800), destOpacity: 1, fromX: 50, destX: 0, easingFunctionForMove: new CircleEase());

grid.PlayFadeMoveAnimation(TimeSpan.FromMilliseconds(800), destOpacity: 0, fromX: 0, destX: 50);

4.平移缩放

grid.PlayMoveAnimation(TimeSpan.FromMilliseconds(500), destX: 0, destY: 0, scaleX: 1, scaleY: 1);

grid.PlayMoveAnimation(TimeSpan.FromMilliseconds(500), destX: 500, destY: 500, scaleX: 0.2, scaleY: 0.2);

下面是实现代码:

 using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation; public static class AnimationHelper
{
#region Animation private const double DoubleEpsilon = 0.001; /// <summary>
/// 播放闪烁动画
/// </summary>
/// <param name="uiElement">作用UI元素</param>
/// <param name="timeSpanValues">播放时间</param>
/// <param name="opacityValues">透明类清单</param>
/// <param name="repeatBehavior">重复次数</param>
/// <param name="autoReserse">是否翻转播放</param>
/// <returns>当成功播放动画时会返回一个Storyboard对象</returns>
public static Storyboard PlayTwinklingAnimation(
this UIElement uiElement,
TimeSpan[] timeSpanValues,
double[] opacityValues,
RepeatBehavior repeatBehavior,
bool autoReserse = false)
{
if (timeSpanValues == null || opacityValues == null
|| timeSpanValues.Length ==
|| timeSpanValues.Length != opacityValues.Length)
{
return null;
} var animation = new DoubleAnimationUsingKeyFrames();
for (int i = ; i < timeSpanValues.Length; i++)
{
var keyframe = new SplineDoubleKeyFrame { KeyTime = timeSpanValues[i], Value = opacityValues[i] };
animation.KeyFrames.Add(keyframe);
} animation.RepeatBehavior = repeatBehavior;
animation.AutoReverse = autoReserse; Storyboard.SetTarget(animation, uiElement);
Storyboard.SetTargetProperty(animation, "Opacity"); var storyboard = new Storyboard();
storyboard.Children.Add(animation);
storyboard.Begin(); return storyboard;
} /// <summary>
/// 播放淡入淡出动画
/// </summary>
/// <param name="uiElement">作用UI元素</param>
/// <param name="timeSpan">动画时长</param>
/// <param name="destOpacity">目标的Opacity</param>
/// <param name="changeVisibility">是否允许改变UI元素的Visibility属性,建议“是”</param>
/// <param name="easingFunction">easingFunction</param>
/// <returns>当成功播放动画时会返回一个Storyboard对象</returns>
public static Storyboard PlayFadeAnimation(
this UIElement uiElement,
TimeSpan timeSpan,
double destOpacity,
bool changeVisibility = true,
EasingFunctionBase easingFunction = null)
{
if (changeVisibility)
{
if (uiElement.Visibility == Visibility.Collapsed &&
destOpacity < DoubleEpsilon)
{
uiElement.Opacity = ;
return null;
} if (Math.Abs(uiElement.Opacity - destOpacity) < DoubleEpsilon)
{
uiElement.Visibility = destOpacity < DoubleEpsilon ? Visibility.Collapsed : Visibility.Visible;
return null;
}
}
else if (Math.Abs(uiElement.Opacity - destOpacity) < DoubleEpsilon)
{
return null;
} if (changeVisibility &&
destOpacity > DoubleEpsilon &&
uiElement.Visibility != Visibility.Visible)
{
uiElement.Opacity = ;
uiElement.Visibility = Visibility.Visible;
} var animation = new DoubleAnimation
{
From = uiElement.Opacity,
To = destOpacity,
Duration = new Duration(timeSpan)
}; if (easingFunction != null)
{
animation.EasingFunction = easingFunction;
} Storyboard.SetTarget(animation, uiElement);
Storyboard.SetTargetProperty(animation, "Opacity"); animation.Completed += (sender, e) =>
{
uiElement.Opacity = destOpacity; if (changeVisibility)
{
if (destOpacity < DoubleEpsilon)
{
uiElement.Visibility = Visibility.Collapsed;
}
}
}; var storyboard = new Storyboard();
storyboard.Children.Add(animation);
storyboard.FillBehavior = FillBehavior.HoldEnd;
storyboard.Begin(); return storyboard;
} /// <summary>
/// 播放移动动画(注:参数destX、destY、scaleX和scaleY至少指定一个)
/// </summary>
/// <param name="frameworkElement">作用UI元素</param>
/// <param name="timeSpan">动画时长</param>
/// <param name="destX">目标的坐标X</param>
/// <param name="destY">目标的坐标Y</param>
/// <param name="scaleX">目标的缩放X</param>
/// <param name="scaleY">目标的缩放Y</param>
/// <param name="centerX">目标的缩放中心点X</param>
/// <param name="centerY">目标的缩放中心点Y</param>
/// <param name="easingFunction">EasingFunction</param>
/// <returns>当成功播放动画时会返回一个Storyboard对象</returns>
public static Storyboard PlayMoveAnimation(
this FrameworkElement frameworkElement,
TimeSpan timeSpan,
double destX = double.NaN,
double destY = double.NaN,
double scaleX = double.NaN,
double scaleY = double.NaN,
double centerX = double.NaN,
double centerY = double.NaN,
EasingFunctionBase easingFunction = null)
{
if (double.IsNaN(destX) && double.IsNaN(destY) && double.IsNaN(scaleX) && double.IsNaN(scaleY))
{
throw new ArgumentException("destX destY scaleX scaleX");
} var storyboard = new Storyboard(); var translateTransform = frameworkElement.GetTranform<TranslateTransform>();
if (!double.IsNaN(destX))
{
if (Math.Abs(translateTransform.X - destX) > DoubleEpsilon)
{
var animation = new DoubleAnimation
{
From = translateTransform.X,
To = destX,
Duration = new Duration(timeSpan),
EasingFunction = easingFunction
}; Storyboard.SetTarget(animation, translateTransform);
Storyboard.SetTargetProperty(animation, "X");
storyboard.Children.Add(animation);
}
} if (!double.IsNaN(destY))
{
if (Math.Abs(translateTransform.Y - destY) > DoubleEpsilon)
{
var animation = new DoubleAnimation
{
From = translateTransform.Y,
To = destY,
Duration = new Duration(timeSpan),
EasingFunction = easingFunction
}; Storyboard.SetTarget(animation, translateTransform);
Storyboard.SetTargetProperty(animation, "Y");
storyboard.Children.Add(animation);
}
} var scaleTransform = frameworkElement.GetTranform<ScaleTransform>();
if (!double.IsNaN(centerX)) scaleTransform.CenterX = centerX;
if (!double.IsNaN(centerY)) scaleTransform.CenterX = centerY;
if (!double.IsNaN(scaleX))
{
if (Math.Abs(scaleTransform.ScaleX - scaleX) > DoubleEpsilon)
{
var animation = new DoubleAnimation
{
From = scaleTransform.ScaleX,
To = scaleX,
Duration = new Duration(timeSpan),
EasingFunction = easingFunction
}; Storyboard.SetTarget(animation, scaleTransform);
Storyboard.SetTargetProperty(animation, "ScaleX");
storyboard.Children.Add(animation);
}
} if (!double.IsNaN(scaleY))
{
if (Math.Abs(scaleTransform.ScaleY - scaleY) > DoubleEpsilon)
{
var animation = new DoubleAnimation
{
From = scaleTransform.ScaleY,
To = scaleY,
Duration = new Duration(timeSpan),
EasingFunction = easingFunction
}; Storyboard.SetTarget(animation, scaleTransform);
Storyboard.SetTargetProperty(animation, "ScaleY");
storyboard.Children.Add(animation);
}
} if (storyboard.Children.Count > )
{
storyboard.Begin();
return storyboard;
} return null;
} /// <summary>
/// 播放一个包含透明度和移动变化的动画
/// </summary>
/// <param name="uiElement">作用UI元素</param>
/// <param name="timeSpan">动画时长</param>
/// <param name="destOpacity">目标Opacity</param>
/// <param name="changeVisibility">是否允许改变UI元素的Visibility属性,建议“是”</param>
/// <param name="fromX">起始坐标X</param>
/// <param name="fromY">起始坐标Y</param>
/// <param name="destX">目标的坐标X</param>
/// <param name="destY">目标的坐标Y</param>
/// <param name="easingFunctionForFade">EasingFunction</param>
/// <param name="easingFunctionForMove">EasingFunction</param>
/// <returns>当成功播放动画时会返回一个Storyboard对象</returns>
public static Storyboard PlayFadeMoveAnimation(
this UIElement uiElement,
TimeSpan timeSpan,
double destOpacity,
bool changeVisibility = true,
double fromX = double.NaN,
double fromY = double.NaN,
double destX = double.NaN,
double destY = double.NaN,
EasingFunctionBase easingFunctionForFade = null,
EasingFunctionBase easingFunctionForMove = null
)
{
var storyboard = new Storyboard { FillBehavior = FillBehavior.HoldEnd }; if (changeVisibility &&
destOpacity > DoubleEpsilon &&
uiElement.Visibility != Visibility.Visible)
{
uiElement.Opacity = ;
uiElement.Visibility = Visibility.Visible;
}
var fadeAnimation = new DoubleAnimation
{
From = uiElement.Opacity,
To = destOpacity,
Duration = new Duration(timeSpan)
};
if (easingFunctionForFade != null)
{
fadeAnimation.EasingFunction = easingFunctionForFade;
}
Storyboard.SetTarget(fadeAnimation, uiElement);
Storyboard.SetTargetProperty(fadeAnimation, "Opacity");
fadeAnimation.Completed += (sender, e) =>
{
uiElement.Opacity = destOpacity; if (changeVisibility)
{
if (destOpacity < DoubleEpsilon)
{
uiElement.Visibility = Visibility.Collapsed;
}
}
};
storyboard.Children.Add(fadeAnimation); if (!double.IsNaN(destX) || !double.IsNaN(destY))
{
var translateTransform = uiElement.GetTranform<TranslateTransform>();
if (!double.IsNaN(destX))
{
var x = double.IsNaN(fromX) ? translateTransform.X : fromX;
if (Math.Abs(x - destX) > DoubleEpsilon)
{
var animation = new DoubleAnimation
{
From = x,
To = destX,
Duration = new Duration(timeSpan),
EasingFunction = easingFunctionForMove
}; Storyboard.SetTarget(animation, translateTransform);
Storyboard.SetTargetProperty(animation, "X");
storyboard.Children.Add(animation);
}
}
if (!double.IsNaN(destY))
{
var y = double.IsNaN(fromY) ? translateTransform.X : fromY;
if (Math.Abs(y - destY) > DoubleEpsilon)
{
var animation = new DoubleAnimation
{
From = y,
To = destY,
Duration = new Duration(timeSpan),
EasingFunction = easingFunctionForMove
}; Storyboard.SetTarget(animation, translateTransform);
Storyboard.SetTargetProperty(animation, "Y");
storyboard.Children.Add(animation);
}
}
} if (storyboard.Children.Count > )
{
storyboard.Begin();
return storyboard;
} return null;
} #endregion #region Transform helper /// <summary>
/// 获得或创建一个新的Transform对象
/// </summary>
/// <typeparam name="T">指定一个Transform类型</typeparam>
/// <param name="uiElement">UI元素</param>
/// <returns>一个Transform对象</returns>
public static T GetTranform<T>(this UIElement uiElement)
where T : Transform, new()
{
if (uiElement.RenderTransform == null)
{
var newTransformGroup = new TransformGroup();
var newTransfrom = new T();
newTransformGroup.Children.Add(newTransfrom); uiElement.RenderTransform = newTransformGroup;
return newTransfrom;
} var transformGroup = uiElement.RenderTransform as TransformGroup;
if (transformGroup != null)
{
if (transformGroup is T)
{
return transformGroup as T;
} var r = GetTranform<T>(transformGroup);
if (r != null)
{
return r;
} var newTransfrom = new T();
transformGroup.Children.Add(newTransfrom);
return newTransfrom;
} var transform = uiElement.RenderTransform as T;
if (transform != null)
{
return transform;
} var newTransformGroup1 = new TransformGroup();
var newTransfrom1 = new T(); //如果原来不是MatrixTransform矩阵,则加入
var matrixTransform = uiElement.RenderTransform as MatrixTransform;
if (matrixTransform == null)
{
newTransformGroup1.Children.Add(uiElement.RenderTransform);
} newTransformGroup1.Children.Add(newTransfrom1);
uiElement.RenderTransform = newTransformGroup1;
return newTransfrom1;
} private static T GetTranform<T>(TransformGroup transformGroup)
where T : Transform, new()
{
foreach (var child in transformGroup.Children)
{
if (child is T)
{
return (T)child;
} var group1 = child as TransformGroup;
if (group1 != null)
{
var r = GetTranform<T>(group1);
if (r != null)
{
return r;
}
}
} return null;
} #endregion
}

需要特别说一下的是:在Win10 UAP开发中,如Grid这中的界面元素都默认使用了2D的仿射矩阵变换动画,通过修改3*3的矩阵数值就能够表达平移、旋转、缩放。2010年在某家公司做一个WPF地图平面功能时就是直接使用的Matrix实现。因此MatrixTransform不能和其他Transform一起使用。

解释一下375行的GetTranform<T>方法,它返回或为uiElement创建指定的Transform。当uiElement的RenderTransform属性为空时候,创建TransformGroup,然后将指定的Transform添加到TransformGroup里,使UIElement的RenderTransform为TransformGroup。

在动画代码里,平移使用TranslateTransform效果,透明度是修改Opacity属性,Opacity为0或者为1时候修改Visibility属性。当然可以在调用PlayXXXAnimation方法时候设置changeVisibility为false来达到不修改Visibility的目的。

EasingFunctionBase是缓动动画效果,就是Bland里不同的动画曲线函数。

最后可根据情况丰富自已的动画库。比如模仿按钮被按下的动画效果,然后写成bool类型的FramewrokElement的附加属性。

WPF/WP/Silverlight/Metro App代码创建动画的思路的更多相关文章

  1. wpf/wp/win8中的代码编写过程

    0.根据需求文档,完成前端界面显示 1.定义事件,初始化事件并定义方法. 2.定义加载数目和当先显示数目,定义方法所需要的变量. 3.编写方法所需要的接口以及接口实现. 4.在方法中引用接口. 5.实 ...

  2. wpf 创建动画三种方式

    动画类型 : 故事版,CompositionTarget,DispachTime 那么到此,三种动态创建动画的方法都已经详细介绍过了,大家可能会有种感觉,比较钟情于第一种WPF/Silverlight ...

  3. 《Programming WPF》翻译 第8章 5.创建动画过程

    原文:<Programming WPF>翻译 第8章 5.创建动画过程 所有在这章使用xaml举例说明的技术,都可以在代码中使用,正如你希望的.可是,代码可以使用动画在某种程度上不可能在x ...

  4. 示例:WPF中自定义StoryBoarService在代码中封装StoryBoard、Animation用于简化动画编写

    原文:示例:WPF中自定义StoryBoarService在代码中封装StoryBoard.Animation用于简化动画编写 一.目的:通过对StoryBoard和Animation的封装来简化动画 ...

  5. 【WPF学习】第五十七章 使用代码创建故事板

    在“[WPF学习]第五十章 故事板”中讨论了如何使用代码创建简单动画,以及如何使用XAML标记构建更复杂的故事板——具有多个动画以及播放控制功能.但有时采用更复杂的故事板例程,并在代码中实现全部复杂功 ...

  6. WPF使用后台C#代码创建Grid

    笔者刚刚接触WPF,菜鸟一枚,在做一个练手程序时遇到这样一个需求,创建一个新的Grid并将其添加至一个ListView中,要求Grid及其子元素应按一定顺序给Name属性赋值,直接使用XAML创建的话 ...

  7. [WPF]如何使用代码创建DataTemplate(或者ControlTemplate)

    1. 前言 上一篇文章([UWP]如何使用代码创建DataTemplate(或者ControlTemplate))介绍了在UWP上的情况,这篇文章再稍微介绍在WPF上如何实现. 2. 使用Framew ...

  8. SharePoint 2013 代码创建应用程序目录(App Catalog)

    众所周知,SharePoint App是2013版本的一大特色,那么,关于App的分发有几种方式呢?SharePoint给我们提供了两种方式,一种是上载到SharePoint应用商店,另一种是在本地S ...

  9. WPF,Silverlight与XAML读书笔记第四十三 - 多媒体支持之文本与文档

    说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. Glyphs对象(WPF,Silverlig ...

随机推荐

  1. Elasticsearch之elasticsearch5.x 新特性

    其实,elasticsearch5.x 和 elasticsearch2.x 并不区别很大. 是因为,ELK里之前版本各种很混乱,直接升级到5.0了. 其实,elasticsearch5.x 按理来说 ...

  2. PHP的文件下载

    1.通过header头部下载文件 header("Content-length: ".filesize($filename)); //指定文件下载的大小      header(' ...

  3. Spring 单例

    我们知道 Web 容器本身就是多线程的,Web 容器为一个 Http 请求创建一个独立的线程,所以由此请求所牵涉到的 Spring 容器中的 Bean 也是运行于多线程的环境下.在绝大多数情况下,Sp ...

  4. PyQt4测试安装ok的程序

    # -*- coding: utf-8 -*- """ ------------------------------------------------- File Na ...

  5. python并发编程之多进程2-(数据共享及进程池和回调函数)

    一.数据共享 1.进程间的通信应该尽量避免共享数据的方式 2.进程间的数据是独立的,可以借助队列或管道实现通信,二者都是基于消息传递的. 虽然进程间数据独立,但可以用过Manager实现数据共享,事实 ...

  6. Android签名机制之---签名过程详解

    http://www.2cto.com/kf/201512/455388.html 一.前言 又是过了好长时间,没写文章的双手都有点难受了.今天是圣诞节,还是得上班.因为前几天有一个之前的同事,在申请 ...

  7. HNOI2019梦游记

    \(Day_0\) 十点半开始睡觉,开始了八个小时的不眠之夜,整晚都没睡着,这状态明天肯定挂了 \(Day_1\) 开局一条鱼,计算几何只会\(20\) 还是\(T2\)的\(20\)纯暴力好打,\( ...

  8. 对matrix,dataframe的操作函数

    1.每行(列)的平均值:rowMeans() ; colMeans() 输入:数值型矩阵:数值型数据框 输出:向量 2.每行(列)的总和:rowSums() ;colSums() 输入:数值型矩阵,数 ...

  9. JavaWeb Cookie

    1. Cookie 1.1. Cookie概述 Cookie译为小型文本文件或小甜饼,Web应用程序利用Cookie在客户端缓存服务器端文件.Cookie是以键值对形式存储在客户端主机硬盘中,由服务器 ...

  10. Bootstrap3全局CSS样式

    目录 1. 概览 2. 栅栏系统 3. 文本 4. 列表 5. 代码 6. 表格 7. 表单 7.1 基本实例 7.2 内联表单 7.3 水平排列的表单 8. 按钮 9. 图片 10. 辅助类 10. ...