一次小而美的重构:使用 C# 在 Avalonia 中生成真正好看的词云
前言
我之前不是开发 StarBlogPublisher(一款 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 —— 简单高效,而且效果不错
一次小而美的重构:使用 C# 在 Avalonia 中生成真正好看的词云的更多相关文章
- AIDE支持实时错误检查、代码重构、代码智能导航、生成APK
AIDE是一个Android Java集成开发环境,可以在Android系统内进行Android软件和游戏的开发.它不仅仅是一个编辑器,而是支持编写-编译-调试运行整个周期,开发人员可以在Androi ...
- 以.net core重构原有.net framework过程中的一些API变更记录(持续更新)
1)Type.IsGenericType类似属性变更 以下是.net framework 4.5中Type抽象类中泛型类型的几个个属性,用于泛型类型的相关信息判断: 以下是.net core(nets ...
- NetCloud——一个网易云音乐评论抓取和分析的Python库
在17的四月份,我曾经写了一篇关于网易云音乐爬虫的文章,还写了一篇关于评论数据可视化的文章.在这大半年的时间里,有时会有一些朋友给我发私信询问一些关于代码方面的问题.所以我最近抽空干脆将原来的代码整理 ...
- .NET重构—单元测试的代码重构
阅读目录: 1.开篇介绍 2.单元测试.测试用例代码重复问题(大量使用重复的Mock对象及测试数据) 2.1.单元测试的继承体系(利用超类来减少Mock对象的使用) 2.1.1.公用的MOCK对象: ...
- 重构第26天 移除双重否定(Remove Double Negative)
理解:”移除双重否定”是指把代码中的双重否定语句修改成简单的肯定语句,这样即让代码可读,同时也给维护带来了方便. 详解:避免双重否定重构本身非常容易实现,但我们却在太多的代码中见过因为双重否定降低了代 ...
- Eclipse 中的重构功能
Eclipse 中的重构功能使其成为了一个现代的 Java 集成开发环境 (IDE),而不再是一个普通的文本编辑器.使用重构,您可以轻松更改您的代码,而不必担心对别处造成破坏.有了重构,您可以只关注于 ...
- C# 利用范型与扩展方法重构代码
在一些C#代码中常常可以看到 //An Simple Example By Ray Linn class CarCollection :ICollection { IList list; public ...
- eclipse重构详解(转)
重构是对软件内部结构的一种调整,目的是在不改变软件行为的前提下,提高其可理解性,降低其修改成本.开发人员可以使用一系列重构准则,在不改变软件行为的前提下,调整软件的结构. 有很多种原因,开发人员应该重 ...
- .NET重构—单元测试重构
.NET重构—单元测试重构 阅读目录: 1.开篇介绍 2.单元测试.测试用例代码重复问题(大量使用重复的Mock对象及测试数据) 2.1.单元测试的继承体系(利用超类来减少Mock对象的使用) 2.1 ...
- lua中 table 元表中元方法的重构实现
转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...
随机推荐
- AllPairs工具助力正交表测试用例设计
AllPairs工具助力正交表测试用例设计 正交表法是一种高效的测试方法,特别适用于软件测试中需要处理多个控件及其多种取值组合的情况.以下是对正交表法的详细解释: 一.正交表法概述 正交表法是一种利用 ...
- DeepSeek文本和编程测试
2025年春节期间,能直面电影<哪吒2>锋芒的,也就只有号称"国运级"的大模型DeepSeek了. 在科技圈里,这句话也可以反过来说. DeepSeek为什么能爆火,自 ...
- 13. Docker-compose容器编排(详解说明)
13. Docker-compose容器编排(详解说明) @ 目录 13. Docker-compose容器编排(详解说明) 1. Docker-compose容器编排概述 2. Docker-Com ...
- CSS 清除内外边距
网页元素很多都带有默认的内外边距,而且不同的浏览器的默认边距值也不一样.因此我们在布局之前,需要先清楚这些边距. <style> /* 这也是CSS的第一行代码 */ * { margin ...
- startup_stm32f10x_xx.s 启动代码文件的选择
网上查到的各个文件的解释是: startup_stm32f10x_cl.s 互联型的器件startup_stm32f10x_hd.s 大容量startup_stm32f10x_hd_vl.s 大容量s ...
- manim边学边做--标准相机
在Manim动画制作库中,Camera类是负责管理屏幕显示内容的核心类,其功能涵盖场景设置.对象渲染.坐标转换等多个关键方面. Camera类作为Manim中渲染流程的核心,在动画制作中主要作用包括: ...
- DW005 - ArgoDB基础
第1章 Argo基础 1.1 ArgoDB对象 说明 在ArgoDB中,可以使用常见的数据库对象,包括数据库(Database),表(Table),视图(View)和函数(Function) 可以使用 ...
- 是否有必要使用 Oracle 向量数据库?
向量数据库最主要的特点是让传统的只能基于具体值/关键字的数据检索,进化到了可以直接基于语义的数据检索.这在AI时代至关重要! 回到标题问题:是否有必要使用 Oracle 向量数据库? 这实际还要取决于 ...
- wxpython-窗体关闭
` def close(self, event): wx.Exit() `
- cypress route 拦截 zepto ajax 请求时候 response 返回 null 问题原因
原文链接:https://blog.jijian.link/2020-08-03/cypress-zepto-ajax-response-null/ 如果你项目有如下几个巧合,大概率会发现一个 aja ...

