echarts柱状图实现斜切以及label展示

0 阅读3分钟

思路

话不多说,ui图如下

image.png

拿到这个ui图的时候就知道不简单了,柱状图简单,斜切功能又怎么实现了,于是查找资料

image.png

发现这个功能很像(来源:横向柱图斜切 - ECharts图表集,echarts gallery社区,Make A Pie,分享你的可视化作品isqqw.com)

尝试发现当值为负数的时候样式爆炸,效果如图

image.png

这个方案只能放弃,于是决定用自定义 custom实现了 思路很简单,就是画出柱状图和label,负数的柱状图我放在右边是因为数据太大会和y轴的值重合了,着急下班,就贴出最终代码了

image.png

最终代码

  return {
    tooltip: {
      show: true,
      backgroundColor: '#012C68',
      borderColor: '#012C68',
      extraCssText: 'box-shadow: 0 0 10px 0 rgba(0, 119, 255, 0.50) inset', // 添加阴影以突出显示
      axisPointer: {
        type: 'shadow',
      },
      textStyle: {
        color: '#fff', // 设置文字颜色为深色,以便在白色背景下清晰可见
      },
      className: 'my-custom-tooltip',
      formatter: function (params) {
        // params 是提示框触发时的数据信息
        if (params.data) {
          const type = params.data.type; // 节点名称
          const unit = params.data.unit || '';
          const name = params.name; // 节点名称
          const value = params.data.value || ''; // 值(你传的是 column4)
          return `
                  <div  style="border-bottom: 1px solid #ccc; padding-bottom: 5px; margin-bottom: 5px;">
                  <strong style="
                  max-width: 150px;
                  display: inline-block;
                  white-space: normal;        /* 允许换行(关键!) */
                  word-wrap: break-word;      /* 长单词/连续字符可断行 */
                  overflow-wrap: break-word;  /* 现代标准写法 */
                ">${name}</strong>
                  </div>
                  ${type}${value}${unit}
                `.replace(/\n/g, '');
        }
        return '';
      },
    },
    xAxis: {
      type: 'value',
      axisLabel: {
        color: '#fff',
        fontSize: 12,
        formatter: function (value) {
          return value;
        },
      },
      splitLine: {
        show: true,
        lineStyle: {
          color: '#fff',
          type: 'dashed', // 虚线
          opacity: 0.2,
        },
      },
      axisLine: {
        show: false,
      },
      axisTick: {
        show: false,
      },
    },
    yAxis: {
      type: 'category',
      data: data.map((v) => `${v.index} ${v.orgName}`),
      axisLabel: {
        color: '#fff',
        fontSize: 14,
        width: 120,
        // 当内容超出宽度时使用省略号
        overflow: 'truncate',
        ellipsis: '...',
        align: 'left',
        margin: 120,
        formatter: (value) => `{arrow|} ${value}`,
        rich: {
          arrow: {
            backgroundColor: {
              image: arrow,
            },
            backgroundRepeat: 'no-repeat',
            // 关键:通过 padding 控制垂直位置
          },
        },
      },
      axisLine: {
        show: false,
      },
      axisTick: {
        show: false,
      },
    },
    grid: {
      top: 20, // 上边距
      left: -110, // 左边距
      right: 30, // 右边距
      bottom: 20, // 下边距
      containLabel: true, // 如果为true,则确保网格包含坐标轴标签
    },
    series: [
      {
        data: data.map((v) => {
          return { ...v };
        }),
        type: 'custom',
        renderItem: function (params, api) {
          const value = api.value(0);
          const categoryIndex = api.value(1);

          // 获取坐标(绝对像素)
          const zeroPoint = api.coord([0, categoryIndex]);
          const valuePoint = api.coord([value, categoryIndex]);

          const barHeight = api.size([0, 1])[1] * 0.3; // 更细
          const y = zeroPoint[1] - barHeight / 2;
          const cut = 8;
          const x0 = zeroPoint[0];
          const x1 = valuePoint[0];
          const width = Math.abs(x1 - x0); // 使用绝对值计算宽度

          let points, labelX, labelY, gradient, bar, labelText, group;

          if (value > 0 && width > cut) {
            // 正值且足够宽做斜切
            points = [
              [x0, y],
              [x1, y],
              [x1 - cut, y + barHeight],
              [x0, y + barHeight],
            ];

            gradient = new echarts.graphic.LinearGradient(0, 0, 1, 0, [
              { offset: 0, color: '#016AFF' },
              { offset: 1, color: '#4AE5FF' },
            ]);

            labelX = x1 - cut + 20;
            labelY = y + barHeight / 2;
          } else if (value < 0 && width > cut) {
            points = [
              [x0, y],
              [x1 + cut, y],
              [x1, y + barHeight],
              [x0, y + barHeight],
            ];

            gradient = new echarts.graphic.LinearGradient(0, 0, 1, 0, [
              { offset: 0, color: '#016AFF' },
              { offset: 1, color: '#4AE5FF' },
            ]);
            // 因为y轴设置为
            // labelX = x1 - cut - 10;
            labelX = x0 + 20;
            labelY = y + barHeight / 2;
          } else {
            points = [
              [Math.min(x0, x1), y],
              [Math.max(x0, x1), y],
              [Math.max(x0, x1), y + barHeight],
              [Math.min(x0, x1), y + barHeight],
            ];
            gradient = new echarts.graphic.LinearGradient(0, 0, 1, 0, [
              { offset: 0, color: '#016AFF' },
              { offset: 1, color: '#4AE5FF' },
            ]);
            labelX = value >= 0 ? x1 + 20 : x1 - 20;
            labelY = y + barHeight / 2;
          }

          bar = {
            type: 'polygon',
            shape: { points: points },
            style: { fill: gradient },
            key: params.dataIndex,
            info: { value: value },
          };

          labelText = {
            type: 'text',
            style: {
              text: value.toString(),
              textAlign: 'center',
              textVerticalAlign: 'middle',
              fill: '#FFFFFF', // 白色文本
              fontSize: 14,
            },
            position: [labelX, labelY],
          };

          group = {
            type: 'group',
            children: [bar, labelText],
          };

          return group; // 返回组对象
        },
      },
    ],
  };