在WPF中使用Shape实现复杂线条动画后,尝试在Avalonia中也实现同样效果。尽管官方提供了从WPF到Avalonia的快速入门文档,但由于第一次使用Avalonia,体验过程中并不是很顺利,主要是卡在线性渐变画刷LinearGradientBrush的使用上。Avalonia中的线性渐变画刷与WPF中的略有差异,但相关文档并不多,故将此次经历记录下来并分享,希望能帮助大家少走弯路。

由于Avalonia在持续更新,本文所述内容仅针对Avalonia 11.0.10版本。

WPF中的LinearGradientBrush

首先回顾一下WPF中LinearGradientBrush的使用,LinearGradientBrush是沿着StartPointEndPoint定义的直线渐变,并通过GradientStops属性设置画刷渐变停止点。默认情况下是沿着绘制区域的对角线进行渐变,也就是起点StartPoint是(0,0),即绘制区域的左上角,终点EndPoint是(1,1),即绘制区域的右下角。生成的渐变色沿对角线进行插值填充。例如:

<LinearGradientBrush x:Key="defaultLinearBrush">
<GradientStop Offset="0.0" Color="Yellow" />
<GradientStop Offset="0.25" Color="Red" />
<GradientStop Offset="0.75" Color="Blue" />
<GradientStop Offset="1" Color="LimeGreen" />
</LinearGradientBrush>

如果StartPoint是(0,0),终点EndPoint是(1,0),则是沿着水平方向从左到右渐变。

如果StartPoint是(0,0),终点EndPoint是(0.5,0),渐变效果如下图所示:

通过这个例子,可以看出StartPointEndPoint的值是相对于绘制区域的大小,渐变范围是从绘制区域最左边到1/2处,右侧1/2使用最后一个GradientStop设置的颜色填充。需要注意的是,StartPointEndPoint的值既可以是相对值,也可以是绝对值。这是由LinearGradientBrushMappingMode属性决定的,MappingMode是枚举类型BrushMappingMode,枚举定义如下:

枚举 取值 说明
Absolute 0 坐标系统与边界框无关。值直接在本地空间中解释。
RelativeToBoundingBox 1 默认值。坐标系统是相对于边界框的:0表示边界框的0%,1表示边界框的100%。例如,(0.5,0.5)描述边界框中间的一个点,(1,1)描述边界框右下角的一个点。

本例中绘制区域右侧1/2部分超出渐变区域的填充规则默认是用渐变向量末端的颜色值填充了剩余的空间,也可以使用 SpreadMethod属性指定填充规则,该枚举类型定义如下:

枚举 取值 说明
Pad 0 默认值。用渐变向量末端的颜色值填充了剩余的空间。
Reflect 1 在相反的方向重复这个渐变,直到空间被填满。
Repeat 2 渐变沿着原方向重复,直到空间被填满。

Avalonia中使用LinearGradientBrush走的弯路

查看Avalonia的API发现LinearGradientBrush也有StartPointEndPointGradientStops属性,便照搬了WPF中的代码。

<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 Fill="{StaticResource linearBrush}" Points="240 19 240 40 220 19"/>

结果得到却是填充色为#377af5的三角形,通过查询资料得知在Avalonia中StartPointEndPoint要使用百分比的数值,即(0%,100%)。但是在修改为StartPoint="0% 100%" EndPoint="100% 0%"后依旧是填充色为#377af5的三角形。

我便怀疑是LinearGradientBrush写法上依旧有问题或者Polygon使用上有问题,于是写了一个矩形测试线性渐变色填充。

<Rectangle Width="20" Height="20" Fill="{StaticResource linearBrush}"/>

结果得到了线性渐变色填充的矩形。于是可以确定是Polygon的使用上不对,根据Polygon的填充色#377af5,我猜测跟Polygon的坐标有关,调整Polygon的位置后填充色会发生变化,于是修改Polygon的坐标,结果得到了想要的渐变色三角形。

<Polygon Fill="{StaticResource linearBrush}" Points="0 0 20 0 20 20" />

探索求证

经过刚才的一番尝试,我初步推测最初得到的填充色为#377af5三角形可能是由于三角形最右侧的点坐标是(240,19),Avalonia绘制了一个边长为240的渐变色正方形,而这个三角形所在的区域颜色刚好是#377af5。于是我绘制了一个6行6列共有36个40x40的正方形组成的大正方形。

<LinearGradientBrush x:Key="linearBrush" StartPoint="0% 100%" EndPoint="100% 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 Fill="{StaticResource linearBrush}" Points="0,0 40,0 40,40 0,40" />
<Polygon Fill="{StaticResource linearBrush}" Points="40,0 80,0 80,40 40,40" />
<Polygon Fill="{StaticResource linearBrush}" Points="80,0 120,0 120,40 80,40" />
<Polygon Fill="{StaticResource linearBrush}" Points="120,0 160,0 160,40 120,40" />
<Polygon Fill="{StaticResource linearBrush}" Points="160,0 200,0 200,40 160,40" />
<Polygon Fill="{StaticResource linearBrush}" Points="200,0 240,0 240,40 200,40" /> <Polygon Fill="{StaticResource linearBrush}" Points="0,40 40,40 40,80 0,80" />
<Polygon Fill="{StaticResource linearBrush}" Points="40,40 80,40 80,80 40,80" />
<Polygon Fill="{StaticResource linearBrush}" Points="80,40 120,40 120,80 80,80" />
<Polygon Fill="{StaticResource linearBrush}" Points="120,40 160,40 160,80 120,80" />
<Polygon Fill="{StaticResource linearBrush}" Points="160,40 200,40 200,80 160,80" />
<Polygon Fill="{StaticResource linearBrush}" Points="200,40 240,40 240,80 200,80" /> <Polygon Fill="{StaticResource linearBrush}" Points="0,80 40,80 40,120 0,120" />
<Polygon Fill="{StaticResource linearBrush}" Points="40,80 80,80 80,120 40,120" />
<Polygon Fill="{StaticResource linearBrush}" Points="80,80 120,80 120,120 80,120" />
<Polygon Fill="{StaticResource linearBrush}" Points="120,80 160,80 160,120 120,120" />
<Polygon Fill="{StaticResource linearBrush}" Points="160,80 200,80 200,120 160,120" />
<Polygon Fill="{StaticResource linearBrush}" Points="200,80 240,80 240,120 200,120" /> <Polygon Fill="{StaticResource linearBrush}" Points="0,120 40,120 40,160 0,160" />
<Polygon Fill="{StaticResource linearBrush}" Points="40,120 80,120 80,160 40,160" />
<Polygon Fill="{StaticResource linearBrush}" Points="80,120 120,120 120,160 80,160" />
<Polygon Fill="{StaticResource linearBrush}" Points="120,120 160,120 160,160 120,160" />
<Polygon Fill="{StaticResource linearBrush}" Points="160,120 200,120 200,160 160,160" />
<Polygon Fill="{StaticResource linearBrush}" Points="200,120 240,120 240,160 200,160" /> <Polygon Fill="{StaticResource linearBrush}" Points="0,160 40,160 40,200 0,200" />
<Polygon Fill="{StaticResource linearBrush}" Points="40,160 80,160 80,200 40,200" />
<Polygon Fill="{StaticResource linearBrush}" Points="80,160 120,160 120,200 80,200" />
<Polygon Fill="{StaticResource linearBrush}" Points="120,160 160,160 160,200 120,200" />
<Polygon Fill="{StaticResource linearBrush}" Points="160,160 200,160 200,200 160,200" />
<Polygon Fill="{StaticResource linearBrush}" Points="200,160 240,160 240,200 200,200" /> <Polygon Fill="{StaticResource linearBrush}" Points="0,200 40,200 40,240 0,240" />
<Polygon Fill="{StaticResource linearBrush}" Points="40,200 80,200 80,240 40,240" />
<Polygon Fill="{StaticResource linearBrush}" Points="80,200 120,200 120,240 80,240" />
<Polygon Fill="{StaticResource linearBrush}" Points="120,200 160,200 160,240 120,240" />
<Polygon Fill="{StaticResource linearBrush}" Points="160,200 200,200 200,240 160,240" />
<Polygon Fill="{StaticResource linearBrush}" Points="200,200 240,200 240,240 200,240" /> <Path
Data="M0,40 L300,40 M0,80 L300,80 M0,120 L300,120 M0,160 L300,160 M0,200 L300,200 M0,240 L300,240 M40,0 L40,300 M80,0 L80,300 M120,0 L120,300 M160,0 L160,300 M200,0 L200,300 M240,0 L240,300"
Stroke="#ddd"
StrokeThickness="1" />

结果得到如上图所示的效果,只是在有限的范围内渐变,而非整个大正方形区域内渐变。尽管和预期的效果不太一样,但依旧可以从中看出一些端倪:

  • 对角线上的小正方形中符合预期的渐变色
  • 渐变向量起点的颜色值填充了对角线左下方的空间,渐变向量末端的颜色值填充对角线右上方的空间
  • 最初得到的填充色为#377af5三角形相对于大正方形的区域颜色也是#377af5

根据这几点现象结合已有的知识分析推测,线性渐变画刷只作用于第0行0列的小正方形,对角线上的正方形及其两侧相邻的正方形颜色是由于插值算法补充的渐变色,对角线两侧的颜色是如同WPF中SpreadMethod.Pad的填充效果。

到了这里不禁会想,Points="240 19 240 40 220 19"Polygon真的没法实现渐变效果吗?Avalonia中StartPoint的百分比值能否超过100%?既然这个大正方形由六行六列的小正方形组成,那就把StartPoint的百分比值设为600%试试看,结果真的得到了预期的效果。下图是36个40x40的小正方形组成的大正方形和一个240x240的正方形的渐变效果对比,通过取色器抽查,每个坐标点颜色值一致。

到了这里,基本明白了Avalonia中线性画刷的机制,StartPoint设置相对值时需要用百分制的数值,与WPF中相对值模式不同的是,Avalonia中相对模式的百分比是基于绘制区域的尺寸,但坐标系统不是基于绘制区域边界,而是基于本地空间(local space)。这或许跟skiasharp的渲染机制有关。

由于相对模式的坐标系统是基于本地空间,这样并没有解决Points="240 19 240 40 220 19"Polygon实现渐变效果的需求,继而需要寻求绝对值模式的解决方式。Avalonia中线性画刷是否支持绝对值呢?查阅API,并没有找到MappingMode属性,难道真的不支持绝对值模式吗?回想一下,最初StartPoint设置为(0,1)是并没有报错,只是结果不是预期的,那看一下36个40x40的小正方形的画刷StartPoint设置为(0,1)是什么效果。

预览界面放大到800%后发现,渐变效果其实也是生效的,这里设置的(0,1)不就是绝对值嘛。当StartPoint="0 240" EndPoint="240 0"时,36个小正方形组成的240x240的区域也实现了预期的渐变效果。StartPoint="220 40" EndPoint="240 19"时就满足了Points="240 19 240 40 220 19"Polygon实现渐变效果的需求。

总结

经过一番尝试和分析,对于Avalonia中线性渐变画刷有了基本了解。归纳了以下几点内容:

  • Avalonia中线性渐变画刷既支持相对模式,也支持绝对模式。
  • StartPointEndPoint的取值为百分比时使用的相对模式,取值为数值则是绝对模式。类似于WPF中设置MappingMode
  • 相对模式下StartPointEndPoint的百分比值是基于绘制区域的尺寸,但坐标系统是基于本地空间(local space),而非相对于绘制区域边界。
  • Avalonia中线性渐变画刷也支持设置渐变范围以外区域的填充方式,和WPF中一样,通过设置SpreadMethod属性实现。

Avalonia中的线性渐变画刷LinearGradientBrush的更多相关文章

  1. (C#)WPF:LinearGradientBrush 线性渐变画刷和RadialGradientBrush 圆形渐变画刷

    <Window x:Class="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/200 ...

  2. WPF线性渐变画刷应用之——炫彩线条

    效果图: Xaml代码: <Rectangle Width="800" Height="10"> <Rectangle.Fill> &l ...

  3. WPF中线性渐变画刷的一个小窍门

    最近被项目里面控件的设计搞的死去活来的,大部分的设计都会需要使用进度条的功能,因为UI形状的变态,使用ProgressBar不能满足需求,没办法就自己想办法实现进度显示.折腾的多了发现一个很不错的方法 ...

  4. 深入理解css3中的线性渐变

    css3中的线性渐变 线性渐变公式: background-image: linear-gradient( [ <angle> | <side-or-corner> ]?, & ...

  5. Direct2D 第4篇 渐变画刷

    原文:Direct2D 第4篇 渐变画刷 #include <windows.h> #include <d2d1.h> #include <d2d1helper.h> ...

  6. CSS3中颜色线性渐变实战

    css3线性渐变可以设置3个参数值:方向.起始颜色.结束颜色.最简单的模式只需要定义起始颜色和结束颜色,起点.终点和方向默认自元素的顶部到底部.下面举例说明: CSS Code复制内容到剪贴板 .te ...

  7. WPF样式之画刷结合样式

    第一种画刷,渐变画刷GradientBrush (拿线性渐变画刷LinearGradientBrush(其实它涵盖在GradientBrush画刷内.现在拿他来说事.),还有一个圆心渐变画刷Radia ...

  8. GDI+编程(画笔/画刷/路径/区域)

    构造Graphics对象 Graphics类是GDI+程序设计的核心,Graphics类能够完成大部分的绘图,文本输出,几何图形的填充及坐标系统的转换等各种操作.在功能上,它与GDI的设备环境(DC) ...

  9. 简述WPF中的画刷(Brush)

    原文:简述WPF中的画刷(Brush) -------------------------------------------------------------------------------- ...

  10. WPF 10天修炼 第八天 - 形状、画刷和变换

    图形 在WPF中使用绘图最简单的就是使用Shape类.Shape类继承自FrameworkElement,是一个专门用来绘图的类.Shape类中年派生的类有直线.矩形.多边形和圆形等. System. ...

随机推荐

  1. UE4下实现HTC Vive手柄拾取物品蓝图编程

    UE4下实现Vive手柄拾取物品 标签: UE4Vive游戏引擎 目录(?) [-] 创建拾取接口 实现接口 定义拾取 简单说下思路,首先定义拾取接口,然后在需要拾取的物品的蓝图类中实现该接口,最后在 ...

  2. PIL.Image, numpy, tensor, cv2 之间的互转,以及在cv2在图片上画各种形状的线

    ''' PIL.Image, numpy, tensor, cv2 之间的互转 ''' import cv2 import torch from PIL import Image import num ...

  3. Python 生成带Logo的圆角带边框二维码

    Python 生成二维码方式就不累述了,不会的自己百度吧 但python生成的二维码太难看了,要么没有logo,要么logo直接贴进去的,难看死了,有的也处理了一下,但没有圆角,也难看: 以下:是不是 ...

  4. 为什么我要写spring.factories文件?

    在阅读spring-boot相关源码时,常常见到spring.factories文件,里面写了自动配置(AutoConfiguration)相关的类名,因此产生了一个疑问:"明明自动配置的类 ...

  5. vue登录3D效果

    实现的效果 登录动态效果很炫酷,话不多说直接上代码: 组件template <template> <div class="entrance"> <di ...

  6. React纯组件的使用

    1. 有无必要使用纯组件 如果应用不是很大型,页面渲染效率使用纯组件与非纯组件差别不大,尽量使用组件 应用一定注意,setState时子组件依赖渲染的属性一定要传递给子组件,不然父组件setState ...

  7. 安装Visual Studio 2010 教程

    1.下载软件 方法一:关注[ 火耳软件安装 ]公众号获取软件,里面还有很多类型的其他软件 或者: 方法二:我的分享链接:https://pan.baidu.com/s/1_Ow2YR-kbnbSc6o ...

  8. 【Serverless实战】B站每日自动签到&&传统单节点网站的Serverless上云

    简介: Serverless好哇!这里将针对个人与生产两个应用方向的测评 使用Serverless实现自动获取每日B站的经验值,让你更快冲到LV6! 你的业务站点还是一台服务器All in One吗? ...

  9. 一遇到复杂分析查询就卡顿?MySQL分析实例了解一下

    随着企业数据爆发式增长,MySQL分析查询卡顿问题越来越多,用户时效性不能保证,精细化运营诉求不能满足.如何能无缝对接业务库,实现毫秒级针对万亿级数据进行即时的多维分析透视和业务探索,MySQL分析实 ...

  10. Serverless 场景下 Pod 创建效率优化

    简介: 众所周知,Kubernetes 是云原生领域的基石,作为容器编排的基础设施,被广泛应用在 Serverless 领域.弹性能力是 Serverless 领域的核心竞争力,本次分享将重点介绍基于 ...