简介:一份面向前端开发者的 ECharts 词云图完整示例与配置解析资源,主要解决开发者在使用 echarts-wordcloud 插件时遇到的引入库、数据格式编排和样式参数调优问题,适合数据可视化初学者和需要快速落地词云图功能的前端工程师。示例从最简单的 demo 入手,展示了从准备 name/weight 结构的数据、初始化图表到 setOption 渲染的完整流程,并对 shape、sizeRange、rotationRange、grid、textStyle.color 等关键参数逐项说明,方便读者理解每个配置项的作用。压缩包共 6 个文件,大小 233KB,包括 3 个 JavaScript 脚本、1 个 HTML 示例页面、1 张效果预览图以及 1 份使用说明文档,文件结构清楚,可以直接打开运行并对照修改。目前已有 11883 人浏览学习,说明该示例具备较强的参考价值。通过这份资料,读者可以快速掌握词云图的实现思路,自由调整文字大小、旋转角度、配色和整体布局,为后续做更复杂的数据可视化页面打下基础。
1. 词云图不是 ECharts 开箱功能,核心版不带你得自己拼
ECharts 主库从 2.x 到 5.x 内置了柱状图、折线图、饼图、地图、热力图这些常规系列,但词云图始终没有进过核心包。你需要额外引入echarts-wordcloud.js这个插件才能让series[0].type = 'wordCloud'被识别。很多人在这一步倒过:从 CDN 拉了最新的echarts.min.js,又照着网上的旧教程塞了echarts-wordcloud.js,页面直接报Cannot read property 'getZr' of undefined。这不是你代码写错了,是主库版本和插件版本错位。下面我用一份完整的词云图 demo 包,把文件依赖、配置参数、数据预处理和常见坑全部过一遍,适合做数据可视化报表、舆情分析页面,或者面试前突击 ECharts 定制系列的人。
2. 拆包分析:从文件清单到加载顺序的依赖链
2.1 四个核心文件的职责划分
资源包里一共有index.html、js/echart3.js、js/jquery-1.9.1.min.js、js/echarts-wordcloud.js、使用说明.txt、示例图.jpg这几个文件。其中三个 JS 文件的分工必须清楚:echart3.js是 ECharts 3.x 的主库,负责画布渲染、坐标系、组件调度;echarts-wordcloud.js是在主库之上注册wordCloud系列类型的插件,它内部调用主库的echarts.extendSeriesModel、ChartView等扩展接口;jquery-1.9.1.min.js在本 demo 里实际上只是辅助 DOM 操作和 ajax 加载数据,词云图本身不依赖 jQuery。
提示:如果你的页面里已经有了别的 jQuery 版本,可以先把
jquery-1.9.1.min.js换成项目已有的版本,词云渲染不会感知 jQuery 的存在。真正不能乱动的是主库和 wordcloud 插件的配对关系。
2.2 为什么 jQuery 1.9.1 会出现在词云图的包里
这个 demo 是早年典型的写法:用 jQuery 的$.ajax拉取远程关键词数据,再塞进 ECharts 的setOption。1.9.1 是 2013 年左右的版本,在老项目里兼容性最稳,$.ajax、$.each这些接口到今天也没怎么变。你完全可以用原生fetch替代:
fetch('data.json') .then(function (res) { return res.json(); }) .then(function (data) { myChart.setOption({ series: [{ type: 'wordCloud', data: data }] }); });这段代码的逻辑是:先请求data.json拿到词频数组,再调用setOption做增量更新,词云图会自动重新布局,不需要手动清空旧数据。参数说明:.then链式处理异步结果,第一层把响应流转成 JSON,第二层拿到数组后直接注入图表;增量更新是 ECharts 的默认行为,只要data数组变了,布局就会重排,旧文字不会残留。
2.3 echarts 3 与 echarts 5 混合使用的典型报错
我见过最多的报错是Cannot read property 'getZr' of undefined,出现这个基本是主库 5.x 配合了为 3.x 编译的 wordcloud 插件。原因在于 ECharts 5 重构了扩展 API 的注册时机,旧插件在init阶段拿不到完整的实例上下文。处理方式有两种:
- 全部降到 3.x 或 4.x,用包内的
echart3.js或去 CDN 拉echarts@4.9.0; - 全部升到 5.x,去官方仓库拉取最新编译版的
echarts-wordcloud.js,不要混搭。
<!-- 方案一:主库走 3.x,与包内一致 --> <script src="js/echart3.js"></script> <script src="js/echarts-wordcloud.js"></script> <!-- 方案二:主库走 5.x,需要配套的新版插件 --> <script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.2.1/echarts.min.js"></script> <script src="js/echarts-wordcloud.js"></script>参数说明:src的顺序不能颠倒,主库必须先加载,插件后加载,否则插件注册系列类型时找不到echarts全局对象,直接抛ReferenceError。方案二里如果echarts-wordcloud.js还是旧版编译产物,依旧会报错,所以先确认插件代码头部是否引用了echarts的模块系统接口。
2.4 一个能直接打开的 index.html 骨架
参考包内index.html的结构,核心是这样一段:
<div id="wc" style="width: 800px; height: 600px;"></div> <script src="js/echart3.js"></script> <script src="js/jquery-1.9.1.min.js"></script> <script src="js/echarts-wordcloud.js"></script> <script> var myChart = echarts.init(document.getElementById('wc')); var data = [ { name: 'Vue', value: 100 }, { name: 'React', value: 80 }, { name: 'Angular', value: 60 }, { name: 'Svelte', value: 30 } ]; myChart.setOption({ series: [{ type: 'wordCloud', shape: 'circle', sizeRange: [12, 60], rotationRange: [-90, 90], data: data }] }); </script>这段代码里需要注意:echarts.init的容器必须有明确宽高,否则画布初始化为 0×0,图表整个不显示;data数组里用的是name和value字段,这是echarts-wordcloud的标准数据结构,有些老教程写weight或count,在标准插件里不会被识别。sizeRange决定字号区间,rotationRange决定旋转范围,这两个参数是词云视觉表现的关键,后面的章节会细说。
3. 配置参数逐项拆解:shape、sizeRange、旋转与颜色
3.1 shape 参数:内置形状与自定义形状函数
shape是 series 级别的一个配置项,用来限定整个词云布局的外轮廓。常见取值包括'circle'、'rect'、'diamond'、'triangle'、'pentagon'、'star'。源码实现上,shape会传入布局算法,每个单词在画布上放置时,会根据当前形状计算可落点的范围,而不是简单地把词排在一条直线上。
自定义形状是更进阶的玩法,传一个函数进去:
shape: function theta(theta) { var r = 10; return [r * Math.cos(theta), r * Math.sin(theta)]; }参数说明:theta是极坐标下的角度,返回值是[x, y]坐标对,插件会把角度转成一个几何边界,词只能落在这个边界内。这个函数适合做品牌 LOGO 形状的词云,比如把轮廓采点后形成极坐标方程。需要注意:函数返回的坐标值相对中心点偏移,尺寸过大时词可能落到画布外,配合drawOutOfBound: false可以裁掉越界部分。
3.2 sizeRange 决定视觉重心
sizeRange: [12, 60]表示最小字号 12 像素、最大字号 60 像素。插件内部会对数据的value做线性映射,权重最高的词拿到 60,权重最低的拿到 12,中间值按比例插值。如果你发现所有词一样大,先确认是不是value字段传成了字符串,比如value: '100',内存里比较大小没问题,但字号映射时会出偏差。
提示:
sizeRange的最大值不建议超过容器短边的一半。800×600 的容器里最大字号 300,会出现词互相重叠、布局算法反复碰撞直到性能下降,最终显示效果还不如 60。
字号映射是纯线性还是对数,插件没有给开关。数据方差特别大时,比如一个词权重 100000,其余全是个位数,线性映射会把小词全部压到最小字号,视觉上只剩一个词。常见做法是对value取对数或开根号后再入库:
var maxVal = Math.max.apply(null, rawData.map(function (d) { return d.value; })); var data = rawData.map(function (d) { return { name: d.name, value: Math.sqrt(d.value / maxVal) * 200 }; });逻辑说明:先算出原始权重最大值,每个值除以最大值后开根号,再乘 200 放大,这样大权重和小权重之间的差距被压缩,词云的高频词仍然突出,但小词不会小到看不见。参数说明:Math.sqrt是核心,它把 0-1 区间的比值做非线性放大;乘 200 只是给后续的sizeRange映射留足数值区间,具体数值取决于你想要的灵敏度。
3.3 rotationRange 与 rotationStep:旋转策略
rotationRange: [-90, 90]允许词在负 90 度到正 90 度之间旋转,rotationStep: 45表示每次旋转的步长是 45 度,也就是一个词可能的角度只有 -90、-45、0、45、90 这几个离散值。步长越小,角度选择越多,视觉效果越活泼,但布局计算量也越大。
不少教程里提到的textRotation、rotation这类写法在标准echarts-wordcloud插件里并不存在,属于自定义版本或误写。如果你的页面里配了textRotation: [0, 90, -90]但完全没生效,不用奇怪,把它换成rotationRange加rotationStep就好。
rotationRange: [-90, 90], rotationStep: 45,参数说明:rotationRange只定义允许的旋转角度区间,rotationStep定义步长,两者配合使用。注意rotationStep必须能被rotationRange的区间长度整除,比如 180 除以 45 是 4,刚好整数。如果你写rotationStep: 30,180 除以 30 也是整数,角度集合变成 -90、-60、-30、0、30、60、90,也是可以的。想全部横排,把rotationRange设为[0, 0]即可。
3.4 textStyle 的 color 支持函数与随机色
词云图的文字颜色有两个入口。一个是全局textStyle,作用于所有词;一个是data[i].textStyle,单独控制某一个词的颜色。标准插件里color支持字符串、对象和函数,常见的动态配色方案:
textStyle: { color: function () { return 'rgb(' + [ Math.round(Math.random() * 160 + 60), Math.round(Math.random() * 160 + 60), Math.round(Math.random() * 160 + 60) ].join(',') + ')'; } }逻辑说明:每次给一个词上色时,函数被调用一次,随机产生 RGB 三个通道在 60 到 220 之间的值。下限 60 是为了避免太暗的颜色在白色背景上看不清,上限 220 是为了避免纯白和背景混在一起。参数说明:如果想让颜色有主题倾向,比如都偏蓝,可以固定 B 通道为 200,只随机 R 和 G;如果按权重渐变,可以在函数里读到当前的params.name或params.value,再返回对应颜色。
3.5 一张速查表收拢全部常用参数
| 参数 | 类型 | 默认值 | 作用 | 注意点 |
|---|---|---|---|---|
type | string | 无 | 固定'wordCloud' | 缺了插件直接空白不报错 |
shape | string/function | 'circle' | 词云外轮廓形状 | 函数模式返回极坐标点 |
sizeRange | array | [12, 60] | 字号最小最大值 | 最大值别超容器短边一半 |
rotationRange | array | [-90, 90] | 允许旋转的角度区间 | 单位是度,不是弧度 |
rotationStep | number | 45 | 旋转步长 | 需能被区间长度整除 |
gridSize | number | 8 | 布局网格像素单位 | 值越小密度越高越卡 |
drawOutOfBound | boolean | false | 是否绘制越界文字 | 设为 true 会看到词延伸到容器外 |
shrinkToFit | boolean | false | 超出边界时是否缩小字号 | 大数据量下建议开启 |
textStyle.color | string/function | '#333' | 文字颜色 | 函数模式每次调用返回一个色值 |
data | array | 无 | { name, value }数组 | value 必须是数字类型 |
gridSize是很多人忽略的参数。它控制词云布局时使用的网格粒度,网格越小,词之间缝隙越小,布局越紧凑,但碰撞检测的计算量按平方增长。1000 个词时gridSize: 4可能直接卡掉浏览器标签页,改成 8 或 12 会明显流畅,代价是词与词之间的空隙变大,视觉上稍微松散一些。
4. 数据预处理与动态更新:从原始文本到词频数组
4.1 中文文本的切词与停用词过滤
词云图本身不负责分词,它接收的是已经统计好的{ name, value }数组。如果你手头的原始数据是一段新闻文本或评论字符串,需要先做分词。英文按空格和标点切分即可,中文没有天然分隔符,常见做法是引入分词库,或者在前端用一个简单的最小切分策略。
var text = '前端开发者的竞争力在于工程效率和综合能力'; var words = text.match(/[\u4e00-\u9fa5]{2,4}/g) || []; var stopWords = ['在于', '可以', '一个', '我们']; var freq = {}; words.forEach(function (w) { if (stopWords.indexOf(w) > -1) return; freq[w] = (freq[w] || 0) + 1; }); var data = Object.keys(freq).map(function (name) { return { name: name, value: freq[name] }; });逻辑说明:正则[\u4e00-\u9fa5]{2,4}从文本里抽取连续 2 到 4 个汉字的片段,这种切分方式精度一般,但不需要额外库。停用词表stopWords里装的都是没有实际意义的高频词,命中后直接跳过。最终遍历freq对象,把每个词的计数转成{ name, value }结构。参数说明:正则里的 2 到 4 是把相邻汉字按滑窗切出候选词,实际项目中你会把这一步换成精确分词接口,但数据流向是一致的:无论分词怎么做,最终都要产出name和value两个字段的数组。
4.2 权重归一化与离群值处理
统计完的词频直接进sizeRange是可以的,但数据里如果出现一个爆炸性关键词,比如某个词出现 10000 次,别的词只有 1 到 5 次,线性映射会让后者的字号全部压到最小,视觉上变成只有一个大词加一堆小蚂蚁。处理方式在第 3 章提过开根号,更稳妥的是先做分位数裁剪,再开根号:
var values = data.map(function (d) { return d.value; }); values.sort(function (a, b) { return a - b; }); var p90 = values[Math.floor(values.length * 0.9)]; data.forEach(function (d) { d.value = Math.min(d.value, p90); d.value = Math.pow(d.value / p90, 0.7) * 100; });逻辑说明:先取所有权重的 90 分位值p90,把超过它的值全部截断,这相当于去掉长尾里的极值。第二步做归一化,每个值除以p90后取 0.7 次幂,最后乘 100。0.7 次幂介于开根号(0.5)和线性(1.0)之间,是一种可调的压缩强度,数字越小压缩越狠。参数说明:p90的具体位置可以根据数据分布改成p95,核心目的是让最高权重与中位权重之间的差距不超过一个数量级,否则字号映射必然失衡。
4.3 异步加载后端数据后 setOption 刷新
真实项目里词云的数据通常来自后端接口,可能是热门搜索词、标签统计数据、日志聚合结果。拿到数据后不要重新init,直接用setOption做增量更新,这样 ECharts 会复用之前的画布和布局状态,减少一次全量重建:
var myChart = echarts.init(document.getElementById('wc')); function loadWordCloud() { fetch('/api/hot-words') .then(function (res) { return res.json(); }) .then(function (json) { var data = json.data.map(function (item) { return { name: item.word, value: item.count }; }); myChart.setOption({ series: [{ type: 'wordCloud', data: data }] }); }) .catch(function () { console.log('加载失败,本次不上报'); }); } setInterval(loadWordCloud, 60000);逻辑说明:loadWordCloud把请求封装成函数,每次调用都会重新拉取接口并覆盖data。60 秒一次做定时刷新,适合舆情大屏或运营看板场景。参数说明:setOption里只传了data,之前设置过的shape、sizeRange会保留,这是 ECharts 的增量合并机制;如果你希望改成完全新的配置,传notMerge = true作为第二个参数,即myChart.setOption(config, true),会把旧配置清掉重新来。
提示:刷新频率超过每 10 秒一次时,建议在
setOption前调用myChart.clear(),避免布局状态叠加导致的内存持续上涨。低频刷新不需要。
5. 把 demo 收进生产环境:事件绑定、性能与排查
5.1 click 事件拿不到关键词?看 data 的对象结构
给词云加点击跳转是高频需求,但经常出现params.name是undefined的情况。原因是事件参数里的data字段需要与 series 里定义的字段一致。标准echarts-wordcloud插件里data的结构如果是{ name: 'Vue', value: 100 },那么在事件回调里拿到的就是params.name、params.value。但如果你的数据源用的是{ word: 'Vue', count: 100 },事件回调里params.name自然拿不到。
myChart.on('click', function (params) { if (params.componentType === 'series' && params.seriesType === 'wordCloud') { window.open('https://example.com/search?q=' + encodeURIComponent(params.name)); } });逻辑说明:on方法注册点击监听,回调里先判断componentType是不是series,再判断seriesType是不是wordCloud,避免误触发其他系列组件。参数说明:encodeURIComponent对关键词做 URL 编码,中文词不带编码直接拼进地址会产生乱码。
5.2 大数据量与重绘性能
词云布局是 CPU 密集型计算,核心瓶颈在碰撞检测。100 个词毫无压力,1000 个词开始有感知,5000 个词以上页面基本卡死。如果你需要展示的词超过 2000 个,我一般会先做 top-N 截断:
var topN = data.sort(function (a, b) { return b.value - a.value; }).slice(0, 500);参数说明:sort按value降序排,slice(0, 500)只保留前 500 个词。词云图的价值在于快速呈现主要特征,而不是显示全量低频词,截断掉长尾对信息损失非常小,但性能提升是数量级的。配合gridSize: 10和shrinkToFit: true,500 个词在任何主流设备上都能顺畅渲染。
另外,不要把setOption放在window.resize事件里直接触发。常见做法是节流:
var resizeTimer; window.addEventListener('resize', function () { clearTimeout(resizeTimer); resizeTimer = setTimeout(function () { myChart.resize(); }, 200); });逻辑说明:resize事件在拖拽窗口时高频触发,每次都调用myChart.resize()会诱发重绘。这里用clearTimeout加setTimeout做了 200 毫秒的防抖,拖拽停下来 200 毫秒后才真正执行一次调整。参数说明:200 毫秒是常规选择,如果图表较重可以调到 300 到 500 毫秒;setTimeout的返回值每次都覆盖resizeTimer,就是为了取消上一次未执行的定时任务。
5.3 常见报错排查表
| 现象 | 可能原因 | 对应解法 |
|---|---|---|
| 页面空白,控制台无报错 | 容器高度为 0 | 给div设置明确的px宽高 |
Cannot read property 'getZr' of undefined | 主库与 wordcloud 插件版本不匹配 | 统一降到 3.x,或升级新插件 |
| 词全部横排,没有旋转 | rotationRange写成[0, 0] | 改成[-90, 90]加rotationStep: 45 |
| 所有词字号一样大 | data里value是字符串 | 用Number()转成数字 |
| 词被截断到容器外 | drawOutOfBound为false且布局空间不足 | 调大gridSize,或减少词量 |
| 一个超大词压制全场 | 数据方差过大 | 90 分位裁剪后开根号归一化 |
| 中文显示为方块 | 字体栈缺少中文字体 | textStyle.fontFamily设置'Microsoft YaHei'或'sans-serif' |
textStyle.fontFamily是最低调但最常见的坑。ECharts 默认字体栈在部分 Linux 服务器上不包含中文字形,词云里所有中文渲染成方块。直接在textStyle里指定fontFamily: 'Microsoft YaHei, PingFang SC, sans-serif',服务端渲染或导出图片时也要保证系统安装了对应字体,否则导出图里中文依然是方块。这个参数建议在项目初始配置时就写好,不要等上线后再补。
本文还有配套的精品资源,点击获取