9.7 模拟实现微信的彩蛋动画

大家在玩微信的时候有没有发现节日的时候发一些节日问候语句如“情人节快乐”,这时候会出现很多爱心形状从屏幕上面飘落下来,我们这小节就是要模拟实现这样的一种动画效果。可能微信里面实现的动画效果都是采用固定的小图片来最为动画的对象,但是我们这小节要对该动画效果增加一些改进,也就是要实现的彩蛋动画的针对的图形形状是动态随机生成的,所以看到从屏幕上面飘落的图形都是形状不太一样的。下面来看一下如何实现星星飘落动画。

9.7.1 实现的思路

首先,我们来分析一下星星飘落动画的实现思路。

(1)动画对象

微信的彩蛋动画可以采用固定的图片对象作为动画的对象,但是因为我们要实现的星星动画,这个星星的形状和颜色是不一样的,所以不能直接用图片做为星星的对象。要创建出不同的星星形状需要用到Path图形绘图的方式来实现,定义好画图的规则,然后用随机数来决定某些点的位置这样就可以绘制出各种不同形状的星星。颜色的随机定义就很好办了,可以先产生随机的0-255的三原色色值,然后用三原色的方式来创建颜色对象。

(2)动画的实现方式

从微信的彩蛋动画效果可以看出来,这些动画的随机型很大,轨迹运动轨迹、速度、方向都是随机,这种情况是很难使用线性插值动画或者关键帧动画去实现的,所以这个动画的实现方式很明显是要采用基于帧动画去实现。在基于帧动画里面,通过CompositionTarget.Rendering事件的处理程序来实现动画的效果,星星飘落的动画效果是星星对象从一个固定区域的最上面某个位置飘落到该区域的最底下,然后再销毁星星对象。我们可以采用Canvas面板来定义动画的区域,用Canvas.LeftProperty和Canvas.TopProperty属性作为定义星星对象位置的坐标,然后通过改变这个坐标来实现星星的下落。

(3)动画的封装

在对动画的实现方式和动画对象的封装的思路清楚之后,我们开始考虑如何来封装这样的一种动画效果。星星对象的实现需要通过一个星星工厂类(StarFactory类)来封装星星创建的逻辑。因为每个星星的运动轨迹都是不一样的,都有其自己飘落的速度和方向,所以我们需要封装一个星星的对象,在星星对象里面负责处理星星的速度,方向,运动轨迹等逻辑。最后动画可以通过附加属性的方式在Canvas面板上实现动画。

下面我们来详细地看一下星星飘落动画的编码实现。

代码清单9-18星星飘落动画(源代码:第9章\Examples_9_18)

9.7.2 星星创建工厂

首先,我们先来实现星星的创建工厂StarFactory类,StarFactory类的作用是创建动画里面的星星对象,动画的实现需要向调用StarFactory类来创建出星星对象,然后对星星进行动画处理,所以StarFactory类是一个非常单一的星星构造工厂,里面不涉及动画的操作,只涉及星星Path对象的创建。

(1)星星对象的绘图原理

星星的对象的绘图坐标模型如图9.28所示,首先,需要确定确定坐标系三个点a、b、c,然后再取三条线段ab、ac、bc的三分之一和三分之二的点坐标和两点区域之前一个随机点,经过这样一个处理之后原来的是3条线段的星星图形,变成了3*4条线段的星星图形,在递归一次就会变成3*4*4条线段的星星图形如此类推。

下面把星星图形形状的构造封装了3个方法:_RefactorPoints方法是用于取两个点线段之间的三分之一点、三分之二点和两点区域间的随机点,最后在加上原来的两个点,返回一个5个点的点集合;_RecurseSide方法是封装了两个点之间递归之后所取到的点集合;_CreatePath方法则是把这些点集合连接起来创建一个Path图形表示星星图形。三个方法的代码如下所示:

StarFactory.cs文件部分代码
------------------------------------------------------------------------------------------------------------------/// <summary>/// 把两个点转化成多级的点的集合/// </summary>/// <param name="a">第一个点</param>/// <param name="b">第二个点</param>/// <param name="level">递归的次数</param>/// <returns>新的点的集合</returns>private static List<Point> _RecurseSide(Point a, Point b, int level){// 递归完成后,返回此线段if (level == 0){return new List<Point> { a, b };}else{// 首先,需要建立起第一次递归的点的列表,一直递归到级别为0List<Point> newPoints = new List<Point>();// 把区域分成5个点foreach (Point point in _RefactorPoints(a, b)){newPoints.Add(point);}List<Point> aggregatePoints = new List<Point>();// 把每一个线段进一步分解for (int x = 0; x < newPoints.Count; x++){int y = x + 1 == newPoints.Count ? 0 : x + 1;aggregatePoints.AddRange(_RecurseSide(newPoints[x], newPoints[y], level - 1));}return aggregatePoints;}}/// <summary>/// 通过输入两个点来构建一个有多个三角形组成的Star形状/// </summary>/// <param name="a">第一个点</param>/// <param name="b">第二个点</param>/// <returns>一个新的几何图形的点的集合</returns>private static IEnumerable<Point> _RefactorPoints(Point a, Point b){// 第一个点yield return a;double dX = b.X - a.X;double dY = b.Y - a.Y;// 第一个点到第二个点1/3处的一个点yield return new Point(a.X + dX / 3.0, a.Y + dY / 3.0);double factor = _random.NextDouble() - 0.5;double vX = (a.X + b.X) / (2.0 + factor) + Math.Sqrt(3.0 + factor) * (b.Y - a.Y) / (6.0 + factor * 2.0);double vY = (a.Y + b.Y) / (2.0 + factor) + Math.Sqrt(3.0 + factor) * (a.X - b.X) / (6.0 + factor * 2.0);// 中间的三角形的顶点yield return new Point(vX, vY);//第二个点到第一个点1/3处的一个点yield return new Point(b.X - dX / 3.0, b.Y - dY / 3.0);//第二个点yield return b;}/// <summary>/// 使用一系列的点来创建路径图形/// </summary>/// <param name="points">点的集合</param>/// <returns>路径图形</returns>private static Path _CreatePath(List<Point> points){PathSegmentCollection segments = new PathSegmentCollection();bool first = true;// 把点添加到线段里面foreach (Point point in points){if (first){first = false;}else{segments.Add(new LineSegment{Point = point});}}PathGeometry pathGeometry = new PathGeometry();//通过线段构建几何图形
        pathGeometry.Figures.Add(new PathFigure{IsClosed = true,StartPoint = points[0],Segments = segments});return new Path { Data = pathGeometry };}

(2)星星颜色的随机生成

星星颜色的产生是通过ARGB的数值来进行创建,这样更加方便用随机数进行处理。因为对星星填充的属性是需要用画刷对象来赋值的,所以需要用随机颜色来创建画刷,这里用线性渐变画刷LinearGradientBrush来填充Path图形。封装的方法_GetColor表示创建随机的颜色对象,_ColorFactory表示对Path图形填充随机的颜色画刷。代码如下所示:

StarFactory.cs文件部分代码
------------------------------------------------------------------------------------------------------------------/// <summary>/// 添加颜色到路径图形/// </summary>/// <param name="input">路径图形</param>private static void _ColorFactory(Path input){LinearGradientBrush brush = new LinearGradientBrush();brush.StartPoint = new Point(0, 0);brush.EndPoint = new Point(1.0, 1.0);GradientStop start = new GradientStop();start.Color = _GetColor();start.Offset = 0;GradientStop middle = new GradientStop();middle.Color = _GetColor();middle.Offset = _random.NextDouble();GradientStop end = new GradientStop();end.Color = _GetColor();end.Offset = 1.0;brush.GradientStops.Add(start);brush.GradientStops.Add(middle);brush.GradientStops.Add(end);input.Fill = brush;}/// <summary>/// 获取一个随机的颜色/// </summary>/// <returns></returns>private static Color _GetColor(){Color color = new Color();color.A = (byte)(_random.Next(200) + 20);color.R = (byte)(_random.Next(200) + 50);color.G = (byte)(_random.Next(200) + 50);color.B = (byte)(_random.Next(200) + 50);return color;}

(3)创建星星对象

创建星星对象需要先有三个点,然后在利用这三个点根据创建星星图形的原理(3*4n,n表示星星递归的层次)来创建星星图形,然后用随机颜色画刷来填充图形。同时为了更加个性化,也对图形做了随机角度的旋转变换特效。代码如下所示:

StarFactory.cs文件部分代码
------------------------------------------------------------------------------------------------------------------const int MIN = 0;const int MAX = 2;// 随机数产生器static readonly Random _random = new Random();// 创建一个Starpublic static Path Create(){Point a = new Point(0, 0);Point b = new Point(_random.NextDouble() * 70.0 + 15.0, 0);Point c = new Point(0, b.X);int levels = _random.Next(MIN,MAX);List<Point> points = new List<Point>();points.AddRange(_RecurseSide(a, b, levels));points.AddRange(_RecurseSide(b, c, levels));points.AddRange(_RecurseSide(c, a, levels));// 画边Path retVal = _CreatePath(points);// 添加颜色
        _ColorFactory(retVal);// 建立一个旋转的角度RotateTransform rotate = new RotateTransform();rotate.CenterX = 0.5;rotate.CenterY = 0.5;rotate.Angle = _random.NextDouble() * 360.0;retVal.SetValue(Path.RenderTransformProperty, rotate);return retVal;}

9.7.3 实现单个星星的动画轨迹

星星对象构造工厂实现之后,接下来就需要实现对星星实体(StarEntity类)的封装了,在StarEntity类里面要实现基于帧动画,在帧刷新事件处理程序里面实现星星飘落的动画逻辑。首先需要处理的是确定星星在区域最顶部的随机位置,下落的随机速度和方向,然后在动画的过程中需要去判断星星是否碰撞到了区域的左边距或者右边距,碰撞之后则需要往反弹回来往另外一边运动。最后还需要判断星星是否已经落到了对底下,如果落到了区域最底下,则需要移除CompositionTarget.Rendering事件和从画布上移除星星图形,还要触发StarflakeDied事件来告知调用方星星已经销毁掉了。StarEntity类的代码如下所示:

StarEntity.cs文件代码
------------------------------------------------------------------------------------------------------------------/// <summary>/// Star实体,封装Star的行为/// </summary>public class StarEntity{// 左边距const double LEFT = 480;// 上边距const double TOP = 800;// 离开屏幕const double GONE = 480;//随机近似数private double _affinity; //  Star实体的唯一idprivate Guid _identifier = Guid.NewGuid();// 随机数产生器 private static Random _random = new Random();// Star所在的画布private Canvas _surface;// 获取Star所在的画布public Canvas Surface{get { return _surface; }}// X,Y坐标和相对速度private double x, y, velocity;// Star的路径图形private Path _starflake;// 获取Star实体的唯一idpublic Guid Identifier{get { return _identifier; }}// 默认的构造器public StarEntity(Action<Path> insert): this(insert, true){}/// <summary>/// 星星对象构造方法/// </summary>/// <param name="fromTop">是否从顶下落下</param>public StarEntity(Action<Path> insert, bool fromTop){_starflake = StarFactory.Create();//产生0到1的随机数_affinity = _random.NextDouble();// 设置速度,和初始化x y轴 velocity = _random.NextDouble() * 2;x = _random.NextDouble() * LEFT;y = fromTop ? 0 : _random.NextDouble() * TOP;// 设置Star在画布的位置
            _starflake.SetValue(Canvas.LeftProperty, x);_starflake.SetValue(Canvas.TopProperty, y);// 添加到画布上
            insert(_starflake);// 记录下Star的画布_surface = _starflake.Parent as Canvas;// 订阅基于帧动画事件CompositionTarget.Rendering += CompositionTarget_Rendering;}// 基于帧动画事件处理void CompositionTarget_Rendering(object sender, object e){_Frame();}// Star下落的每一帧的处理private void _Frame(){// 下降的y轴的大小y = y + velocity + 3.0 * _random.NextDouble() - 1.0;// 判断是否离开了屏幕if (y > GONE){CompositionTarget.Rendering -= CompositionTarget_Rendering;_surface.Children.Remove(_starflake);// 通知外部,Star已经被清除EventHandler handler = StarflakeDied;if (handler != null){handler(this, EventArgs.Empty);}}else{// 水平轻推double xFactor = 10.0 * _affinity;if (_affinity < 0.5) xFactor *= -1.0;//小于0.5向左边移动 大于0.5向右边移动 等于0.5垂直下降x = x + _random.NextDouble() * xFactor;// 左边的边缘if (x < 0){x = 0;_affinity = 1.0 - _affinity;}// 右边的边缘if (x > LEFT){x = LEFT;_affinity = 1.0 - _affinity;}_starflake.SetValue(Canvas.LeftProperty, x);_starflake.SetValue(Canvas.TopProperty, y);}// 转动RotateTransform rotate = (RotateTransform)_starflake.GetValue(Path.RenderTransformProperty);rotate.Angle += _random.NextDouble() * 4.0 * _affinity;}// 当Star飘落到底下的时候的回收Star事件public event EventHandler StarflakeDied;// 重载获取唯一的对象码GetHashCode方法public override int GetHashCode(){return Identifier.GetHashCode();}// 重载实现判断对象是否一样的Equals方法public override bool Equals(object obj){return obj is StarEntity && ((StarEntity)obj).Identifier.Equals(Identifier);}}

9.7.4 封装批量星星飘落的逻辑

StarEntity类实现了一个星星的动画逻辑的封装,下面要实现一个StarBehavior类用附加属性的方式在Canvas上添加批量的星星飘落的动画。StarBehavior类里面通过AttachStarFlake属性表示是否在该Canvas面板上添加星星飘落动画,当设置为true的时候表示触发动画的开始,false则表示停止添加星星,知道星星全部飘落到底下的时候动画停止。在开始播放动画的时候会初始化多个StarEntity对象,并运行其飘落的动画效果,当飘落到底下StarEntity对象被销毁的时候,会触发StarflakeDied事件,在StarflakeDied事件里面继续初始化新的StarEntity对象,如果动画要被停止了beginning = false,则不再创建新的StarEntity对象。StarBehavior类的代码如下所示:

StarBehavior.cs文件代码
------------------------------------------------------------------------------------------------------------------/// <summary>///  StarBehavior类管理附加属性的行为触发批量星星的构造和动画的实现/// </summary>public static class StarBehavior{// 屏幕上生成的星星数量const int CAPACITY = 75;// 动画是否已经开始的标识符private static bool beginning = false;// Star对象列表private static List<StarEntity> _starflakes = new List<StarEntity>(CAPACITY);// 添加动画效果的属性public static DependencyProperty AttachStarFlakeProperty = DependencyProperty.RegisterAttached("AttachStar",typeof(bool),typeof(StarBehavior),new PropertyMetadata(false, new PropertyChangedCallback(_Attach)));// 获取属性方法public static bool GetAttachStarFlake(DependencyObject obj){return (bool)obj.GetValue(AttachStarFlakeProperty);}// 设置属性方法public static void SetAttachStarFlake(DependencyObject obj, bool value){obj.SetValue(AttachStarFlakeProperty, value);}// 附加属性属性改变事件处理方法public static void _Attach(object sender, DependencyPropertyChangedEventArgs args){Canvas canvas = sender as Canvas;if (canvas != null && args.NewValue != null && args.NewValue.GetType().Equals(typeof(bool))){if ((bool)args.NewValue){// 画布上还有子元素证明星星还没全部飘落下去if (canvas.Children.Count > 0){return;}// 开始动画beginning = true;for (int x = 0; x < _starflakes.Capacity; x++){StarEntity starflake = new StarEntity((o) => canvas.Children.Add(o));starflake.StarflakeDied += new EventHandler(Starflake_StarflakeDied);_starflakes.Add(starflake);}}else{// 结束动画beginning = false;}}}// 回收Star的事件static void Starflake_StarflakeDied(object sender, EventArgs e){StarEntity starflake = sender as StarEntity;// 获取Star的面板,用来添加一个新的StarCanvas canvas = starflake.Surface;_starflakes.Remove(starflake);if (beginning){// 如果动画还在继续运行一个Star消失之后再创建一个新的StarStarEntity newFlake = new StarEntity((o) => canvas.Children.Add(o), true);newFlake.StarflakeDied += Starflake_StarflakeDied;_starflakes.Add(newFlake);}}}

9.7.5 星星飘落动画演示

上面对星星飘落动画的逻辑都已经封装好了,下面通过一个Windows 10的例子来使用星星飘落动画。

MainPage.xaml文件主要代码
------------------------------------------------------------------------------------------------------------------<Canvas Grid.Row="1" x:Name="myCanvas" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"   ></Canvas><Button Grid.Row="1" x:Name="button" VerticalAlignment="Bottom" Content="开始星星飘落" Click="button_Click_1"></Button>

MainPage.xaml.cs文件主要代码
------------------------------------------------------------------------------------------------------------------// 按钮事件,播放动画和停止动画private async void button_Click_1(object sender, RoutedEventArgs e){if ((bool)myCanvas.GetValue(StarBehavior.AttachStarFlakeProperty) == false){// 要等所有的星星都全部落下去之后才可以再次播放动画if (myCanvas.Children.Count > 0){await new MessageDialog("星星动画未完全结束").ShowAsync();return;}myCanvas.SetValue(StarBehavior.AttachStarFlakeProperty, true);button.Content = "停止新增星星";}else{myCanvas.SetValue(StarBehavior.AttachStarFlakeProperty, false);button.Content = "开始星星飘落";}}

本文来源于《深入浅出Windows 10通用应用开发》

源代码下载:http://vdisk.weibo.com/u/2186322691

目录:http://www.cnblogs.com/linzheng/p/5021428.html

欢迎关注我的微博@WP林政   微信公众号:wp开发(号:wpkaifa)

Windows10/WP技术交流群:284783431

转载于:https://www.cnblogs.com/linzheng/p/5041033.html

[深入浅出Windows 10]模拟实现微信的彩蛋动画相关推荐

  1. pc模拟微信二维码背景_[深入浅出Windows 10]模拟实现微信的彩蛋动画

    9.7 模拟实现微信的彩蛋动画 大家在玩微信的时候有没有发现节日的时候发一些节日问候语句如"情人节快乐",这时候会出现很多爱心形状从屏幕上面飘落下来,我们这小节就是要模拟实现这样的 ...

  2. [深入浅出Windows 10]不同平台设备的适配

    2.3 不同平台设备的适配 Windows 10通用应用程序针对特定的平台还会有一个子API的集合,当我们要使用到某个平台的特定API的时候(比如手机相机硬件按钮触发事件),这时候就需要调用特定平台的 ...

  3. java过滤微信表情符号_微信隐藏彩蛋!表情加符号就能传递“神秘信息”

    微信翻译出彩蛋. 而有的语音信息在转文字后也会有表情翻译的信息出现,比如「太生气了」就能召唤出一个的表情. 类似的小彩蛋还有很多,你很难说这是产品经理的小趣味还是产品对词语识别的 bug. 最近,用户 ...

  4. 微信新彩蛋太 6 了,满屏爱心合为一心,还可以炸屎...

    这是「进击的Coder」的第 164 篇热点新闻来源:菜鸟学Python " 阅读本文大概需要 2 分钟. " 今天,小编又发现微信又有新彩蛋了,微信新增满屏爱心.炸屎表情,具体来 ...

  5. 黑客在 Windows 95 邮件应用中发现彩蛋

    本文转载自IT之家,IT之家 3 月 27 日消息 Windows 95 是微软公司的一个爆款操作系统,发售初期人们都要排队购买该系统. 开发这款操作系统及其应用程序的开发人员有很多值得骄傲的地方,他 ...

  6. 微信新彩蛋太6了,满屏爱心合为一心,还可以炸屎...

    点上方"菜鸟学Python",选择"星标"484篇原创干货,第一时间送达 今天,小编又发现微信又有新彩蛋了,微信新增满屏爱心.炸屎表情,具体来说,微信更新到 8 ...

  7. Chrome 灭霸打响指彩蛋动画 Android实现

    github地址 在手机chrome搜索灭霸后,会出现一个手套的图片,点击图片后会出现一个彩蛋,即一半的搜索结果会消失.消失的动画如下图所示: 可以看到这个动画 大致可以理解为:将当前view分为两份 ...

  8. html5 彩蛋动画,egg.js-趣味复活节彩蛋js插件

    Egg.js是一款非常有趣的复活节彩蛋js插件.该复活节彩蛋插件可以监控用户的键盘输入,当用户输入正确的字母序列的时候,复活节彩蛋会打开,在页面中显示你意想不到的内容. 十二世纪时,人们在复活节节庆中 ...

  9. 微信新增彩蛋表情,遭网友疯狂吐槽

    本文转载自 IT之家 微信新功能真的越来越沙雕,社死程度也直线上升.例如前几天 8.0.6 版本上线的[炸一炸],专治各种手痒拍拍党,后天性多动症用户. 谁敢拍你的头像,立马蹦出一颗炸弹炸得对方满屏震 ...

最新文章

  1. Run time setting设置详解
  2. 小看--发布-订阅(观察者)模式
  3. 甲骨文全球大会——看SOA
  4. PHP - windows下编译PHP 7.2的memcache
  5. java布局管理器的应用总结,GridBagLayout布局管理器的应用
  6. 【iOS】从实际出发理解多线程(二)--NSThread基础操作
  7. USACO-Section1.5 Arithmetic Progressions(枚举)
  8. php获取扫码枪的内容,一起看看js获取扫码枪输入数据的方法_WEB前端开发
  9. Extjs基础入门视频教程
  10. WIN7 Activation,完美激活Windows 7,开机无字符,无OEM信息
  11. JAVA对接大汉三通短信http接口
  12. ivms4200 远程桌面访问测试过程及问题汇总
  13. Android Studio 开关控件Switch使用
  14. 在线Word文档怎么做—使用超级文档一站式解决
  15. Pygame小工具:模拟键盘 - 虚拟键盘(Keyboard)
  16. mac QQ 语音或视频时其他声音变小的解决办法
  17. 微信中“下单账号和支付账号不一致,请核实后再支付”原因及解决办法 hideMenuItems 复制链接
  18. python简单实现微信抢红包功能
  19. Javaweb酒店预约管理系统(框架SpringBoot+Vue)
  20. Excel怎么批量设置行高

热门文章

  1. 时间的对错 人的对错
  2. Python: FFT的输入与输出分析
  3. !important
  4. ACWing算法基础课-耍杂技的牛
  5. 对Request-ID的一些认识
  6. 【邢不行|量化小讲堂系列14-Python量化入门】数据告诉你:惊人的指数定投策略
  7. windows添加计划任务
  8. (转)[超頻測試] 頂級龍二965的浮點運算速度因外頻上300而爆走16%
  9. bashrc与profile的区别
  10. 百度网盘分享链接无效