如何基于动态数据使用Morris.js绘制柱状图?
动态适配Morris柱状图的解决方案
1. 先理清核心问题:数据格式不匹配
你遇到的额外Undefined标签问题,本质是因为你的原始数据结构和Morris Bar要求的格式不兼容。Morris需要的data是每个元素对应一个x轴标签,包含该标签下所有系列的数值;而你的输入是每个元素对应一个数据系列,这就导致Morris错误解析了数据,生成了多余的x轴条目。
2. 编写动态数据转换函数
先把你的原始数据转换成Morris能识别的格式。假设你的原始数据是:
const rawData = [ {"label":"Cats","values":[57,92,94]}, {"label":"Dogs","values":[85,78,53]}, {"label":"Birds","values":[81,57,70]} ];
我们写个函数把它转成Morris需要的结构:
function transformRawData(rawData) { // 先判断数据是否为空,避免报错 if (!rawData.length || !rawData[0].values.length) return []; // 生成x轴标签(如果后端有提供明确的x轴名称,直接替换这里即可) const xLabels = rawData[0].values.map((_, idx) => `Period ${idx + 1}`); // 初始化转换后的数组,每个元素对应一个x轴标签 const transformedData = xLabels.map(x => ({ x })); // 把每个系列的数值填充到对应的x轴条目里 rawData.forEach(series => { // 把系列标签转成合法的对象key(处理空格/特殊字符) const seriesKey = series.label.toLowerCase().replace(/\s+/g, '_'); series.values.forEach((value, idx) => { transformedData[idx][seriesKey] = value; }); }); return transformedData; }
3. 动态生成系列配置项
有了转换后的数据,我们可以直接从原始数据里提取ykeys和labels,不管系列数量是2个还是N个都能适配:
// 生成ykeys:对应转换后data里的系列字段名 const ykeys = rawData.map(series => series.label.toLowerCase().replace(/\s+/g, '_')); // 生成labels:图表上显示的系列名称 const labels = rawData.map(series => series.label);
4. 初始化动态图表
把这些动态生成的参数传入Morris.Bar,就能自动适配任意数量的系列了:
const transformedData = transformRawData(rawData); Morris.Bar({ element: 'bar-example', data: transformedData, xkey: 'x', // 对应转换后数据里的x轴字段 ykeys: ykeys, labels: labels, resize: true, // 可选:窗口大小变化时自动重绘 barColors: ['#0088cc', '#33bb55', '#ff6600'] // 可选:如果需要动态颜色,可以写个循环取色的逻辑 });
完整可运行示例
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css"> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script> <div id="bar-example" style="height: 250px;"></div> <script> // 模拟从控制器获取的原始数据 const rawData = [ {"label":"Cats","values":[57,92,94]}, {"label":"Dogs","values":[85,78,53]}, {"label":"Birds","values":[81,57,70]} ]; function transformRawData(rawData) { if (!rawData.length || !rawData[0].values.length) return []; // 这里可以替换成后端传递的真实x轴标签,比如年份、月份 const xLabels = rawData[0].values.map((_, idx) => `Period ${idx + 1}`); const transformedData = xLabels.map(x => ({ x })); rawData.forEach(series => { const seriesKey = series.label.toLowerCase().replace(/\s+/g, '_'); series.values.forEach((value, idx) => { transformedData[idx][seriesKey] = value; }); }); return transformedData; } const transformedData = transformRawData(rawData); const ykeys = rawData.map(series => series.label.toLowerCase().replace(/\s+/g, '_')); const labels = rawData.map(series => series.label); // 动态生成颜色(可选) const colorPool = ['#0088cc', '#33bb55', '#ff6600', '#9933cc', '#cc3333']; const barColors = ykeys.map((_, idx) => colorPool[idx % colorPool.length]); Morris.Bar({ element: 'bar-example', data: transformedData, xkey: 'x', ykeys: ykeys, labels: labels, barColors: barColors, resize: true }); </script>
额外优化提示
- 如果后端能提供明确的x轴标签(比如
["2021", "2022", "2023"]),直接替换xLabels的生成逻辑即可 - 对于带空格或特殊字符的系列标签,用
replace(/\s+/g, '_')转成合法的对象key,避免语法错误
内容的提问来源于stack exchange,提问作者diminuta
相关产品推荐
相关产品推荐

