看到巧用 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. Arm架构下麒麟操作系统安装配置Mariadb数据库

    1.安装配置JDK (1)检查机器是否已安装JDK 执行 java -version命令查看机器是否安装JDK,一般麒麟操作系统默认安装openjdk 1.8. (2)安装指定版本JDK 如果麒麟操作 ...

  2. 直播预告丨 OpenHarmony 标准系统多媒体子系统之相机解读

    5 月 26日(周四)晚上 19 点,OpenHarmony 开源开发者成长计划知识赋能第五期"掌握 OpenHarmony 多媒体的框架原理"的第六节直播课,即将开播! 深开鸿资 ...

  3. 30分钟成为Contributor|如何多方位参与OpenHarmony开源贡献?

    如何优雅地参与开源贡献,向顶级开源项目提交 PR(Pull Request).战"码"先锋直播间第八期围绕"OpenAtom OpenHarmony(以下简称" ...

  4. Maven 读取pom.xml

    方法一 1.编写配置文件,要读取的内容用@@包裹. spring: application: # @变量名@ 读取pom.xml中的值 version: @project.version@ 2.pom ...

  5. Qt:MD5加密

    #include <QCryptographicHash> QString source_value = "123456"; // 待加密原始数据 QCryptogra ...

  6. js 使用flow

    前言 what is flow?我想是的,很多人都没有接触过,的确,他是一个新的项目,是的facebook开发的东西,一般还是可以的,有必要去学习一下,在react还是比较重要的. 它做的一件事叫做静 ...

  7. jenkins 持续集成和交付——开篇(一)

    前言 因为以前就很想看下jenkins了,平时工作中也使用,主要是写脚本,但是jenkins 主要还是说运维部门来搞定的,因为公司安全部门认为程序员不应该去接触运维的东西,但是上次面试问了下,准备把这 ...

  8. mmdeploy源码安装 (转换faster rcnn r50/yolox为tensorrt,并用mmdeploy sdk推理)

    mmdeploy源码安装 (转换faster rcnn r50/yolox为tensorrt,并进行推理) 这个系列是一个随笔,是我走过的一些路,有些地方可能不太完善.如果有那个地方没看懂,评论区问就 ...

  9. 有用的JavaScript技巧

    首次为变量赋值时务必使用var关键字 变量没有声明而直接赋值得话,默认会作为一个新的全局变量,要尽量避免使用全局变量. 使用===取代== ==和!=操作符会在需要的情况下自动转换数据类型.但===和 ...

  10. PolarDB-X源码解读系列:DML之Insert流程

    简介: Insert类的SQL语句的流程可初略分为:解析.校验.优化器.执行器.物理执行(GalaxyEngine执行).本文将以一条简单的Insert语句通过调试的方式进行解读. 在阅读本文之前,强 ...