Echarts 柱状图实现同时表现百分比+原始值+汇总值

打印 上一主题 下一主题

主题 1011|帖子 1011|积分 3033

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
原始结果:柱状图

二开结果1:

核心逻辑
   同时表现百分比和原始值
  1.  label: {
  2.       show: true,
  3.       position: 'inside',
  4.       formatter: (params) => {
  5.         const rawValue = rawData[params.seriesIndex][params.dataIndex];
  6.         const percentage = Math.round(params.value * 1000) / 10;
  7.         return `${rawValue} \n(${percentage}%)`;
  8.       }
  9.     },
复制代码
   表现汇总值
  1. // Add a new series for displaying total values
  2. series.push({
  3.     name: 'Total',
  4.     type: 'bar',
  5.     stack: 'total',
  6.     itemStyle: {
  7.         color: 'rgba(0,0,0,0)' // 透明颜色
  8.     },
  9.     label: {
  10.         show: true,
  11.         position: 'top',
  12.         formatter: params => `Total: ${totalData[params.dataIndex]}`
  13.     },
  14.     data: totalData.map(value => 0.01) // 微小的值以便能显示标签但不影响图形
  15. });
复制代码

代码表明
   

  • 新增表现总值的系列

    • 您添加了一个名为 'Total' 的新系列到 series 数组中。
    • 这个系列使用 type: 'bar',并且堆叠在名为 'total' 的堆栈中,这与其他系列使用的堆栈名称一致。这确保了柱状图的对齐,纵然该系列是不可见的。

  • 透明的柱状图

    • itemStyle 被设置为 color: 'rgba(0,0,0,0)',使得该系列的柱状图完全透明。这是一个巧妙的方法,可以确保这些柱状图不增长任何可见的元素到图表中,但仍然可以在它们上面放置标签。

  • 标签配置

    • label 对象中的 show: true 确保表现标签。
    • position 设置为 'top',因此标签表现在每个柱状图堆栈的顶部。
    • formatter 函数自定义了标签的文本。它使用 params.dataIndex 获取 totalData 中对应的值,并表现为 Total: {value}。这提供了关于每个类别(星期几)中所有堆叠元素的总值的清楚信息。

  • 带有微小值的数据

    • 该系列的 data 数组被设置为 totalData.map(value => 0.01)。这将每个数据点设置为一个非常小的值(0.01)。这些微小的值的目的是为标签创建一个占位符,而不影响图表的实际可视化。由于柱状图本身是透明的,这个值确保了标签可以正确地定位和表现,而不会为柱状图增长任何视觉重量。

  分析:

  

  • 使用透明的柱状图来表现标签:通过使用透明的柱状图,您可以在柱状图堆栈的顶部放置标签,而不会改变图表的视觉外观。这是一种常见的技术,当您盼望添加额外的信息而不影响数据的可视化时。
  • 数据中的微小值:使用微小值(0.01)确保标签与柱状图相关联,但不会显著地影响堆叠柱状图的高度。这在ECharts中尤其有用,由于标签是与特定的数据点相关联的。
  • 堆叠配置:使用相同的堆叠标识符('total')使透明柱状图与其余堆叠柱状图完美对齐,确保标签位置的一致性。
  这种方法对于突出表现总值,同时保持数据可视化的完整性非常有用。这是一个为图表提供额外信息而不使其变得杂乱或扭曲的巧妙解决方案。
  完整版代码
  1. // There should not be negative values in rawDataconst rawData = [  [100, 302, 301, 334, 390, 330, 320],  [320, 132, 101, 134, 90, 230, 210],  [220, 182, 191, 234, 290, 330, 310],  [150, 212, 201, 154, 190, 330, 410],  [820, 832, 901, 934, 1290, 1330, 1320]];const totalData = [];for (let i = 0; i < rawData[0].length; ++i) {  let sum = 0;  for (let j = 0; j < rawData.length; ++j) {    sum += rawData[j][i];  }  totalData.push(sum);}const grid = {  left: 100,  right: 100,  top: 50,  bottom: 50};const series = [  'Direct',  'Mail Ad',  'Affiliate Ad',  'Video Ad',  'Search Engine'].map((name, sid) => {  return {    name,    type: 'bar',    stack: 'total',    barWidth: '60%',    label: {      show: true,      position: 'inside',      formatter: (params) => {        const rawValue = rawData[params.seriesIndex][params.dataIndex];        const percentage = Math.round(params.value * 1000) / 10;        return `${rawValue} \n(${percentage}%)`;      }    },    data: rawData[sid].map((d, did) =>      totalData[did] <= 0 ? 0 : d / totalData[did]    )  };});// Add a new series for displaying total values
  2. series.push({
  3.     name: 'Total',
  4.     type: 'bar',
  5.     stack: 'total',
  6.     itemStyle: {
  7.         color: 'rgba(0,0,0,0)' // 透明颜色
  8.     },
  9.     label: {
  10.         show: true,
  11.         position: 'top',
  12.         formatter: params => `Total: ${totalData[params.dataIndex]}`
  13.     },
  14.     data: totalData.map(value => 0.01) // 微小的值以便能显示标签但不影响图形
  15. });option = {  legend: {    selectedMode: false  },  grid,  yAxis: {    type: 'value'  },  xAxis: {    type: 'category',    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']  },  series};
复制代码
二开结果2:

 完整版代码
  1. // There should not be negative values in rawData
  2. const rawData = [
  3.   [100, 302, 301, 334, 390, 330, 320],
  4.   [320, 132, 101, 134, 90, 230, 210],
  5.   [220, 182, 191, 234, 290, 330, 310],
  6.   [150, 212, 201, 154, 190, 330, 410],
  7.   [820, 832, 901, 934, 1290, 1330, 1320]
  8. ];
  9. const totalData = [];
  10. for (let i = 0; i < rawData[0].length; ++i) {
  11.   let sum = 0;
  12.   for (let j = 0; j < rawData.length; ++j) {
  13.     sum += rawData[j][i];
  14.   }
  15.   totalData.push(sum);
  16. }
  17. const grid = {
  18.   left: 100,
  19.   right: 100,
  20.   top: 50,
  21.   bottom: 50
  22. };
  23. const series = [
  24.   'Direct',
  25.   'Mail Ad',
  26.   'Affiliate Ad',
  27.   'Video Ad',
  28.   'Search Engine'
  29. ].map((name, sid) => {
  30.   return {
  31.     name,
  32.     type: 'bar',
  33.     stack: 'total',
  34.     barWidth: '60%',
  35.     label: {
  36.       show: true,
  37.       position: 'inside', // Position the labels on top of the bars
  38.       formatter: (params) => {
  39.         const originalValue = rawData[sid][params.dataIndex];
  40.         const percentage = (originalValue / totalData[params.dataIndex] * 100).toFixed(2);
  41.         return `${originalValue} \n(${percentage}%)`;
  42.       },
  43.     },
  44.     data: rawData[sid].map((d, did) =>
  45.       totalData[did] <= 0 ? 0 : d / totalData[did]
  46.     )
  47.   };
  48. });
  49. option = {
  50.   tooltip: {
  51.     trigger: 'axis',
  52.     axisPointer: {
  53.       type: 'shadow'
  54.     },
  55.     formatter: (params) => {
  56.       const total = totalData[params[0].dataIndex];
  57.       const header = `<div style="font-weight:bold">${params[0].axisValue}</div>
  58.                       <div>Total: ${total}</div>`;
  59.       const body = params.map(param => {
  60.         const originalValue = rawData[param.seriesIndex][param.dataIndex];
  61.         const percentage = (originalValue / total * 100).toFixed(2);
  62.         return `<div>${param.seriesName}: ${originalValue} (${percentage}%)</div>`;
  63.       }).join('');
  64.       return header + body;
  65.     }
  66.   },
  67.   legend: {
  68.     selectedMode: false
  69.   },
  70.   grid,
  71.   yAxis: {
  72.     type: 'value'
  73.   },
  74.   xAxis: {
  75.     type: 'category',
  76.     data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  77.   },
  78.   series
  79. };
复制代码
实现思路与修改:

   

  • 计算每天的总访问数:起首遍历 rawData 并计算每一天所有泉源的总访问数。这些总数被存储在 totalData 数组中。
  • 配置每个数据源系列:为每一个数据源创建一个 series 对象。每个系列代表一种访问泉源,并包罗一些配置选项,如范例、堆叠设置、标签表现方式等。
  • 配置标签表现:为了让用户在图表上直观地看到原始值和占比,我们必要在每个柱形上添加标签。标签的内容包罗原始值和百分比。
  • 配置提示框(Tooltip):为了提供更丰富的信息,我们配置了一个提示框,当用户悬停在柱形上时会表现当天的总访问数和各个泉源的具体数值及占比。
   二开结果3:

 完整版代码
  1. // There should not be negative values in rawData
  2. const rawData = [
  3.   [100, 302, 301, 334, 390, 330, 320],
  4.   [320, 132, 101, 134, 90, 230, 210],
  5.   [220, 182, 191, 234, 290, 330, 310],
  6.   [150, 212, 201, 154, 190, 330, 410],
  7.   [820, 832, 901, 934, 1290, 1330, 1320]
  8. ];
  9. const totalData = [];
  10. for (let i = 0; i < rawData[0].length; ++i) {
  11.   let sum = 0;
  12.   for (let j = 0; j < rawData.length; ++j) {
  13.     sum += rawData[j][i];
  14.   }
  15.   totalData.push(sum);
  16. }
  17. const grid = {
  18.   left: 100,
  19.   right: 100,
  20.   top: 50,
  21.   bottom: 50
  22. };
  23. const series = [
  24.   'Direct',
  25.   'Mail Ad',
  26.   'Affiliate Ad',
  27.   'Video Ad',
  28.   'Search Engine'
  29. ].map((name, sid) => {
  30.   return {
  31.     name,
  32.     type: 'bar',
  33.     stack: 'total',
  34.     barWidth: '60%',
  35.     label: {
  36.       show: true,
  37.       position: 'inside', // Position the labels on top of the bars
  38.       formatter: (params) => {
  39.         const originalValue = rawData[sid][params.dataIndex];
  40.         const percentage = (originalValue / totalData[params.dataIndex] * 100).toFixed(2);
  41.         return `${originalValue} (${percentage}%)`;
  42.       },
  43.     },
  44.     itemStyle: {
  45.       emphasis: {
  46.         // focus : 'series',
  47.         label: {
  48.           show: true,
  49.           position: 'top',
  50.           fontSize: 12,
  51.           color: 'red',
  52.           formatter: (params) => totalData[params.dataIndex]
  53.         }
  54.       }
  55.     },
  56.     data: rawData[sid].map((d, did) =>
  57.       totalData[did] <= 0 ? 0 : d / totalData[did]
  58.     )
  59.   };
  60. });
  61. option = {
  62.   legend: {
  63.     selectedMode: false
  64.   },
  65.   grid,
  66.   yAxis: {
  67.     type: 'value'
  68.   },
  69.   xAxis: {
  70.     type: 'category',
  71.     data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  72.   },
  73.   series
  74. };
复制代码
表明:

   

  • 添加了 itemStyle 选项,此中包罗了 emphasis 子项。
  • 在 emphasis 中设置了 label,用于在鼠标悬停时表现总值。
  • emphasis.label.show 设为 true,表现在鼠标悬停时表现标签。
  • emphasis.label.position 设为 'bottom',使标签表现在柱子底部。
  • emphasis.label.fontSize 设为 12,调解字体巨细。
  • emphasis.label.formatter 使用 totalData[params.dataIndex] 表现当前柱子对应的总值
   柱状图转换为条形图

 核心代码修改,变更xAxis,yAxis 中的 x y 即可
 

 条形图同时展示百分比、原始值、汇总值功能
  1. // There should not be negative values in rawData
  2. const rawData = [
  3.   [100, 302, 301, 334, 390, 330, 320],
  4.   [320, 132, 101, 134, 90, 230, 210],
  5.   [220, 182, 191, 234, 290, 330, 310],
  6.   [150, 212, 201, 154, 190, 330, 410],
  7.   [820, 832, 901, 934, 1290, 1330, 1320]
  8. ];
  9. const totalData = [];
  10. for (let i = 0; i < rawData[0].length; ++i) {
  11.   let sum = 0;
  12.   for (let j = 0; j < rawData.length; ++j) {
  13.     sum += rawData[j][i];
  14.   }
  15.   totalData.push(sum);
  16. }
  17. const grid = {
  18.   left: 100,
  19.   right: 100,
  20.   top: 50,
  21.   bottom: 50
  22. };
  23. const series = [
  24.   'Direct',
  25.   'Mail Ad',
  26.   'Affiliate Ad',
  27.   'Video Ad',
  28.   'Search Engine'
  29. ].map((name, sid) => {
  30.   return {
  31.     name,
  32.     type: 'bar',
  33.     stack: 'total',
  34.     barWidth: '60%',
  35.     label: {
  36.       show: true,
  37.        position: 'inside',
  38.       formatter: (params) => {
  39.         const rawValue = rawData[params.seriesIndex][params.dataIndex];
  40.         const percentage = Math.round(params.value * 1000) / 10;
  41.         return `${rawValue} \n(${percentage}%)`;
  42.       }
  43.     },
  44.     data: rawData[sid].map((d, did) =>
  45.       totalData[did] <= 0 ? 0 : d / totalData[did]
  46.     )
  47.   };
  48. });
  49. series.push({
  50.     name: 'Total',
  51.     type: 'bar',
  52.     stack: 'total',
  53.     itemStyle: {
  54.         color: 'red' // 透明颜色
  55.     },
  56.     label: {
  57.         show: true,
  58.         // position: 'middle',
  59.         formatter: params => `Total: ${totalData[params.dataIndex]}`
  60.     },
  61.     data: totalData.map(value => 0.0) // 微小的值以便能显示标签但不影响图形
  62. });
  63. option = {
  64.   legend: {
  65.     selectedMode: false
  66.   },
  67.   grid,
  68.   xAxis: {
  69.     type: 'value'
  70.   },
  71.   yAxis: {
  72.     type: 'category',
  73.     data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  74.   },
  75.   series
  76. };
复制代码
 结果展示



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

宝塔山

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表