简介:面向前端开发者的ECharts词云图实战资料包,围绕词云图从数据准备、图表初始化到常用配置项设定给出完整demo,并逐项讲解sizeRange、rotationRange、textRotation、textStyle等核心参数,帮助读者快速做出适配自身项目的词云效果。压缩包共6个文件,包含可直接运行的HTML示例、3个JS文件(含ECharts主库、词云扩展与jQuery依赖)、1张效果预览图及1份配套使用说明,整体体积仅233KB,轻量易用。已有11883人学习下载,适合具备基础前端知识、希望快速上手ECharts词云图的开发者。资源既提供开箱即用的页面源码,又对关键配置参数做了整理说明,便于按需调整词形、字号、旋转角度与颜色随机策略,减少查阅文档的时间成本。
1. 词云图的真正难点在于配置参数的“手感”
ECharts 官方包其实不直接内置词云图,平时项目里最常见的做法是引入echarts-wordcloud这个扩展插件,再基于 ECharts 的series配置体系来组装。很多人第一次跑通 demo 觉得很简单,无非是定义type: 'wordCloud'、塞一段data数组,但真正到了业务里会发现三个问题:词与词之间为什么会挤成一团、为什么有的词大得离谱、为什么图片形状的词云在线上环境加载不出来。这三个问题全部指向同一件事——配置参数怎么调。本文不打算只给一套能复现的完整 demo,而是会从最小可运行版本开始,逐个拆sizeRange、gridSize、rotationRange、maskImage这些参数的底层含义和调整逻辑,顺带把异步加载、点击事件、中文分词这些真实场景里的配套写法一起给你。
2. echarts 词云图的引入方式与最小可运行 demo
2.1 为什么 echarts-wordcloud 需要单独安装
在 ECharts 4 时代,社区里常用的是echarts-wordcloud这个由 ecomfe 维护的扩展仓库;ECharts 5 发布后,它的主包仍然只保留常规图表类型,词云图依旧以独立插件形式存在。主要原因是词云布局算法(通常基于 d3-cloud 的算法思路)依赖 canvas 的逐像素计算和随机布局,和 ECharts 核心的绘图体系耦合度较低,做成插件既降低主包体积,也方便按需加载。
安装时的常见做法是:
npm install echarts echarts-wordcloud如果你的项目还是 script 标签直接引入,那就先引入echarts.min.js,再引入echarts-wordcloud.min.js,顺序不能反,否则插件挂载不到echarts命名空间上。
2.2 最小 demo:一个本地就能跑起来的词云图
下面这个 demo 不依赖任何打包工具,直接建一个 HTML 文件就能在浏览器里看到效果。它演示了词云图最基本的配置结构,也是后面讲参数时的对照样本。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <title>echarts 词云图最小 demo</title> <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/echarts-wordcloud@2.1.0/dist/echarts-wordcloud.min.js"></script> </head> <body> <div id="wc" style="width: 600px; height: 400px;"></div> <script> const chart = echarts.init(document.getElementById('wc')); const words = [ { name: '前端', value: 100 }, { name: '词云图', value: 80 }, { name: 'echarts', value: 60 }, { name: '配置参数', value: 50 }, { name: 'demo', value: 40 }, { name: '数据可视化', value: 30 } ]; const option = { series: [{ type: 'wordCloud', data: words, sizeRange: [14, 60], rotationRange: [0, 0], gridSize: 8, textStyle: { fontFamily: 'sans-serif' } }] }; chart.setOption(option); </script> </body> </html>这段配置的作用是:把 6 个词按value大小映射到 14 到 60 像素的字号区间,rotationRange: [0, 0]让所有词保持水平排列,gridSize: 8控制词与词之间的最小留白。如果你要把这段代码挪到 webpack 或 vite 项目里,只需要把script标签替换成import * as echarts from 'echarts'和import 'echarts-wordcloud',其余配置无需改动。
2.3 注册方式对项目体积的影响
在 ECharts 5 里,按需引入是官方推荐的用法,词云图插件在这套体系下有两种注册路径。
import * as echarts from 'echarts'; import 'echarts-wordcloud';插件内部会自己调用echarts.registerChart之类的注册逻辑,所以导入这个模块就够了。千万不要用import * as WordCloud from 'echarts-wordcloud'这种写法再去手动echarts.use,因为插件不是标准的 ECharts 组件模块,重复注册会报Component series.wordCloud not exists或者反而覆盖掉已有图表类型。
另外要留意版本兼容性:echarts-wordcloud@2.x对应 ECharts 5,echarts-wordcloud@1.x对应 ECharts 4。如果项目还在 ECharts 4,安装时应该指定npm install echarts-wordcloud@1,否则依赖树的版本冲突会一直报 warning。
3. 词云图核心配置参数详解:从数据映射到布局算法
3.1 series 里最容易理解错的两个字段:type 和 data
type: 'wordCloud'是插件注册时的系列名,不能写成wordcloud或word-clound,大小写敏感。data数组里的每一项是一个对象,name表示要展示的文字,value表示该词的权重,这个值会直接影响字号大小。
这里有一个关键逻辑:value并不直接等于最终的像素字号,它只是权重值。词云图内部会先统计整个数据集里value的最大值和最小值,再根据你设定的sizeRange做线性映射。例如sizeRange: [14, 60],value最小为 10 的词至少显示成 14px,value最大的词最多显示成 60px。也就是说,同一个词在不同的数据集合里,即使value相同,最终呈现的字号也可能完全不同。
const option = { series: [{ type: 'wordCloud', data: [ { name: '算法', value: 88, itemStyle: { color: '#c23531' } }, { name: '布局', value: 66 }, { name: '渲染', value: 44 } ], sizeRange: [12, 80] }] };注意上面代码里第一项多写了一个itemStyle,这是echarts-wordcloud支持的单项样式覆盖,优先级高于外层textStyle,在需要突出某个重点词时很实用。data里除了name、value、itemStyle,还可以带textStyle做单词语字号以外的独立控制。
3.2 sizeRange 和 gridSize:图片的“密度”与“稀疏度”怎么调
sizeRange是一个长度为 2 的数组,[min, max]控制最小和最大字号。这个参数直接决定整张图给人的视觉冲击力。min 太小会导致生僻小词几乎看不清,max 太大会让核心词和其他词之间的对比过度夸张。我一般会按“最大词的字号不超过容器短边的一半”来估算,比如 600x400 的容器里,max 设 80 以内比较安全。
gridSize是词云图布局时的网格步长,单位是像素。它控制每个词在布局时占用的最小格子大小。数值越小,词与词之间的缝隙越小,整体更紧凑,但布局计算量会上升;数值越大,词之间越稀疏,甚至会出现明显的空隙。
| 参数 | 类型 | 默认值 | 作用 | 适用建议 |
|---|---|---|---|---|
sizeRange | array | [12, 60] | 字号映射区间 | 词数少可加大 max |
gridSize | number | 8 | 布局网格步长 | 8 到 16 之间,词多调小 |
rotationRange | array | [-90, 90] | 旋转角度范围 | 想整齐就设[0, 0] |
rotationStep | number | 45 | 旋转角度的步进 | 配合 rotationRange 使用 |
shrinkToFit | boolean | false | 长词是否缩小字号以适配容器 | 中文长词建议开true |
shape | string | 'circle' | 词云整体形状 | circle、star、diamond等 |
rotationRange和rotationStep的组合需要单独说明。默认情况下词云图的词会在 -90 度到 90 度之间随机旋转,步进为 45 度,所以你会看到横排、竖排和 45 度斜排混在一起。想让某个词强制横排,只能在data项里单独写rotation: 0。全局想全部横排,就把rotationRange设成[0, 0],这也是中文后台管理系统里最常见的做法。
3.3 textStyle 与 shrinkToFit:中文字体族和超长词的兜底策略
textStyle继承自 ECharts 的图形文本样式,但词云图场景下最值得调的是fontFamily和fontWeight。中文字体如果直接用sans-serif,在部分 Windows 服务器上会渲染成默认宋体,观感偏陈旧。我会在系统里优先声明中文字体栈:
textStyle: { fontFamily: '"PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif', fontWeight: 'bold' }shrinkToFit是一个容易被忽略但非常关键的参数。当某个词本身很长,比如“前端数据可视化解决方案实践”,在布局到接近边界时,按正常字号放不下,默认行为会把这个词从候选位置剔除,导致这个词从图上消失。开启shrinkToFit: true之后,插件会尝试将这个词语的字号逐渐缩小,直到能被放进可用空间。如果你发现词云里总是少了一些长关键词,第一反应就应该是检查这个参数。
const option = { series: [{ type: 'wordCloud', shape: 'circle', sizeRange: [12, 70], rotationRange: [0, 0], gridSize: 10, shrinkToFit: true, textStyle: { fontWeight: 'bold' } }] };这段配置适合大部分中文业务词云页:横排、紧凑但不过度密集、长词不丢失。gridSize: 10相比默认的 8 稍微拉开间距,在 40 个以上的词量时观感会更透气。
3.4 按数据规模选择 shape 和布局倾向
shape参数支持circle、cardioid、diamond、triangle、triangle-forward、pentagon、star这几种内置形状。需要注意,有些形状如pentagon和star的可放置区域比圆形小得多,词多的时候会出现大量词放不进有效区域而被丢弃的情况。所以数据量超过 50 个词时,我一般不建议用非圆形 shape,除非你已经确认丢几个非核心词不影响业务表达。
形状选择背后本质上是“可用区域”的几何约束。使用maskImage的时候,shape会被忽略,这个话题放到第 5 章具体讲。
4. 词云图的数据加载、交互与中文词频适配
4.1 从异步接口拉数据并渲染的标准写法
实际开发中词云图的数据很少写死在 code 里,大多来自搜索日志、文章标签、评论关键词之类的统计分析接口。接口返回的格式一般是下面这种:
[ { "word": "前端", "count": 1200 }, { "word": "架构", "count": 876 } ]这个格式不能直接塞给词云图,因为series.data需要的是name和value字段。前端在拿到接口数据之后要做一次map映射。
async function loadWordCloud(url, chartInstance) { const response = await fetch(url); const rawList = await response.json(); const words = rawList.map(item => ({ name: item.word, value: item.count })); chartInstance.setOption({ series: [{ type: 'wordCloud', data: words, sizeRange: [12, 64], gridSize: 8, rotationRange: [0, 0], shrinkToFit: true }] }); }这段代码的逻辑很直白:fetch获取数据,map转换字段名,然后一次性把 data 丢给setOption。其中chartInstance是echarts.init之后的实例对象,在 Vue 或 React 组件里一般放在ref或useRef中管理,不要在每次更新时重新init。
有一点要提醒:如果页面里同时有多个 tab 或图表,setOption的第二个参数建议传true,即chartInstance.setOption(option, true),表示整个配置替换而不是合并,否则上一次的数据残留在旧 series 里会导致图表渲染异常。
4.2 词云图上的点击事件与 tooltip
词云图天然适合做聚合页的导航入口:点击某个词跳到对应的搜索页或列表页。ECharts 事件绑定的方式是chart.on,对词云图来说,click事件的回调参数里params.name就是被点击的词,params.value是它的权重值。
chart.on('click', (params) => { if (params.componentType === 'series' && params.seriesType === 'wordCloud') { console.log('clicked word:', params.name); // 在这里做路由跳转或弹窗 } }); chart.setOption({ tooltip: { show: true, formatter: (params) => `${params.name}<br/>权重值:${params.value}` } });tooltip的formatter支持字符串模板和函数两种写法,上面用的是函数式,方便附加单位或额外说明。注意params.componentType的过滤很关键,因为图表容器上的空白区域也会触发click事件,如果不过滤,用户点空白处会拿到一个不完整的params对象。
4.3 中文分词:词云图数据源怎么准备才不出乱象
很多人以为词云图“不行”,其实问题出在数据源是一段一段的句子,而不是分词后的词组。前端如果想直接从一段长文本生成词云,最常见的做法是引入轻量分词库,比如nodejieba只能在服务端跑,浏览器里用segmentit或tiny-segmenter这类纯 JS 分词库。
const { Segment, useDefault } = require('segmentit'); const segment = new Segment(); segment.use(useDefault()); const text = '前端开发是构建用户界面的工程学科,涉及页面结构、样式和交互逻辑。'; const result = segment.doSegment(text, { simple: true }); console.log(result); // ['前端', '开发', '构建', '用户界面', '工程', '学科']得到分词数组之后,还需要过滤掉“的、了、是、和”这类停用词以及单字词,再做一次词频统计,才能生成词云图的 data。实际项目里更推荐的架构是在服务端完成分词和词频统计,把纯[{name, value}]格式返回给前端,因为浏览器分词一方面受限于体积,另一方面处理大段文本时主线程阻塞会明显影响首屏体验。
function countWords(wordList) { const map = new Map(); for (const w of wordList) { map.set(w, (map.get(w) || 0) + 1); } return Array.from(map.entries()) .filter(([word]) => word.length > 1 && !stopWords.has(word)) .map(([name, value]) => ({ name, value })) .sort((a, b) => b.value - a.value) .slice(0, 100); }这段代码就是典型的“前端词频统计三步走”:Map计数、过滤长度和停用词、截断前 100 个词。截断的目的是避免词太多导致布局拥挤或计算卡顿,100 个词以内词云图能保持在百毫秒级的布局速度。
4.4 自适应宽高与窗口 resize
词云图在响应式布局里有一个容易踩的坑:初始化时容器是隐藏或宽度为 0 的,比如在弹窗里、折叠面板里,或者异步渲染的列表里。这种情况下echarts.init拿到的容器宽度是 0,绘制出来就是空白或错乱。解决办法有两个,一个是确保容器可见后再 init,另一个是监听窗口变化时调用resize。
window.addEventListener('resize', () => { chart.resize(); });如果你的容器宽度变化不是因为窗口尺寸变化,而是因为侧边栏折叠,那就需要在折叠动画结束后手动调用chart.resize()。词云图对 resize 的响应不如折线图、柱状图那么“宽容”,因为布局算法在尺寸变化后需要重新计算所有词的位置,建议在 resize 时顺手做一次重绘,避免出现文字分布在容器之外的迹象。
5. 把词云图调出彩的三个细节技巧
5.1 用 maskImage 做品牌形状词云
echarts-wordcloud 支持通过maskImage指定一张图片作为词云的裁剪形状。这个功能在活动运营页非常常用,比如品牌 Logo、特殊文字轮廓。但使用时有几个容易踩的坑:图片必须是网络可访问的完整地址或 dataURL,本地相对路径在某些打包配置下会失效;并且shape参数在设置了maskImage时会自动被忽略。
series: [{ type: 'wordCloud', maskImage: 'https://example.com/logo.png', sizeRange: [10, 50], rotationRange: [0, 0] }]我在实际项目中一般会把 Logo 图片提前压缩并转成 dataURL 塞进配置里,避免线上偶发的图片加载失败导致整个词云图不渲染。另外图片背景需要是纯色且轮廓清晰,透明 PNG 的效果最好,否则边缘会出现不规则的噪声词。
收敛参数对形状类词云尤其重要。原本在圆形布局下能放 100 个词的区域,换成 Logo 形状后有效区域可能只有一半,很多词会被丢弃。建议把sizeRange的 max 调小 10% 到 20%,gridSize适当增大,给边缘区域更多缓冲,才能保证词不溢出到形状外面。
5.2 随机种子:让两次渲染布局一致
词云图布局算法有随机性,同一份数据每次刷新后词的位置和角度都可能不同,这在某些需要截图对比或做动画过渡的场景里会带来困扰。协议上是支持在所有词固定后,通过设置某种固定布局来稳定渲染的,但 echarts-wordcloud 本身没公开随机种子参数。我的处理方式是把渲染逻辑包成一个纯函数:输入是 data 和配置,输出是 finalOption,在单元测试层面断言核心词的渲染位置不为空。要真正保持布局稳定,更直接的做法是在拿到数据后先按value降序排序,让核心词优先参与布局,这样即使位置轻微变化,视觉重点也不会跑偏。
5.3 大数据量下的降级策略
当词的数量超过 200,布局计算耗时和内存占用都会显著上升,尤其在低端移动设备上会出现明显卡顿。我的做法是分级渲染:首次渲染用 top 80 个词,保证首屏流畅;然后通过setTimeout在下一次空闲时把完整数据合并进去,用chart.setOption增量更新。
chart.setOption({ series: [{ type: 'wordCloud', data: top80Words, sizeRange: [12, 64] }] }); requestIdleCallback(() => { chart.setOption({ series: [{ type: 'wordCloud', data: fullWords, sizeRange: [12, 64] }] }); });requestIdleCallback在这里的作用是把重计算调度到浏览器空闲时段,避免阻塞点击和滚动事件。如果浏览器兼容性要求高,也可以用setTimeout(..., 200)降级替代。判断是否需要降级的经验阈值是:词的数量超过 200,或者单个词文本长度超过 20 个字符,两者满足其中一个就建议启动分级渲染策略。
本文还有配套的精品资源,点击获取