Avalonia中的線性漸變畫刷LinearGradientBrush

来源:https://www.cnblogs.com/czwy/p/18204994
-Advertisement-
Play Games

自定義可移動點二維坐標軸控制項 目錄 路由參數 坐標軸控制項定義 Demo 路由參數 X_YResultCollection為當前X軸對應Y軸值存儲字典 public class ResultCollectionChangedEventArgs(RoutedEvent routedEvent, obje ...


在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>

image

如果StartPoint是(0,0),終點EndPoint是(1,0),則是沿著水平方向從左到右漸變。
如果StartPoint是(0,0),終點EndPoint是(0.5,0),漸變效果如下圖所示:
image

通過這個例子,可以看出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" />

image

結果得到如上圖所示的效果,只是在有限的範圍內漸變,而非整個大正方形區域內漸變。儘管和預期的效果不太一樣,但依舊可以從中看出一些端倪:

  • 對角線上的小正方形中符合預期的漸變色
  • 漸變向量起點的顏色值填充了對角線左下方的空間,漸變向量末端的顏色值填充對角線右上方的空間
  • 最初得到的填充色為#377af5三角形相對於大正方形的區域顏色也是#377af5

根據這幾點現象結合已有的知識分析推測,線性漸變畫刷只作用於第0行0列的小正方形,對角線上的正方形及其兩側相鄰的正方形顏色是由於插值演算法補充的漸變色,對角線兩側的顏色是如同WPF中SpreadMethod.Pad的填充效果。

到了這裡不禁會想,Points="240 19 240 40 220 19"Polygon真的沒法實現漸變效果嗎?Avalonia中StartPoint的百分比值能否超過100%?既然這個大正方形由六行六列的小正方形組成,那就把StartPoint的百分比值設為600%試試看,結果真的得到了預期的效果。下圖是36個40x40的小正方形組成的大正方形和一個240x240的正方形的漸變效果對比,通過取色器抽查,每個坐標點顏色值一致。
image

到了這裡,基本明白了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)是什麼效果。
image

預覽界面放大到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屬性實現。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 市面上關於認證授權的框架已經比較豐富了,大都是關於單體應用的認證授權,在分散式架構下,使用比較多的方案是--<應用網關>,網關里集中認證,將認證通過的請求再轉發給代理的服務,這種中心化的方式並不適用於微服務,這裡討論另一種方案--<認證中心>,利用jwt去中心化的特性,減輕認證中心的壓力,有理 ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • 在小公司中,往往沒有一個前後端分離的大型團隊,去各司其職的負責構建web應用程式。面對比較簡單的需求,可能所謂團隊只有一個人,既要開發前端又要開發後端。 如果能有一項技術,能夠前後端通吃,並且具備非常高的開發效率,那就非常適合小公司的小型項目的小型甚至一人團隊來使用了。 aspdotnet就是這樣高 ...
  • 結構體 struct 是一種用戶自定義的值類型,常用於定義一些簡單(輕量)的數據結構。對於一些局部使用的數據結構,優先使用結構體,效率要高很多。 ...
  • 在很早之前,就想過開發一款抽獎軟體,卻一直沒有實際去做,最近經過一段時間的準備,終於開發出了一款基於WPF+Sqlite版的抽獎軟體,包括客戶端和管理端。本項目主要是為了熟悉WPF開發流程,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 安裝nuget包 Wesky.Net.OpenTools 1.0.8或以上版本。支持.net framework 4.6以上版本,以及所有.net core以及以上版本引用。 開發一個簡單的Winform界面,用來測試使用。如需該winform的demo,可以在公眾號【Dotnet Dancer】後 ...
  • 最近群里有個小伙伴把Dapper遷移SqlSugar幾個不能解決的問題進行一個彙總,我正好寫一篇文章來講解一下 一、sql where in傳參問題: SELECT * FROM users where id IN @ids 答: SqlSugar中應該是 var sql="SELECT * FRO ...
  • 自定義分頁控制項 tip: 該控制項的樣式用的是materialDesign庫,需要下載Nuget包 Code Xaml <UserControl x:Class="TestTool.CustomControls.PagingControl" xmlns="http://schemas.microsof ...
一周排行
    -Advertisement-
    Play Games
  • PasteSpider是什麼? 一款使用.net編寫的開源的Linux容器部署助手,支持一鍵發佈,平滑升級,自動伸縮, Key-Value配置,項目網關,環境隔離,運行報表,差量升級,私有倉庫,集群部署,版本管理等! 30分鐘上手,讓開發也可以很容易的學會在linux上部署你得項目! [從需求角度介 ...
  • SQLSugar是什麼 **1. 輕量級ORM框架,專為.NET CORE開發人員設計,它提供了簡單、高效的方式來處理資料庫操作,使開發人員能夠更輕鬆地與資料庫進行交互 2. 簡化資料庫操作和數據訪問,允許開發人員在C#代碼中直接操作資料庫,而不需要編寫複雜的SQL語句 3. 支持多種資料庫,包括但 ...
  • 在C#中,經常會有一些耗時較長的CPU密集型運算,因為如果直接在UI線程執行這樣的運算就會出現UI不響應的問題。解決這類問題的主要途徑是使用多線程,啟動一個後臺線程,把運算操作放在這個後臺線程中完成。但是原生介面的線程操作有一些難度,如果要更進一步的去完成線程間的通訊就會難上加難。 因此,.NET類 ...
  • 一:背景 1. 講故事 前些天有位朋友在微信上丟了一個崩潰的dump給我,讓我幫忙看下為什麼出現了崩潰,在 Windows 的事件查看器上顯示的是經典的 訪問違例 ,即 c0000005 錯誤碼,不管怎麼說有dump就可以上windbg開幹了。 二:WinDbg 分析 1. 程式為誰崩潰了 在 Wi ...
  • CSharpe中的IO+NPOI+序列化 文件文件夾操作 學習一下常見的文件、文件夾的操作。 什麼是IO流? I:就是input O:就是output,故稱:輸入輸出流 將數據讀入記憶體或者記憶體輸出的過程。 常見的IO流操作,一般說的是[記憶體]與[磁碟]之間的輸入輸出。 作用 持久化數據,保證數據不再 ...
  • C#.NET與JAVA互通之MD5哈希V2024 配套視頻: 要點: 1.計算MD5時,SDK自帶的計算哈希(ComputeHash)方法,輸入輸出參數都是byte數組。就涉及到字元串轉byte數組轉換時,編碼選擇的問題。 2.輸入參數,字元串轉byte數組時,編碼雙方要統一,一般為:UTF-8。 ...
  • CodeWF.EventBus,一款靈活的事件匯流排庫,實現模塊間解耦通信。支持多種.NET項目類型,如WPF、WinForms、ASP.NET Core等。採用簡潔設計,輕鬆實現事件的發佈與訂閱。通過有序的消息處理,確保事件得到妥善處理。簡化您的代碼,提升系統可維護性。 ...
  • 一、基本的.NET框架概念 .NET框架是一個由微軟開發的軟體開發平臺,它提供了一個運行時環境(CLR - Common Language Runtime)和一套豐富的類庫(FCL - Framework Class Library)。CLR負責管理代碼的執行,而FCL則提供了大量預先編寫好的代碼, ...
  • 本章將和大家分享在ASP.NET Core中如何使用高級客戶端NEST來操作我們的Elasticsearch。 NEST是一個高級別的Elasticsearch .NET客戶端,它仍然非常接近原始Elasticsearch API的映射。所有的請求和響應都是通過類型來暴露的,這使得它非常適合快速上手 ...
  • 參考delphi的代碼更改為C# Delphi 檢測密碼強度 規則(仿 google) 仿 google 評分規則 一、密碼長度: 5 分: 小於等於 4 個字元 10 分: 5 到 7 字元 25 分: 大於等於 8 個字元 二、字母: 0 分: 沒有字母 10 分: 全都是小(大)寫字母 20 ...