原文:通通玩blend美工(8)——动态绘制路径动画,画出个萌妹子~


  2年前我在玩Flex的时候就一直有一个疑问,就是如何来实现一个蚊香慢慢烧完的Loading动画呢?  

  刚经历了某甲方高强度一个月的洗礼后,这几天刚好闲下来,这个问题又浮现在我脑海里。于是经过几番思索纠结后,我发现了一个更好玩的效果,如下:

  

                                                 ↑点击开始
   只要是通过path来表示的都可以画出来哦亲~如果好好构思一下是可以实现很震撼的效果的,你想到了没?

 

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)——动态绘制路径动画,画出个萌妹子~的更多相关文章

  1. 通通玩blend美工(1)——荧光Button

    原文:通通玩blend美工(1)--荧光Button 最近老大出差去了,光做项目也有点烦,写点教程消遣消遣(注:此乃初级教程,所以第一个消遣是本人消遣,第二个是指供各位看官消遣...) 看着各位大虾出 ...

  2. 通通玩blend美工(6)上——仿iPhone滚动选择器的ListBox(UI设计)

    原文:通通玩blend美工(6)上--仿iPhone滚动选择器的ListBox(UI设计) 好久没更新博客了,由于项目比较紧,期间收到不少园友的短消息,感谢大家对我的支持~~. 相信各位都在自己的神机 ...

  3. 通通玩blend美工(3)——可爱的云

    原文:通通玩blend美工(3)--可爱的云 好久没有写这个系列的博客了,这里给个电梯吧,照顾新来的同学~~ 通通玩blend美工(1)——荧光Button 通通玩blend美工(2)——时钟 目前我 ...

  4. 通通玩blend美工(2)——时钟

    原文:通通玩blend美工(2)--时钟 谢谢大家对我上一篇Blend的支持:通通玩blend美工(1)——荧光Button 再接再厉再来一篇~~! 这篇是建立在已经看得懂上一篇为基础来写的,有些细节 ...

  5. 通通玩blend美工(6)下——仿iPhone滚动选择器的ListBox(交互逻辑)

    原文:通通玩blend美工(6)下--仿iPhone滚动选择器的ListBox(交互逻辑) 上一篇我们已经把界面画出来了,这篇我们就来制作交互的逻辑吧.上一篇的电梯: http://www.cnblo ...

  6. 通通玩blend美工(7)——简约而不简单的块

    原文:通通玩blend美工(7)--简约而不简单的块 最近在研发一个WPF快速开发框架,满脑子都是各种逻辑各种模式,写一篇比较休闲娱乐的博客,宣泄下我对美工的热爱. 我一直以来有意无意在手机应用或者各 ...

  7. 通通玩blend美工(5)——旋转木马,交互性设计

    原文:通通玩blend美工(5)--旋转木马,交互性设计 这一篇偏向于逻辑的比较多,放在这个系列里会不会欠妥呢?在中国交互性设计也是美工的份内职责哦~ 所以没有blend基础的人也可以看懂这篇文章,不 ...

  8. svg实现绘制路径动画

    1,首先用svg绘制一条path路径,然后进行如下操作 ps: 下面是svg中两个属性及值的意义 stroke-dasharray是让你指定画出的线段每段的长度,第二个值是各段之间空隙的长度. str ...

  9. (数据科学学习手札85)Python+Kepler.gl轻松制作酷炫路径动画

    本文示例代码.数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 Kepler.gl相信很多人都听说过,作为 ...

随机推荐

  1. Java NIO的基本概念与使用

    public class TestBuffer { /** * 一. 缓冲区 (Buffer):Java Nio中负责数据的 存取+缓冲就是数组.用于存储不同类型的数据 * * 根据类型不同(bool ...

  2. linux下如何查找nginx配置文件的位置

    nginx的配置放在nginx.conf文件中,一般我们可以使用以下命令查看服务器中存在的nginx.conf文件. locate nginx.conf /usr/local/etc/nginx/ng ...

  3. PXC安装

    安装软件依赖包yum install -y  perl-IO-Socket-SSL perl-DBD-MySQL perl-Time-HiRes socat nc    openssl-devel l ...

  4. 关于如何在Sublime下安装插件

    安装插件的两种方式 通过Package Control安装 不能安装 手工安装 安装插件的两种方式 在sublime下安装插件有两种方式,一种是通过package control来进行安装,另一种呢就 ...

  5. 走进windows编程的世界-----窗体的注冊及创建

    1   窗体注冊和创建 1.1WIN32 窗体程序创建步骤 1.WinMain入口函数的定义 2.WindowProc函数的定义 3.注冊窗体类 RegisterClass.RegisterClass ...

  6. uboot中rtc顶层分析

    uboot一般不会要求开启rtc,只是还是支持rtc以备特殊需求的. 底层驱动移植前面两篇已经介绍.这里介绍顶层的调用过程.顶层在uboot/common/cmd_date.c /* * (C) Co ...

  7. 找不到头文件xxxxx.h file not found

    项目里有该文件,但是还是显示找不到.或者是cocopods打开的项目. 原因:.h文件路径找不到.具体找不到的原因有很多种. 1.一般会设置 IOS引用三方framewrok的头文件出现'xxxxx/ ...

  8. 简洁常用权限系统的设计与实现(四):不维护level,用递归方式构造树

    第三篇中,我们通过维护节点的深度level,通过迭代所有的节点,只需要一次,就构造了树.  本篇,换一种方式. 好处是:不维护节点的深度level,增加和修改节点时,也不用维护.递归实现,代码比较清晰 ...

  9. 【物理】概念的理解 —— Phase(相位)

    Phase is the position of a point in time (an instant) on a waveform cycle. 相位指的是波形周期中点在某一时刻的位置.Phase ...

  10. BZOJ 2064 - 状压DP

    传送门 题目大意: 给两个数组, 数组中的两个元素可以合并成两元素之和,每个元素都可以分裂成相应的大小,问从数组1变化到数组2至少需要多少步? 题目分析: 看到数据范围\(n<=10\), 显然 ...