一次小而美的重构:使用 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 ...
随机推荐
- 安全可信 | 天翼云自研密钥管理系统(KMS)获得《商用密码产品认证证书》!
近日,天翼云自研密钥管理系统(KMS)通过国家密码管理局安全性审查,符合GM/T 0051<密码设备管理 对称密钥管理技术规范>要求,获得由国家密码管理局商用密码检测中心颁发的<商用 ...
- redis启停shell脚本
启停脚本(redis-5.0.5) 一.编辑脚本 vim /u01/redis/redisServer.sh #!/bin/sh # # Simple Redis init.d script conc ...
- spring的事务管理有几种方式实现
一.事务的4个特性: 原子性:一个事务中所有对数据库的操作是一个不可分割的操作序列,要么全做,要么全部做. 一致性:数据不会因为事务的执行而遭到破坏. 隔离性:一个事务的执行,不受其他事务 ...
- C# 程序流控制 条件语句
C#语言最基本的重要语句:控制程序流的语句.它们不是按代码在程序中的排列位置顺序执行的. 条件语句 条件语句可以根据条件是否满足或根据表达式的值来控制代码的执行分支.C#有两个控制代码的分支结构: i ...
- Luogu P5005 中国象棋 - 摆上马 / Luogu P8756 国际象棋 题解 [ 蓝 ] [ 状压 dp ] [ 位运算 ]
国际象棋:模板棋盘状压. 摆上马:需要点思维的棋盘状压,相比上一道题加了"蹩马脚"的设定. Easy_version :国际象棋 概述一下此类棋盘问题的思路: 用二进制数表示出棋盘 ...
- STM32 DMA操作
https://blog.csdn.net/u014754841/article/details/79525637?utm_medium=distribute.pc_relevant.none-tas ...
- 如何在 PIP 配置文件中设置默认源?
在不同的操作系统中,在 PIP 配置文件中设置默认源的方法如下: Windows 操作系统 打开文件资源管理器,在地址栏输入 %APPDATA% 并回车,进入用户配置目录. 在该目录下创建一个名为 p ...
- swoole(8)http服务
简介: swoole内置实现了一个简单的httpServer类.swoole的http server相当于php-fpm.最大优势在于高性能,代码只载入一次 http_server本质是swoole_ ...
- 基于Microsoft.Extensions.AI核心库实现RAG应用
大家好,我是Edison. 之前我们了解 Microsoft.Extensions.AI 和 Microsoft.Extensions.VectorData 两个重要的AI应用核心库.基于对他们的了解 ...
- 【Abaqus Case】2D弹塑性接触分析
2D 弹塑性-接触分析 本案例属于材料非线性+边界条件非线性 问题描述 前处理 材料 *Material, name=steel *Density 7.8e-09, *Elastic 210000., ...

