一次小而美的重構:使用 C# 在 Avalonia 中生成真正好看的詞云
前言
我之前不是用 Avalonia 開發了 StarBlogPublisher(一款為 StarBlog 設計的 Markdown 文章發布工具)嗎?
當時里面有個分類 詞云(Word Cloud) 展示功能。
初版的詞云雖然 "能用",但效果極其粗糙——基本只是簡單堆疊文字,完全沒有體現出詞云那種靈動、密集、錯落有致的美感。
于是,我決定 徹底重構 這一模塊,重新尋找合適的詞云生成方案。
選型
在 Avalonia 生態中是沒有直接可用的詞云組件的。
不過沒事,C# 的生態還算豐富,基本要啥有啥,詞云自然不在話下。
在調研階段,我找到了兩個比較流行的 C# 詞云庫:
簡單對比一下:
| 特性 | Sdcb.WordCloud | KnowledgePicker.WordCloud |
|---|---|---|
| 渲染引擎 | SkiaSharp(跨平臺) | SkiaSharp(跨平臺) |
| 輸出格式 | 圖片(PNG)、SVG、JSON | 圖片(Bitmap)、SVG(需要自繪) |
| 自定義程度 | 高(遮罩、字體、多方向、JSON輸出等) | 中(字體、顏色、布局可定制,但不支持遮罩) |
| 遮罩功能 | ? 原生支持遮罩圖生成特定形狀詞云 | ? 暫不支持遮罩,生成規則矩形詞云 |
| 最近維護狀態 | 活躍(2024年持續更新) | 活躍(2024年有提交) |
| 使用復雜度 | 中(配置多、自由度高) | 中(較簡潔,適合快速集成) |
共同點
- 兩者都使用
SkiaSharp,意味著可以在 Windows、Linux、macOS 等多平臺運行。 - 都支持靈活配置字體、布局、顏色,并且速度非常快。
主要區別
- Sdcb.WordCloud 更注重視覺效果(支持復雜遮罩圖案),適合追求自定義形狀、炫酷效果的場景。
- KnowledgePicker.WordCloud 更注重性能和簡潔性,適合標準矩形詞云生成,不追求復雜形狀。
最終,我選擇了功能更強大、兼容性更好的 Sdcb.WordCloud。
Sdcb.WordCloud簡介
Sdcb.WordCloud 是一個基于 SkiaSharp 的跨平臺詞云生成庫,具備以下特點:
- 跨平臺兼容:Windows、Linux、macOS 均可使用。
- 多種輸出:支持生成圖片、SVG文件或JSON數據。
- 高度可定制:自定義字體、顏色、遮罩圖案、文本排列方式等。
- 無依賴System.Drawing:在服務器環境也能輕松部署。
- 開源友好:MIT License,開發者自由擴展。
安裝
dotnet add package Sdcb.WordCloud
實戰:在 StarBlogPublisher 中應用
重構后的詞云生成邏輯主要分為兩步:
獲取詞頻數據
首先,從后端API請求分類詞頻數據,并進行簡單擴充(讓詞云密度更高)。
private async Task<List<WordScore>?> GetWordScores() {
var response = await ApiService.Instance.Categories.GetWordCloud();
if (response.Data == null) throw new Exception("獲取詞云數據失敗");
var originalScores = response.Data
.Select(e => new WordScore(Score: e.Value, Word: e.Name))
.ToList();
var extendedScores = new List<WordScore>();
foreach (var score in originalScores) {
for (int i = 0; i < 10; i++) {
extendedScores.Add(score);
}
}
return extendedScores;
}
這里小技巧:
?? 將原本每個單詞的詞頻復制多次,可以有效提升詞云的視覺密度和豐富度。
生成詞云圖像
拿到詞頻數據后,使用 WordCloud.Create() 創建詞云對象,并通過遮罩圖案和字體定制,生成最終的詞云圖片。
private async Task GenerateWordCloudImage() {
var wordScores = await GetWordScores();
if (wordScores == null || !wordScores.Any()) {
ErrorMessage = "沒有可用的詞云數據";
return;
}
var wc = WordCloud.Create(new WordCloudOptions(900, 900, wordScores) {
FontManager = new FontManager([
SKTypeface.FromFamilyName("Times New Roman")
]),
Mask = MaskOptions.CreateWithForegroundColor(
SKBitmap.Decode(await new HttpClient().GetByteArrayAsync(
"https://io.starworks.cc:88/cv-public/2024/alice_mask.png"
)),
SKColors.White
)
});
using var skImage = wc.ToSKBitmap();
using var data = skImage.Encode(SKEncodedImageFormat.Png, 100);
using var stream = new MemoryStream(data.ToArray());
WordCloudImage = new Bitmap(stream);
}
這里用了兩點增強體驗的小技巧:
- ?? 遮罩圖:使用一張指定形狀的透明圖,詞云可以呈現人物輪廓、LOGO形狀等,極大提升美感。
- ?? 自定義字體:更換字體可以讓整體風格更符合網站/應用的設計感。
效果展示
話說之前的效果能算詞云嗎??
| 修改前 | 修改后 |
|---|---|
![]() |
![]() |
小結
通過這次重構,我總結出幾點經驗:
- 選對庫很重要,跨平臺、高擴展性是首要考慮。
- 詞云美觀與否,關鍵在于密度、遮罩形狀、字體風格的搭配。
- 盡可能異步請求和局部優化,避免UI卡頓。
如果你也在C#項目中需要集成詞云功能,推薦試試Sdcb.WordCloud —— 簡單高效,而且效果不錯
微信公眾號:「程序設計實驗室」
專注于互聯網熱門新技術探索與團隊敏捷開發實踐,包括架構設計、機器學習與數據分析算法、移動端開發、Linux、Web前后端開發等,歡迎一起探討技術,分享學習實踐經驗。



浙公網安備 33010602011771號