一次小而美的重构:使用 C# 在 Avalonia 中生成真正好看的词云
重构StarBlogPublisher中词云展示功能的过程。作者对比了两个流行的C#词云库,最终选择了功能更强大的Sdcb.WordCloud。文章详细说明了如何使用该库获取词频数据并生成词云图像,并展示了重构前后的效果对比。
目录11 个章节
前言
我之前不是用 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 —— 简单高效,而且效果不错
_EOF_
一次小而美的重构:使用 C# 在 Avalonia 中生成真正好看的词云
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
开发现代化的.NetCore控制台程序:(2)创建一个C#项目模板
又一个.NetCore模板?FluentConsole.Templates,一键生成骨架,包含DI、日志、配置。安装?dotnet new install,然后dotnet new flu-cli -n MyProject。太简单了吧,谁在乎?
2023-11-10 · C# · 约 1 分钟
C#高性能数组拷贝实验
前言 昨天 wc(Wyu_Cnk) 提了个问题 C# 里多维数组拷贝有没有什么比较优雅的写法? 这不是问对人了吗?正好我最近在搞图像处理,要和内存打交道,我一下就想到了在C#里面直接像C/C++一样做内存拷贝。 优雅?no,要的就是装逼,而且性能还要强🕶 概念 首先澄清一下 C# 里的多维数组 (Multi-dimensional Array) 是这样的 byte[,] arr = new by
2023-01-30 · C# · 约 1 分钟
使用C#和MemoryCache组件实现轮流调用APIKey以提高并发能力
本文介绍了如何利用C#语言中的MemoryCache组件,结合并发编程技巧,实现轮流调用多个API Key以提高系统的并发能力。通过示例代码和详细说明,读者将了解如何有效地管理API Key的调用次数限制,并优化系统性能。
2024-03-09 · C# · 约 1 分钟
C#学习(一)委托的概念和使用
前言 C#学习系列是根据我以前的笔记整理出来复习,顺便发一下文章做个记录。 先引用一个介绍: C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。 委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。 根据使用其他
2021-12-18 · C# · 约 1 分钟
讨论
共 0 条加载评论中…

