看到巧用 CSS/SVG 实现复杂线条光效动画的文章,便也想尝试用WPF的Shape配合动画实现同样的效果。ChokCoco大佬的文章中介绍了基于SVG的线条动画效果和通过角向渐变配合 MASK 实现渐变线条两种方式。WPF中的Shape与SVG非常相似,因此这种方式也很容易实现。但WPF中仅有的两种渐变画刷不包含角向渐变,本文使用了另外两种方式实现同样的效果。

在Avalonia的API文档中有看到ConicGradientBrush,应该可以用角向渐变的方式来实现。

首先看一下三种方式实现的效果(录制的gif中颜色存在一些偏差,动画有些卡顿,实际效果要好一些):

基于Polyline的线条动画效果

这种方式也是利用StrokeDashArray实现虚线样式,然后通过动画设置StrokeDashOffset来实现动画。首先,用Polyline绘制一段折线:

<Polyline Points="240 20 140 20 140 100 0 100" Stroke="#ddd" />

这样,我们就得到一条这样的折线:

接下来,利用StrokeDashArray实现与上边折线相同路径的虚线(点划线):

<Polyline Points="240 20 140 20 140 100 0 100" Stroke="red"
StrokeDashArray="20 30" />

StrokeDashArray设置了虚线(点划线)中实线段的长度以及间隔,这里和SVG中的stroke-dasharray略有不同,WPF中StrokeDashArray使用的是相对值。例如此处设置的StrokeDashArray="20 30"表示实线段长度为20,间隔为30,这些值是相对于线段的宽度StrokeThickness。如果StrokeThickness=2,那么实线段长度就是40个设备无关单位(Device Independent Units),间隔就是60DIUs。

当我们把间隔设置足够大时,就可以只看到一条实线段,这里折线中三条线段总长是320,因此把实线段设置20,间隔设置300:

<Polyline Points="240 20 140 20 140 100 0 100" Stroke="red"
StrokeDashArray="20 300" />



接下来就是借助StrokeDashOffset来实现动画。

<Grid
Grid.Row="0"
Grid.Column="0"
Margin="5">
<Polyline Points="240 20 140 20 140 100 0 100" Stroke="#ddd" />
<Polyline
Points="240 20 140 20 140 100 0 100"
Stroke="red" StrokeThickness=""
StrokeDashArray="20 300">
<Polyline.Triggers>
<EventTrigger RoutedEvent="Polyline.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" Storyboard.TargetProperty="StrokeDashOffset">
<DoubleAnimation
From="0"
To="-320"
Duration="0:0:3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Polyline.Triggers>
</Polyline>
</Grid>

与CSS/SVG实现的方式一样,WPF中也只能对整段虚线设置渐变色,无法对其中一段实线设置。要想实现渐变效果只能另寻他法。

基于多条线段的动画

最朴素的想法就是用一条渐变色的线段沿着折线的路径移动,但是最大的问题在于折线拐角处难以处理。最为粗暴简单的思路就是针对折线的三段准备三条线段,第一条线段动画即将结束时,第二条开始,第二条动画即将结束时第三条开始。

<Polyline Points="240 20 140 20 140 100 0 100" Stroke="#ddd" />
<Polyline
x:Name="polyline1"
Points="260 20 240 20"
Stroke="{StaticResource linearBrush}" />
<Polyline
x:Name="polyline2"
Points="140 0 140 20"
Stroke="{StaticResource linearBrush}" />
<Polyline
x:Name="polyline3"
Points="160 100 140 100"
Stroke="{StaticResource linearBrush}" />

这里有个细节需要注意,第1条线段向左移动刚好离开折线水平轨迹时,第2条线段才开始向下延垂直轨迹移动,并且移动速度一致,才能保证形成的移动的线段颜色连贯且长度不变。

<Storyboard x:Key="moveLines" RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="polyline1" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
<EasingDoubleKeyFrame KeyTime="00:00:00.800" Value="-100" />
<EasingDoubleKeyFrame KeyTime="00:00:01" Value="-121" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="polyline2" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)">
<EasingDoubleKeyFrame KeyTime="00:00:00.800" Value="0" />
<EasingDoubleKeyFrame KeyTime="00:00:01" Value="20" />
<EasingDoubleKeyFrame KeyTime="00:00:01.8" Value="80" />
<EasingDoubleKeyFrame KeyTime="00:00:02" Value="101" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="polyline3" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
<EasingDoubleKeyFrame KeyTime="00:00:01.8" Value="0" />
<EasingDoubleKeyFrame KeyTime="00:00:02" Value="-20" />
<EasingDoubleKeyFrame KeyTime="00:00:03" Value="-160" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>

这样看效果并不明显,接下来就是需要用一个形状把完成动画的线段遮挡起来:

<Polygon Fill="#fff" Points="240,19 139,19 139,99 0,99 0,79 119,79 119,0 240,0 240,19 240,21 141,21 141,101 0,101 0,99 -20,99 -20,101 -20,122 161,122 161,41 260,41 260,19" />

这样基本实现了渐变色线条的动画效果,但终究不够优雅。

基于等腰三角形的动画

上一种方法中,在拐角处由两条线段配合的动画实现的效果,一条线段移出,另一条移入,连接起来刚好是个等腰直角三角形。

然后用线性渐变色填充三角形就可以实现移出的线段颜色和移入部分颜色相同。

<LinearGradientBrush x:Key="linearBrush" StartPoint="0 1" EndPoint="1 0">
<GradientStop Offset="0.25" Color="#399953" />
<GradientStop Offset="0.5" Color="#fbb300" />
<GradientStop Offset="0.75" Color="#d53e33" />
<GradientStop Offset="1" Color="#377af5" />
</LinearGradientBrush>
<Polygon
x:Name="trigle"
Fill="{StaticResource linearBrush}"
Points="240 19 240 40 220 19"/>

接下来就是三角形沿着轨迹移动的动画以及遮挡轨迹以外部分了。

<Storyboard x:Key="moveanimation" RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="trigle" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="20" />
<EasingDoubleKeyFrame KeyTime="00:00:01" Value="-99" />
<EasingDoubleKeyFrame KeyTime="00:00:02" Value="-99" />
<EasingDoubleKeyFrame KeyTime="00:00:03" Value="-240" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="trigle" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)">
<EasingDoubleKeyFrame KeyTime="00:00:01" Value="0" />
<EasingDoubleKeyFrame KeyTime="00:00:02" Value="80" />
<EasingDoubleKeyFrame KeyTime="00:00:03" Value="80" />
</DoubleAnimationUsingKeyFrames>
</Storyboard> <Grid Grid.Row="0" Grid.Column="1">
<Polyline Points="240 20 140 20 140 100 0 100" Stroke="#ddd" />
<Polygon
x:Name="trigle"
Fill="{StaticResource linearBrush}"
Points="240 19 240 40 220 19">
<Polygon.RenderTransform>
<TranslateTransform />
</Polygon.RenderTransform>
<Polygon.Triggers>
<EventTrigger RoutedEvent="Polygon.Loaded">
<BeginStoryboard Storyboard="{StaticResource moveanimation}" />
</EventTrigger>
</Polygon.Triggers>
</Polygon>
<Polygon Fill="#fff" Points="240,19 139,19 139,99 0,99 0,79 119,79 119,0 240,0 240,19 240,21 141,21 141,101 0,101 0,99 -20,99 -20,101 -20,122 161,122 161,41 260,41 260,19" d:IsHidden="True" />
</Grid>

小结

基于Polyline的线条StrokeDashOffset的方式最为灵活简洁,不仅适用于直角折线,还适用于各种曲线。如果把此处的Polyline换成一个Ellipse,就可以实现简单的转圈圈等待的动效,但其不足在于线条样式美化空间有限。

基于多条线段的动画可以美化线条,但只适用于Polyline或者直线组成的Path,一旦存在曲线就不适用了。

基于等腰三角形的动画可以看做是基于多条线段的动画的一种特殊场景,局限性较大,仅适用于带直角的折线。

WPF使用Shape实现复杂线条动画的更多相关文章

  1. 【Web动画】SVG 实现复杂线条动画

    在上一篇文章中,我们初步实现了一些利用基本图形就能完成的线条动画: [Web动画]SVG 线条动画入门 当然,事物都是朝着熵增焓减的方向发展的,复杂线条也肯定比有序线条要多. 很多时候,我们无法人工去 ...

  2. 【Web动画】SVG 线条动画入门

    通常我们说的 Web 动画,包含了三大类. CSS3 动画 javascript 动画(canvas) html 动画(SVG) 个人认为 3 种动画各有优劣,实际应用中根据掌握情况作出取舍,本文讨论 ...

  3. PPT制作线条动画

    0.小叙闲言 今天在用PPT做动画的时候小有心得,百度了一下线条动画制作,有一个贴子里面的讨论,也给了我一些灵感,贴子地址:http://www.rapidbbs.cn/thread-24577-1- ...

  4. 炫酷线条动画--svg

    我们经常可以在一些页面看到看起来很酷的线条动画,有些可以用css实现,有些css就无能为力了,今天来研究另一种实现方式,svg 如果对svg是什么还不了解的话,可以先去看看svg的基础教程: 一般对于 ...

  5. WPF实现QQ群文件列表动画(二)

    上篇(WPF实现QQ群文件列表动画(一))介绍了WPF实现QQ群文件列表动画的大致思路,结合我之前讲过的WPF里ItemsControl的分组实现,实现起来问题不大,以下是效果图: 其实就是个List ...

  6. SVG动画实践篇-无中生有的线条动画

    git: https://github.com/rainnaZR/svg-animations/tree/master/src/pages/step2/path 说明 这个动画实现的是线条动画,主要用 ...

  7. WPF中自动增加行(动画)的TextBox

    原文:WPF中自动增加行(动画)的TextBox WPF中自动增加行(动画)的TextBox WPF中的Textbox控件是可以自动换行的,只要设置TextWrapping属性为"Wrap& ...

  8. WPF入门(三)->几何图形之线条(LineGeometry)

    原文:WPF入门(三)->几何图形之线条(LineGeometry) 前一章我们对wpf的xaml语言有了一定的了解,那么我们现在开始来学习如何使用wpf来画出几何图形. LineGeometr ...

  9. WPF中的PathAnimation(路径动画)

    原文:WPF中的PathAnimation(路径动画) WPF中的PathAnimation(路径动画)                                                 ...

  10. 线条之美,玩转SVG线条动画

    线条之美,玩转SVG线条动画 作者:AlloyTeam www.alloyteam.com/2017/02/the-beauty-of-the-lines-break-lines-svg-animat ...

随机推荐

  1. #单调队列#JZOJ 1753 锻炼身体

    题目 一个\(n*m\)的矩阵,有些格子不能经过,有\(k\)个时段, 要么停留某个格子,要么沿时段规定的方向移动,问最多能够移动多少次 \(n,m,k\leq 200\) 分析 题目已经提示了\(O ...

  2. 在Keycloak中实现多租户并在ASP.NET Core下进行验证

    Keycloak是一个功能强大的开源身份和访问管理系统,提供了一整套解决方案,包括用户认证.单点登录(SSO).身份联合.用户注册.用户管理.角色映射.多因素认证和访问控制等.它广泛应用于企业和云服务 ...

  3. 响应式系统reactive system初探

    目录 初识响应式系统 什么是响应式系统 响应式系统的四大特点 及时响应性(Responsive) 恢复性(Resilient) 有弹性(Elastic) 消息驱动(Message Driven) 总结 ...

  4. 首届OpenHarmony竞赛训练营结营颁奖,75所高校学子助力建设开源生态

      由OpenAtom OpenHarmony(以下简称"OpenHarmony")项目群工作委员会和OpenHarmony项目群技术指导委员会主办的首届OpenHarmony竞赛 ...

  5. 从 Oracle 到 MySQL 数据库的迁移之旅

    目录 引言 一.前期准备工作 1.搭建新的MySQL数据库 2 .建立相应的数据表 2.1 数据库兼容性分析 2.1.1 字段类型兼容性分析 2.1.2 函数兼容性分析 2.1.3 是否使用存储过程? ...

  6. 重新整理.net core 计1400篇[七] (.net core 中的依赖注入)

    前言 请阅读第六篇,对于理解.net core 中的依赖注入很关键. 和我们上一篇不同的是,.net core服务注入保存在IServiceCollection 中,而将集合创建的依赖注入容器体现为I ...

  7. 《Effective C#》系列之(五)——优化集合的使用

    一.优化集合的使用 在<Effective C#>这本书中,优化集合的使用是其中一章的内容.以下是该章节的一些核心建议,以及使用C#代码示例说明: 使用泛型集合:泛型集合可以避免装箱和拆箱 ...

  8. Django框架——图书管理系统、聚合查询、分组查询、F与Q查询

    图书管理系统 1.表设计 先考虑普通字段再考虑外键字段 数据库迁移.测试数据录入 2.首页展示 3.书籍展示 4.书籍添加 5.书籍编辑 后端如何获取用户想要编辑的数据.前端如何展示出待编辑的数据 6 ...

  9. LLM开源小工具(基于代码库快速学习/纯shell调用LLM灵活管理系统)

    随着AI的各种信息的发展,LLM各种模型不断涌现,作为一名IT人员不得不向前走,不断探索学习发现新知识. 随着学习,也了解到一些对于模型的调用,从而解决一些问题,或者对已有工具或应用的重写.如下是两个 ...

  10. JVM简明笔记3:类加载机制

    1 类的加载 类的加载指的是将类的 .class 文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class 对象,用来封装类在方法区内的数据结 ...