曦远 (DealiAxy)一份私人的文字刊物

StarBlog
返回文章列表折页手记阅读全文
C#

一次小而美的重构:使用 C# 在 Avalonia 中生成真正好看的词云

重构StarBlogPublisher中词云展示功能的过程。作者对比了两个流行的C#词云库,最终选择了功能更强大的Sdcb.WordCloud。文章详细说明了如何使用该库获取词频数据并生成词云图像,并展示了重构前后的效果对比。

曦远 (DealiAxy)约 10 分钟
目录11 个章节

前言

我之前不是用 Avalonia 开发了 StarBlogPublisher(一款为 StarBlog 设计的 Markdown 文章发布工具)吗?

当时里面有个分类 词云(Word Cloud) 展示功能。 初版的词云虽然 "能用",但效果极其粗糙——基本只是简单堆叠文字,完全没有体现出词云那种灵动、密集、错落有致的美感。

于是,我决定 彻底重构 这一模块,重新寻找合适的词云生成方案。

选型

在 Avalonia 生态中是没有直接可用的词云组件的。

不过没事,C# 的生态还算丰富,基本要啥有啥,词云自然不在话下。

在调研阶段,我找到了两个比较流行的 C# 词云库:

简单对比一下:

特性Sdcb.WordCloudKnowledgePicker.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形状等,极大提升美感。
  • 🖋 自定义字体:更换字体可以让整体风格更符合网站/应用的设计感。

效果展示

话说之前的效果能算词云吗??

修改前修改后
image-20250426235822892image-20250426235918517

小结

通过这次重构,我总结出几点经验:

  • 选对库很重要,跨平台、高扩展性是首要考虑。
  • 词云美观与否,关键在于密度、遮罩形状、字体风格的搭配。
  • 尽可能异步请求和局部优化,避免UI卡顿。

如果你也在C#项目中需要集成词云功能,推荐试试Sdcb.WordCloud —— 简单高效,而且效果不错

_EOF_

一次小而美的重构:使用 C# 在 Avalonia 中生成真正好看的词云

本文作者:
程序设计实验室
版权声明:
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关文章

C#爬虫:CatSpider#-开发笔记

前言 (PS:我这里用了#号代替了Sharp这个单词) CatSpider是毕设里的数据采集模块,本来爬虫类的应用肯定使用python来开发嘛,不过用request_html做解析的时候,python的动态类型真的把我恶心到了,而且感觉这个库也不是很成熟,html5lib也不好用,也没心思去深入了,之前看到有大佬用.net core平台做爬虫,于是我也来试试,没想到效果贼好,特别是配合LinqPa

2022-05-27 · C# · 约 1 分钟

C#中的Attribute与Python-TypeScript的装饰器是同个东西吗

前言 最近成功把「前端带师」带入C#的坑~~(实际是前端带师开始从cocos转unity游戏开发了)~~ 某天,「前端带师」看到这段代码后问了个问题:[这个是装饰器]? [HttpGet] public Response Get() { return ... } 我第一反应觉得不是,这玩意在C#中叫“特性”(英文名Attribute,下文统称为特性),在Java中叫注解,虽然写法和Pyth

2022-06-26 · C# · 约 1 分钟

C#实现解析Markdown文档并处理图片链接

前言 又是好久没写博客了 其实也不是没写,是最近在「做一个博客」,从2月21日开始,大概一个多星期的时间,疯狂刷进度,边写代码边写了一整系列的博客开发笔记,目前为止已经写了16篇了,然后上3月之后工作有点忙~~加上有点沉迷原神~~,然后进度就暂时慢下来了 事实证明做什么事情一旦停下来就会产生惰性,于是停了这么久,我终于下定决心继续更新! 这次开发的博客用的是C#语言,搭配.netcore技术栈;前

2022-03-19 · C# · 约 1 分钟

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…