简介:针对前端数据可视化场景,这份词云图开发资源整理了完整的可运行示例与配置参数说明,适合需要快速掌握词云图实现的前端开发者及数据分析师。压缩包内共6个文件,包含3个JavaScript脚本、1个HTML页面、1份使用说明文档和1张效果示例图,整体仅233KB,轻量且结构紧凑,直接打开HTML即可查看效果。脚本已集成词云图所需的常用依赖,免去单独引入的繁琐;资源已有11883人学习下载,适合作为实际项目的基础模板。内容从基础环境引入到词云图数据组织、初始化与配置,重点详解形状、字体大小范围、旋转角度、文字颜色等核心参数,并附带高级应用思路,便于在新闻分析、社区话题挖掘等场景灵活复用。资源内附使用说明文档,对示例结构、参数含义和常见问题做了梳理,整体循序渐进,帮助刚接触词云图的开发者高效上手。
1. echarts 词云图不是内置图表,demo 先跑通才是关键
把一堆用户反馈倒进画布,高频词自动呈现大小错落的视觉层级,这是词云图比柱状图更直观的地方。但 echarts 官方包并不包含词云图,必须借助 echarts-wordcloud 扩展,所以很多人第一次做前端 echarts 词云图时,卡在安装、注册和系列类型上。一个能直接打开运行的 demo,比反复看文档更解决问题;配置参数则是把 demo 改成可用产品的最后一段距离。下面先给一个最小可运行 demo,再逐个拆解 wordCloud 系列的布局、文本、高亮参数,最后把数据清洗、点击事件和性能调优串起来。适合前端开发、数据可视化初学者,也适合在准备前端面试时,把 echarts 词云图的原理与配置讲完整。
2. echarts 词云图 demo 的最小可运行页面与数据格式
2.1 安装与引入:先搞清楚 echarts-wordcloud 是怎么挂载的
echarts-wordcloud 不是一个独立的图表库,它是在 echarts 的扩展机制上开发的插件。引入之后,它会向 echarts 注册一个名为wordCloud的系列类型,所以在写配置时只需要把series[0].type设置为wordCloud。如果跳过这个扩展,echarts 主包遇到wordCloud会直接抛错:Series wordCloud is not used。为了不卡在第一步,这里给出两种最常见的引入方式。
在 npm 工程里,echarts-wordcloud 作为 echarts 的扩展包存在,安装 echarts 和它两个包就够了。对于 Vue 或 React 项目,推荐在入口文件里统一引入并挂到全局,而不是每个组件都注册一遍。如果只是本地调试一个独立 demo,用 CDN 方式更快,关键是先加载 echarts 主包,再加载 echarts-wordcloud,顺序反了会出现扩展注册不到全局 echarts 对象上的问题。
npm install echarts echarts-wordcloudimport * as echarts from 'echarts'; import 'echarts-wordcloud';上述命令先把两个依赖装进 package.json,然后在代码里import 'echarts-wordcloud'。这个扩展包内部会读取全局的 echarts 构造函数,并在其上注册wordCloud系列。所以页面里只要出现一次扩展引用,后续所有chart.setOption都能识别type: 'wordCloud'。用 CDN 加载echarts.min.js后再加载echarts-wordcloud.min.js,效果和 npm 引入一致,只是把依赖粒度放到了 script 标签层级。
2.2 一个可以直接保存运行的完整 HTML demo
下面这个 HTML 文件可以保存成wordcloud-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/dist/echarts.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/echarts-wordcloud@2/dist/echarts-wordcloud.min.js"></script> </head> <body> <div id="word-cloud" style="width: 800px; height: 600px"></div> <script> const words = [ { name: '前端', value: 98 }, { name: 'JavaScript', value: 86 }, { name: 'TypeScript', value: 72 }, { name: 'Vue', value: 66 }, { name: 'React', value: 63 }, { name: 'CSS', value: 52 }, { name: 'ECharts', value: 48 }, { name: '性能优化', value: 42 }, { name: '工程化', value: 37 }, { name: '组件化', value: 31 }, { name: '数据可视化', value: 28 }, { name: '浏览器', value: 25 }, { name: '网络协议', value: 22 }, { name: '构建工具', value: 18 }, { name: '调试', value: 15 } ]; const chart = echarts.init(document.getElementById('word-cloud')); chart.setOption({ tooltip: { formatter: (params) => `${params.name}<br/>词频:${params.value}` }, series: [{ type: 'wordCloud', shape: 'circle', left: 'center', top: 'center', width: '80%', height: '80%', sizeRange: [14, 60], rotationRange: [-90, 90], rotationStep: 45, gridSize: 8, drawOutOfBound: false, textStyle: { fontFamily: 'Arial, "Microsoft YaHei", sans-serif', fontWeight: 'bold', color: () => { const r = Math.round(Math.random() * 160); const g = Math.round(Math.random() * 160); const b = Math.round(Math.random() * 160); return `rgb(${r},${g},${b})`; } }, emphasis: { textStyle: { color: '#ff6600' } }, data: words }] }); </script> </body> </html>容器#word-cloud建议给固定宽高,因为 echarts.init 在容器没有宽高时会拿到 0,图表的 canvas 不会被绘制。数据数组中每一项的结构是{ name, value },name是展示的词,value控制字号权重。series.type写成wordCloud才能触发扩展里的布局算法。left/top/width/height四个值一起配合,让绘图区比容器四周留出边距,避免字号最大的词顶到边缘。textStyle里的fontFamily对中文词很重要,单独写sans-serif也能显示,但指定中文字体可以避免个别平台渲染出奇怪的字形。
tooltip 里给一个箭头函数,可以直接读到params.name和params.value。词云图没有 x/y 轴,tooltip 默认回调的params就是当前词条对应的 data 项。这比用value回调再拼字符串更直观。
2.3 数据格式与渲染流程
wordCloud 系列的数据格式和 echarts 饼图非常接近:数组里的对象必须有name和value,也可以用itemStyle单独覆盖某个词的颜色、字体。它不接受[value, name]这样的元组形式,也不会自动做分词。
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| name | string | 是 | 要展示的关键词,支持中英文 |
| value | number | 是 | 权重,值越大字号越大 |
| itemStyle.color | string | 否 | 覆盖该词颜色 |
| itemStyle | object | 否 | 该词独立样式,优先级高于系列级 textStyle |
当setOption执行后,echarts 会先把 series 配置传给 echarts-wordcloud 的布局器。布局器把所有词条按value从大到小排序,从区域中心开始尝试放置,每放一个词就与已放置的词做矩形碰撞检测,直到没有重叠或达到最大尝试次数。所以数据量越大、gridSize越小,计算耗时越长。理解这个渲染流程,后面调参数时就不会只盯着视觉,还会想到布局性能。
3. echarts 词云图 wordCloud 系列配置参数详解:从 gridSize 到 rotationStep
3.1 布局参数决定词云密度和边界
词云图的画面效果主要由布局参数决定,而不是字体。第一次跑完 demo 后,通常会遇到三个问题:所有词挤在左上角、词与词粘在一起、边缘词被切掉,分别对应sizeRange、gridSize、drawOutOfBound三个参数。
sizeRange: [min, max]控制最小和最大字号,单位是 px。value 最大值映射到 max,最小值映射到 min。若数据里最大词频和最小词频相差很大,建议先做归一化,否则长尾词的字体几乎看不见。若所有 value 都接近,画布上的字会显得大小拉不开,可以把 range 跨度拉大,比如[12, 80]。
gridSize是布局时移动的步长,也是词与词之间最小间隙。默认值是 8,数值小会让布局更紧凑,但碰撞检测次数会显著上升;数值大则词间距变大,图表更稀疏。drawOutOfBound默认 false,绘制时会把超出边界的词裁掉。如果设成 true,接近边缘的词可能只显示一半,建议保持 false。
| 参数 | 默认值 | 典型范围 | 调参方向 |
|---|---|---|---|
| sizeRange | [12, 60] | [10, 80] | 词频差异大时调大 min/max 跨度 |
| gridSize | 8 | 4 ~ 20 | 太密或太疏时调整 |
| drawOutOfBound | false | true/false | 边缘单词被截断就检查此项 |
| width / height | 75% | 70% ~ 100% | 留边距或铺满画布 |
需要特别注意的是,这些参数修改后词云布局会完全重排。如果只是想微调,优先改gridSize,它不影响字号映射,只影响间距。left、top支持center和百分比,width、height建议用百分比,这样在chart.resize()后能跟随容器缩放。
3.2 旋转与字体参数:让中文词云不歪七扭八
rotationRange和rotationStep是一对配合使用的参数。rotationRange只会从rotationStep的倍数中取角度,例如[-90, 90]配45,候选角度是 -90、-45、0、45、90。若设置[0, 0],所有词横排。中文词云更适合把rotationStep设成 90,只有横竖两个方向,读起来更整齐;英文词云可以保留 45。rotationStep越小,候选越多,布局器需要多尝试几种角度,计算量也会上来。
textStyle.fontFamily不要在 CSS 里给 canvas 设置,canvas 的字体渲染不继承页面字体,必须直接传给词云图。textStyle.fontWeight全局加粗即可,如果想突出高权重词,可以在data里给对应词条设置更细粒度的样式。color支持函数,这是词云图最常见的玩法,随机取色时控制一下饱和度和亮度,避免和背景混在一起。
textStyle: { fontFamily: 'Microsoft YaHei', fontWeight: 'bold', color: () => { const hue = Math.round(Math.random() * 360); return `hsl(${hue}, 60%, 45%)`; } }上面的color函数每次渲染都会执行,返回一个基于色相随机生成的颜色。hsl比rgb更容易控制饱和度,用60%以上的饱和度能保证词条在白色背景上有足够的对比度。fontFamily写成'Microsoft YaHei'适配常见 Windows 系统,Mac 上会被 fallback 到系统中文字体,不会乱码。
3.3 通用配置联动:tooltip、emphasis、animation
wordCloud 系列也支持 echarts 的通用组件。tooltip可以显示词频,emphasis控制鼠标悬浮高亮。不过它没有坐标系轴,像dataZoom、legend对词云图没有意义。animation默认开启,建议保留,否则数据量大时切换数据会有明显的跳变感。
emphasis.textStyle里可以设置textShadowColor和textShadowBlur,比单纯改颜色更容易看出焦点。如果希望点击某个词后固定高亮,需要手动重设数据,因为dispatchAction的 highlight 在 wordCloud 上的稳定性不如柱状图。
emphasis: { textStyle: { color: '#ff6600', textShadowColor: 'rgba(255, 102, 0, 0.4)', textShadowBlur: 8 } }, animation: true, animationDuration: 600最后提醒一个和grid相关的坑:wordCloud 是独立系列,不占grid,也不响应xAxis/yAxis。如果你把 series 塞进一个配置了坐标系轴的图表里,坐标轴会被画出来但词云图照样不理会。所以词云图页面里通常不配置 xAxis/yAxis,直接给 series 指定绘图区域即可。
4. echarts 词云图实战调整:数据清洗、性能与点击事件
4.1 先做文本清洗,再做词频统计
后端给的数据往往不是干净的name/value列表,而是一堆日志或评论文本。这时候需要一个把文本切成词的函数。下面这个函数用正则实现简单切词:中文按连续汉字切,英文按连续字母数字切,再过滤单字、停用词和低频词。
function buildWordData(texts, topN = 80) { const stopWords = new Set(['的', '了', '和', '是', '在', '我', '有']); const counter = new Map(); texts.forEach(text => { const tokens = text.match(/[\u4e00-\u9fa5]+|[a-zA-Z0-9_]+/g) || []; tokens.forEach(token => { const word = token.toLowerCase(); if (word.length < 2 || stopWords.has(word)) return; counter.set(word, (counter.get(word) || 0) + 1); }); }); const list = Array.from(counter.entries()) .map(([name, value]) => ({ name, value })) .sort((a, b) => b.value - a.value) .slice(0, topN); const max = list[0]?.value || 1; return list.map(item => ({ name: item.name, value: Math.max(1, Math.round((item.value / max) * 100)) })); }这个函数有三个可调点:stopWords集合会影响高频功能词是否出现;topN限制词条数量,避免布局器处理几百上千个词;最后的归一化把最大词频映射到 100。归一化很有必要,比如长尾词 value 是 1,最大是 5000,不归一化的话sizeRange里最小字号几乎看不见。调参时可以先用归一化后的数据跑,再回来改 sizeRange,能少很多试错。
如果想做到真正的语义分词,常见做法是接后端词法分析,前端只消费词频。浏览器自带的Intl.Segmenter在中文里能按词道理分词,但兼容性还在爬坡,生产环境慎用。
提示:如果
params.value在 tooltip 里显示 undefined,说明传给 series 的 data 缺了 value 字段。词云图的 value 不能像某些图表一样省略,否则字号权重和 tooltip 都会失效。
4.2 大数据量下的布局性能与 maskImage
词云图的瓶颈在碰撞检测。每个词都要和已放置的词做矩形相交判断,复杂度接近 O(n²),词条一多,浏览器主线程就会长时间占用。通常用三个手段控制它:第一是topN截断,页面只展示最重要的 50~100 个词;第二是调大gridSize,比如从 6 改到 10;第三是固定容器大小,避免频繁触发 resize 重排。
| 词条数量 | 推荐调整 |
|---|---|
| 词数 > 300 | 截断到 80,gridSize 调大到 10 |
| 词数 100~300 | sizeRange 拉大,rotationStep 设 90 |
| 词数 < 50 | gridSize 调小到 4,让布局更密 |
maskImage是 echarts-wordcloud 提供的图形遮罩配置,常见做法是准备一张和容器同比例的 PNG,把词限制在图片轮廓内。使用它会让布局计算更慢,因为每个候选位置都要判断图片 alpha 像素。图片不能带白色背景,必须把轮廓区域保留为不透明、其余区域透明,否则遮罩会变成整张矩形。传入的 maskImage 必须是已经加载完成的 HTMLImageElement:
const img = new Image(); img.onload = () => { chart.setOption({ series: [{ maskImage: img }] }); }; img.src = './cloud-mask.png';注意img.src的路径如果跨域,canvas 会被污染,后续chart.getDataURL()导出图片时会失败。压缩到 1000px 内再使用,因为 mask 越大,布局器需要判断的像素点越多,主线程阻塞时间成倍增加。
4.3 点击事件与高亮交互
词云图只有在交互上给出反馈,才是一个可交付的模块。最基础的交互是点击词条后跳转或筛选。在 setOption 之后注册 click 事件即可。词云图的params.seriesType是wordCloud,数据格式和饼图类似,直接用params.name取点击的词。
chart.on('click', (params) => { if (params.seriesType !== 'wordCloud') return; const seriesData = option.series[0].data; const nextData = seriesData.map((word) => ({ ...word, itemStyle: word.name === params.name ? { color: '#ff6600' } : { color: '#d9d9d9' } })); chart.setOption({ series: [{ data: nextData }] }); });这里把所有词重设一遍颜色,点击的词变橙色,其余变灰。虽然会触发整个 series 更新,但权重没变,词的位置基本会维持原位,体感是可接受的。如果只想做 hover 高亮,优先使用emphasis,不要给每个词绑定 mouseover,词云图词条多,事件监听过多会加重浏览器负担。
在 echarts 数据可视化项目里,词云图经常和饼图、地图放在同一个页面做多视图联动,面试题也常问三者交互差异。词云图的点击事件更适合做下钻条件,比如点击“前端”就把表格和详情列表筛选成前端相关内容。选中状态不要依赖 dispatchAction,用修改 data 的方式更可控。
5. 词云图交付验证:用 finished 事件和 getDataURL 导出图片
5.1 通过 finished 事件拿到渲染结果
词云图第一次渲染是异步完成的。直接在setOption后面同步调用getDataURL,有时会拿到空白图。正确做法是监听finished事件,等动画和布局流程全部结束后再导出。
let exported = false; chart.on('finished', () => { if (exported) return; exported = true; const url = chart.getDataURL({ type: 'png', pixelRatio: 2, backgroundColor: '#ffffff' }); document.getElementById('preview').src = url; });pixelRatio设为 2 可以让导出图片在 2 倍屏上不模糊。backgroundColor填#ffffff,否则透明背景在部分浏览器预览里看起来像黑图。用exported标志位避免后续每次重绘都重新导出,因为词云交互触发 setOption 后,finished事件还会再次触发。
5.2 用两套 sizeRange 做方案评审
交付前经常要在横排和带旋转的两套方案里选。常见做法是用chart.setOption覆盖系列参数并开启notMerge: true,让图表完全重新走一遍布局。
chart.setOption({ series: [{ type: 'wordCloud', rotationRange: [0, 0], rotationStep: 0, sizeRange: [20, 80], gridSize: 6, data: words }] }, { notMerge: true });注意覆盖时type: 'wordCloud'和data都不能丢,notMerge: true会丢弃上一次 option 里所有系列配置。生产环境不要用notMerge做高频更新,它会让组件状态全部重建,开销比正常 setOption 大不少。
最后一个小技巧:如果某个词在图上一直消失,先不要怀疑 sizeRange,把gridSize临时调大到 20 再跑一遍。调大网格后单个词占的格子变少,长尾词更容易被放到空隙里。同一个 data 在 gridSize 改大后仍然消失,说明是数据侧需要过滤或提升权重;如果改大后出现了,说明画布太挤,再加最小字号或减少词条数量即可。
本文还有配套的精品资源,点击获取