通通玩blend美工(8)——动态绘制路径动画,画出个萌妹子~
原文:通通玩blend美工(8)——动态绘制路径动画,画出个萌妹子~
2年前我在玩Flex的时候就一直有一个疑问,就是如何来实现一个蚊香慢慢烧完的Loading动画呢?
刚经历了某甲方高强度一个月的洗礼后,这几天刚好闲下来,这个问题又浮现在我脑海里。于是经过几番思索纠结后,我发现了一个更好玩的效果,如下:
1.总体思路
下面我就来分析一下实现思路。
仔细观察下,
1:遍历出所有的Path。
2:把各个Path分解成各种点。
3:依次对各个点执行PointAnimation动画(直线执行PointAnimation,曲线执行PointAnimationUsingPath)。
2.详细设计
首先,我们得画一个路径作为动画的样本路径。
1、准备萌妹子一枚
用打开后,转成路径,如下:
参数:
确定后得到
可以手动删除一些无用的路径。
然后导出路径
打开导出的Xaml将Path都Copy出来用。
2、遍历出萌妹子的各种轮廓
这里我之前写过一个遍历一个对象的可视树下所有的某类型的子对象的方法(wpf移植过来的为了适应,临时小改了一下)。
List<Path> list = new List<Path>();
/// <summary>
/// 遍历某对象下某类型的所有子元素
/// </summary>
/// <param name="myVisual">遍历的对象</param>
/// <param name="type">子元素的类型</param>
public void EnumVisual(DependencyObject myVisual, Type type)
{
for (int i = ; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
{
DependencyObject childVisual = (DependencyObject)VisualTreeHelper.GetChild(myVisual, i); if (childVisual != null)
{
if (childVisual.GetType() == type)
{
list.Add(childVisual as Path);
}
EnumVisual(childVisual, type);
}
}
return;
}
然后
EnumVisual(Group, typeof(Path));
其中Group为包含所有Path的容器,这样list里就充满了萌妹子的轮廓。
3、把每一条Path都分割成各种点的表示方式
这里我们先来了解一下Path的路径标记语法。其实,Path的线段是有两种表示方法的,如下:
第一种:
<Path Data="M96,200 L176,288 C176,288 272,232 248,208" Fill="#FFF8F8F8" Stroke="Black"/>
第二种:
<Path x:Name="path" Fill="#FFF8F8F8" Stroke="Black">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="96,200">
<LineSegment Point="176,288"/>
<BezierSegment Point3="248,208" Point2="272,232" Point1="176,288"/>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
这两段代码表示的是同样的路径。其中M=StartPoint,L=LineSegment,C=BezierSegment,LineSegment的Point属性指的是线段的终结点,BezierSegment的Point3指的是曲线的终结点,Point1和Point2分别指的是起始点控制点和结束点控制点的位置,就是用钢笔工具画曲线时拖动曲度的那两个点。当然了,每个线段的起始点为上一个线段的终结点。迷你表示法还有大小写分别表示绝对位置与相对位置,更多说明请参考下面链接。
详情请参考:http://msdn.microsoft.com/zh-cn/library/ms752293.aspx
我们遍历出来的Path其实都是用第一种表示方法表示的,这样不方便我们后台来分解解析。我们得把第一种表示方法装换为第二种。这里我们得借用国外大侠分享的类:
http://stringtopathgeometry.codeplex.com/
这个东西可以很方便的实现这两种格式的转换,如下:
StringToPathGeometryConverter _s = new StringToPathGeometryConverter();
PathGeometry PG = _s.Convert(path.Data.ToString());
这样转换出来的PG就是第二种表示方法了。
通过遍历第二种表示方法里面PathFigure的子项就可以知道点与点之间的关系了,依次在点与点之间执行PointAnimation和PointAnimationUsingPath让终结点从起始点运动到结束位置,就实现划线的效果了。
曲线的话必须得用PointAnimationUsingPath来执行才显得自然一些,当然了,silverlight里是没有PointAnimationUsingPath动画的,所以得借助某外国达人写的PointAnimationUsingPath类,用法和WPF里差不多,不过个人觉得可以在Animation里直接来设置target和targetProperty还有Begin方法,这一点比微软写的更好用。
详情请参看:http://www.codeproject.com/Articles/30819/Animation-Along-a-Path-for-Silverlight
好了,直接上代码:
/// <summary>
/// 存储遍历到的Path样本
/// </summary>
List<Path> list = new List<Path>(); /// <summary>
/// Path样本索引
/// </summary>
int pathNum = ; /// <summary>
/// 新绘制的路径的集合
/// </summary>
List<Path> drawPathList = new List<Path>(); /// <summary>
/// 点集合索引
/// </summary>
int num = ; /// <summary>
/// 当前绘制路径的样本Path
/// </summary>
PathGeometry PG; /// <summary>
/// 当前绘制路径中的点集合
/// </summary>
PathFigure PF; /// <summary>
/// 当前绘制路径中的结束点
/// </summary>
Point endPoint;
/// <summary>
/// 开始绘制一条路径
/// </summary>
/// <param name="path">路径样本</param>
private void PathPlay(Path path)
{
Path ph = new Path();//当前绘制的路径
drawPathList.Add(ph);
ph.Stroke = new SolidColorBrush(Colors.Black);
drawGrid.Children.Add(ph);//添加进画布 PathGeometry thisPG = new PathGeometry();//动画路径的数据
ph.Data = thisPG;
StringToPathGeometryConverter _s = new StringToPathGeometryConverter();
PG = _s.Convert(path.Data.ToString());//读取样本路径,分解段
PF = new PathFigure();//创建集合
endPoint = PF.StartPoint = PG.Figures[].StartPoint;//设置起始点,一开始的终结点为起始点
thisPG.Figures.Add(PF);
Play();//开始绘制分段的路径 } /// <summary>
/// 绘制路径的一段
/// </summary>
private void Play()
{
try
{
if (pathNum >= list.Count)//画完所有的路径
{
FillColor();//开始填充颜色
Group.Visibility = Visibility.Visible;
return;
}
else if (num >= PG.Figures[].Segments.Count)//画完一条线
{
if (pathNum < list.Count)
{
num = ;
PathPlay(list[pathNum++] as Path);//播放完毕就播放下一条线
return;
}
}
PathSegment item = PG.Figures[].Segments[num++];//读取下一个点
if (item.ToString().Contains("Line"))//如果这个点是直线
{
LineSegment _ls = new LineSegment();
_ls.Point = endPoint;
PathSegment PS = _ls;//创建一条直线的初始状态点 PointAnimation PA = new PointAnimation();//动画到读取的点的位置
PA.To=(item as LineSegment).Point;
PA.Duration = new Duration(TimeSpan.FromMilliseconds());
PA.Completed += new EventHandler((sender1, e1) =>//播放完毕后进行递归,绘制下一条线
{
Play();
});
PF.Segments.Add(PS);//添加点
//PS.BeginAnimation(LineSegment.PointProperty, PA);
Storyboard sb = new Storyboard();
sb.Children.Add(PA);
Storyboard.SetTarget(PA, PS);
Storyboard.SetTargetProperty(PA, new PropertyPath("Point"));
sb.Begin();//开始动画
endPoint = (item as LineSegment).Point;//记录终结点 }
else if (item.ToString().Contains("Bezier"))//如果这个点是贝尔曲线
{
BezierSegment _bs = new BezierSegment();
_bs.Point1 = _bs.Point2 = _bs.Point3 = endPoint;
PathSegment PS = _bs;
PointAnimationUsingPath PA = new PointAnimationUsingPath();//创建终结点的路径动画,曲线要严格按照路径来运动
PA.Target = PS;
PA.TargetProperty = new PropertyPath("Point3");
PA.Duration = TimeSpan.FromMilliseconds();
//生成动画的路径形状
PathGeometry newPG = new PathGeometry();
PathFigure newPF = new PathFigure();//创建集合
newPF.StartPoint = endPoint;//s设置起始点和每次动画的种植点
newPG.Figures.Add(newPF);
BezierSegment _bs1 = new BezierSegment();
_bs1.Point1 = (item as BezierSegment).Point1;
_bs1.Point2 = (item as BezierSegment).Point2;
_bs1.Point3 = (item as BezierSegment).Point3;
newPF.Segments.Add(_bs1); PA.PathGeometry = newPG;
PA.Completed += new EventHandler((sender1, e1) =>
{
Play();
});
PA.Begin();
//同样对控制点也要进行一般的动画
PointAnimation PA1 = new PointAnimation();
PA1.To=(item as BezierSegment).Point1;
PA1.Duration=new Duration(TimeSpan.FromMilliseconds());
PointAnimation PA2 = new PointAnimation();
PA2.To=(item as BezierSegment).Point2;
PA2.Duration=new Duration(TimeSpan.FromMilliseconds());
PF.Segments.Add(PS);
//PS.BeginAnimation(BezierSegment.Point3Property, PA);
//PS.BeginAnimation(BezierSegment.Point1Property, PA1);
//PS.BeginAnimation(BezierSegment.Point2Property, PA2);
Storyboard sb = new Storyboard();
//sb.Children.Add(PA);
//Storyboard.SetTarget(PA, PS);
//Storyboard.SetTargetProperty(PA, new PropertyPath("Point"));
//sb.Begin();//开始动画
endPoint = (item as BezierSegment).Point3;
}
}
catch
{ }
}
方法里用了各种递归是因为处理完一条Path的所有动画后执行下一条Path的动画,而每一条Path里的每一小段也得依次处理, 要让一序列的动画依次播放,得在动画播放完毕后再播放下一段动画,各位大虾有没有更好的方法来依次播放一序列动画呢??
后记
原版是Wpf的,wpf自带了PointAnimationUsingPath动画,所以实现起来代码少得多了。接下来我打算优化后把它封成一个行为,方便以后使用。
文中出现了这么多外国牛人的文章,当然了以小弟的强烈爱国情怀是无法完全理解,所以特别谢http://www.cnblogs.com/beniao/archive/2010/05/26/1736446.html。
觉得本文还可以的话要点击下面的推荐哦喵~
通通玩blend美工(8)——动态绘制路径动画,画出个萌妹子~的更多相关文章
- 通通玩blend美工(1)——荧光Button
原文:通通玩blend美工(1)--荧光Button 最近老大出差去了,光做项目也有点烦,写点教程消遣消遣(注:此乃初级教程,所以第一个消遣是本人消遣,第二个是指供各位看官消遣...) 看着各位大虾出 ...
- 通通玩blend美工(6)上——仿iPhone滚动选择器的ListBox(UI设计)
原文:通通玩blend美工(6)上--仿iPhone滚动选择器的ListBox(UI设计) 好久没更新博客了,由于项目比较紧,期间收到不少园友的短消息,感谢大家对我的支持~~. 相信各位都在自己的神机 ...
- 通通玩blend美工(3)——可爱的云
原文:通通玩blend美工(3)--可爱的云 好久没有写这个系列的博客了,这里给个电梯吧,照顾新来的同学~~ 通通玩blend美工(1)——荧光Button 通通玩blend美工(2)——时钟 目前我 ...
- 通通玩blend美工(2)——时钟
原文:通通玩blend美工(2)--时钟 谢谢大家对我上一篇Blend的支持:通通玩blend美工(1)——荧光Button 再接再厉再来一篇~~! 这篇是建立在已经看得懂上一篇为基础来写的,有些细节 ...
- 通通玩blend美工(6)下——仿iPhone滚动选择器的ListBox(交互逻辑)
原文:通通玩blend美工(6)下--仿iPhone滚动选择器的ListBox(交互逻辑) 上一篇我们已经把界面画出来了,这篇我们就来制作交互的逻辑吧.上一篇的电梯: http://www.cnblo ...
- 通通玩blend美工(7)——简约而不简单的块
原文:通通玩blend美工(7)--简约而不简单的块 最近在研发一个WPF快速开发框架,满脑子都是各种逻辑各种模式,写一篇比较休闲娱乐的博客,宣泄下我对美工的热爱. 我一直以来有意无意在手机应用或者各 ...
- 通通玩blend美工(5)——旋转木马,交互性设计
原文:通通玩blend美工(5)--旋转木马,交互性设计 这一篇偏向于逻辑的比较多,放在这个系列里会不会欠妥呢?在中国交互性设计也是美工的份内职责哦~ 所以没有blend基础的人也可以看懂这篇文章,不 ...
- svg实现绘制路径动画
1,首先用svg绘制一条path路径,然后进行如下操作 ps: 下面是svg中两个属性及值的意义 stroke-dasharray是让你指定画出的线段每段的长度,第二个值是各段之间空隙的长度. str ...
- (数据科学学习手札85)Python+Kepler.gl轻松制作酷炫路径动画
本文示例代码.数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 Kepler.gl相信很多人都听说过,作为 ...
随机推荐
- 用Java对CSV文件进行读写操作
需要jar包:javacsv-2.0.jar 读操作 // 读取csv文件的内容 public static ArrayList<String> readCsv(String filepa ...
- CMakeListx.txt 编辑语法学习
已hello.cpp为源文件,构建一个CMakeLists.txt cmake_minimum_required(VERSION 2.8) project(hello) add_executable( ...
- 【u010】银河英雄传说
Time Limit: 1 second Memory Limit: 128 MB [问题描述] 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开 ...
- Python 数组[],元组(),字典{}的异同
序列 Python有6中内建的序列,在这里我们重点讨论两种,数组和元组.所有序列都可以做某些特定的操作,大致上常用的是:加,乘,索引,分片以及检查某个元素是否属于序列的成员. Python还提供一些内 ...
- 高并发測试工具webbench
1.简单介绍 webbench最多能够模拟3万个并发连接去測试server的负载能力.编译和配置简单,仅基于TCP协议上对server进行測试. Webbench也是开放源代码.从代码上看,每一个cl ...
- webrtc 它android与PC互通
折腾了一个多星期,今天终将PC和android音频,视频全部打通. 到现在,android与android,pC与PC,android与PC之间已经解决了互通,的音频和视频是能够. 前段时间开了PC与 ...
- iOS CALayer使用
CALayer使用 iOS的设备中,我们之所以能看到各种各样的控件.文字.图片,都是Core Animation框架的功劳.它通过图层的合成,最终显示在屏幕上.而今天这篇文章讲的就是Core Anim ...
- URAL 1684. Jack's Last Word KMP
题目来源:URAL 1684. Jack's Last Word 题意:输入a b 把b分成若干段 每一段都是a的前缀 思路:b为主串 然后用a匹配b 记录到b的i位置最大匹配的长度 然后切割 切割的 ...
- node 设置自动启用定时任务控件 node-schedule
[转]Quartz中时间表达式的设置-----corn表达式 时间格式: <!-- s m h d m w(?) y(?) -->, 分别对应: 秒>分>小时>日&g ...
- 浅谈java枚举类
一.什么情况下使用枚举类? 有的时候一个类的对象是有限且固定的,这种情况下我们使用枚举类就比较方便? 二.为什么不用静态常量来替代枚举类呢? public static final int SEASO ...