【大前端】ECharts 绘制立体柱状图

立体柱状图分为:

  1. 纯色立体柱状图
  2. 渐变立体柱状图

常用实现方式

纯色立体柱状图

纯色立体柱状图,使用MarkPoint和颜色渐变就实现,如下代码

在这里插入图片描述

import * as echarts from 'echarts';

var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom, 'dark');
var option;

const xAxisData = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
const seriesData = [120, 200, 150, 200, 70, 110, 130];

const gridTop = 200;

const maxBoxLeftWidth = 13,
  maxBoxRightWidth = 16;

const maxBoxWidth = maxBoxLeftWidth + maxBoxRightWidth;
// 外部边框菱形高度
const maxBoxRhomboidHeight = 8.6;

const innerBoxLeftWidth = 9,
  innerBoxRightWidth = 12;
const innerBoxWidth = innerBoxLeftWidth + innerBoxRightWidth;

// 内部边框菱形高度
const innerBoxRhomboidHeight = 6;

const markPointData = [];

// 内部盒大小
function computedInnerBoxSize(width, value) {
  const maxValue = myChart.convertFromPixel({ yAxisIndex: 0 }, gridTop);
  const valueOffsetY = myChart.convertToPixel({ yAxisIndex: 0 }, value);

  const zeroOffsetY = myChart.convertToPixel({ yAxisIndex: 0 }, 0);

  const diff = zeroOffsetY - valueOffsetY;

  return [
    width,
    value == maxValue
      ? diff - innerBoxRhomboidHeight * 1.5
      : diff - innerBoxRhomboidHeight / 2
  ];
}

// 内部盒偏移量
function computedInnerBoxOffet(width, value) {
  const maxValue = myChart.convertFromPixel({ yAxisIndex: 0 }, gridTop);
  const valueOffsetY = myChart.convertToPixel({ yAxisIndex: 0 }, value);
  const zeroOffsetY = myChart.convertToPixel({ yAxisIndex: 0 }, 0);
  const diff = zeroOffsetY - valueOffsetY;

  return [width / 2, value == maxValue ? diff / 2 : '50%'];
}

// 外部盒大小
function computedMaxBoxSize(width) {
  const maxValue = myChart.convertFromPixel({ yAxisIndex: 0 }, gridTop);
  const maxValueOffsetY = myChart.convertToPixel({ yAxisIndex: 0 }, maxValue);

  const zeroOffsetY = myChart.convertToPixel({ yAxisIndex: 0 }, 0);

  return [width, zeroOffsetY - maxValueOffsetY - maxBoxRhomboidHeight];
}

// 外部盒偏移量
function computedMaxBoxOffet(width, value) {
  const zeroOffsetY = myChart.convertToPixel({ yAxisIndex: 0 }, 0);

  const maxValue = myChart.convertFromPixel({ yAxisIndex: 0 }, gridTop);
  const maxValueOffsetY = myChart.convertToPixel({ yAxisIndex: 0 }, maxValue);

  const valueOffsetY = myChart.convertToPixel({ yAxisIndex: 0 }, value);

  const maxBoxHeight = zeroOffsetY - maxValueOffsetY;

  const barHeight = zeroOffsetY - valueOffsetY;

  return [width / 2, barHeight - maxBoxHeight / 2];
}

//计算底部菱形偏移量
function computedBottomRhomboidOffset(thomboidHeight, value) {
  const zeroOffsetY = myChart.convertToPixel({ yAxisIndex: 0 }, 0);
  const valueOffsetY = myChart.convertToPixel({ yAxisIndex: 0 }, value);

  const diff = zeroOffsetY - valueOffsetY;

  return [0, diff - thomboidHeight / 2];
}

seriesData.forEach((value, i) => {
  const xAxis = xAxisData[i],
    yAxis = value;

  // 添加外层左侧边框
  markPointData.push({
    xAxis,
    yAxis,
    symbol: 'rect',
    symbolSize(value, params) {
      return computedMaxBoxSize(maxBoxLeftWidth);
    },
    symbolOffset(a) {
      return computedMaxBoxOffet(-maxBoxLeftWidth, value);
    },
    itemStyle: {
      color: '#04C3C9',
      opacity: 0.4
    }
  });

  // 添加外层右侧边框
  markPointData.push({
    xAxis,
    yAxis,
    symbol: 'rect',
    symbolSize(value, params) {
      return computedMaxBoxSize(maxBoxRightWidth);
    },
    symbolOffset() {
      return computedMaxBoxOffet(maxBoxRightWidth, value);
    },
    itemStyle: {
      color: '#0DDEE5',
      opacity: 0.4
    }
  });

  // 外侧顶层
  markPointData.push({
    xAxis,
    y: gridTop + maxBoxRhomboidHeight / 2,
    symbol: 'path://M0,3.25L9,6.5L21,3.25L12,0L0,3.25Z',
    symbolSize: [maxBoxWidth, maxBoxRhomboidHeight],
    itemStyle: {
      color: '#24848E',
      opacity: 1
    }
  });

  // 外侧底层
  markPointData.push({
    xAxis,
    yAxis,
    symbol: 'path://M0,3.25L9,6.5L21,3.25L12,0L0,3.25Z',
    symbolSize: [maxBoxWidth, maxBoxRhomboidHeight],
    symbolOffset() {
      return computedBottomRhomboidOffset(maxBoxRhomboidHeight, value);
    },
    itemStyle: {
      color: '#24848E',
      opacity: 1
    }
  });

  // 添加内层左侧边框
  markPointData.push({
    xAxis,
    yAxis,
    symbol: 'rect',
    symbolSize() {
      return computedInnerBoxSize(innerBoxLeftWidth, value);
    },
    symbolOffset(a) {
      return computedInnerBoxOffet(-innerBoxLeftWidth, value);
    },
    itemStyle: {
      color: '#04C3C9',
      opacity: 1
    }
  });

  // 添加内层右侧边框
  markPointData.push({
    xAxis,
    yAxis,
    symbol: 'rect',
    symbolSize() {
      return computedInnerBoxSize(innerBoxRightWidth, value);
    },
    symbolOffset() {
      return computedInnerBoxOffet(innerBoxRightWidth, value);
    },
    itemStyle: {
      color: '#0DDEE5',
      opacity: 1
    }
  });

  // 内侧顶层
  markPointData.push({
    xAxis,
    yAxis,
    symbol: 'path://M0,3.25L9,6.5L21,3.25L12,0L0,3.25Z',
    symbolSize: [innerBoxWidth, innerBoxRhomboidHeight],
    symbolOffset() {
      const maxValue = myChart.convertFromPixel({ yAxisIndex: 0 }, gridTop);
      return [0, value == maxValue ? '100%' : 0];
    },
    itemStyle: {
      color: '#43F9FF',
      opacity: 1
    }
  });

  // 内侧底层
  markPointData.push({
    xAxis,
    yAxis,
    symbol: 'path://M0,3.25L9,6.5L21,3.25L12,0L0,3.25Z',
    symbolSize: [innerBoxWidth, innerBoxRhomboidHeight],
    symbolOffset() {
      return computedBottomRhomboidOffset(innerBoxRhomboidHeight, value);
    },
    itemStyle: {
      color: {
        type: 'linear',
        x: 0,
        y: 1,
        x1: 0,
        y1: 0,
        colorStops: [
          {
            offset: 0,
            color: '#04C3C9'
          },
          {
            offset: 0.5,
            color: '#04C3C9'
          },
          {
            offset: 0.5,
            color: '#0DDEE5'
          },
          {
            offset: 1,
            color: '#0DDEE5'
          }
        ],
        global: false // 缺省为 false
      },
      opacity: 1
    }
  });
});

option = {
  grid: {
    top: gridTop
  },
  xAxis: {
    type: 'category',
    data: xAxisData
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      data: seriesData,
      name: '数量',
      type: 'pictorialBar',
      yAxisIndex: 0,
      barWidth: maxBoxWidth,
      symbol: 'rect',
      itemStyle: {
        opacity: 0
      },
      markPoint: {
        data: markPointData,
        silent: true
      }
    }
  ]
};

option && myChart.setOption(option);


SVG

SVG适用于纯色立体柱状图和渐变立体柱状图

ECharts不能直接使用SVG字符串,须要转化为Base64格式的数据(可以使用网络SVG图片地址)

在这里插入图片描述

import * as echarts from "echarts";

var chartDom = document.getElementById("main");
var myChart = echarts.init(chartDom, "dark");
var option;

const xAxisData = ["A", "B", "C", "D", "E"];
const yAxisData = [10, 20, 30, 50, 60];

const markPointData = [];

const barWidth = 30;

const svgStr =
    '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="14" height="105" viewBox="0 0 14 105"><defs><linearGradient x1="0.5" y1="0" x2="0.5" y2="1" id="master_svg0_1_6290"><stop offset="0%" stop-color="#28A9A2" stop-opacity="1"/><stop offset="100%" stop-color="#28A9A2" stop-opacity="0"/></linearGradient><linearGradient x1="0.5" y1="0" x2="0.5" y2="1" id="master_svg1_1_6296"><stop offset="0%" stop-color="#35FFF4" stop-opacity="1"/><stop offset="100%" stop-color="#35FFF4" stop-opacity="0"/></linearGradient></defs><g><g><rect x="0" y="0" width="7" height="105" rx="0" fill="url(#master_svg0_1_6290)" fill-opacity="1"/></g><g><rect x="7" y="0" width="7" height="105" rx="0" fill="url(#master_svg1_1_6296)" fill-opacity="1"/></g></g></svg>';
// 转换为Base64格式
const svgDataURL = "data:image/svg+xml;base64," + btoa(svgStr);

yAxisData.forEach((v, i) => {

    //添加顶部渐变
    markPointData.push({
        xAxis: xAxisData[i],
        yAxis: v,
        symbol: "path://M 0 5 L 15 10 L 30 5 L 15 0 L 0 5 Z", //菱形图标
        symbolSize: [barWidth, barWidth * 0.5],
        itemStyle: {
            color: "rgba(126, 255, 248,1)"
        }
    });
});

option = {
    title: {
        text: "立体柱状图示例"
    },
    tooltip: {},
    xAxis: {
        data: xAxisData
    },
    yAxis: {},
    series: [
        {
            name: "销量",
            type: "pictorialBar",
            data: yAxisData,
            barWidth: barWidth,
            markPoint: {
                data: markPointData,
                silent: true
            },
            symbol:`image://${svgDataURL}`,
        }
    ]
};

option && myChart.setOption(option);

2. MarkPoint

MarkPoint 适用于多组图标组合展示

它和SVG不同的是绘制方式不一样,SVG是互联网通用绘制方式,MarkPoint是ECharts特有的绘制方式

在这里插入图片描述

import * as echarts from 'echarts';

var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom, 'dark');
var option;

const xAxisData = ['A', 'B', 'C', 'D', 'E'];
const yAxisData = [10, 20, 30, 50, 60];

const markPointData = [];

const barWidth = 30;

// 计算柱状图高度
function computedBarHeight(value, params) {
  // 左上角 到 Y轴 0 刻度的高度,如果x轴是数值轴,则例使用xAxisIndex标记
  const zeroHeight = myChart.convertToPixel({ yAxisIndex: 0 }, 0);
  // 左上角到Y轴目标数值的高度,如果x轴是数值轴,则例使用xAxisIndex标记
  const height = myChart.convertToPixel({ yAxisIndex: 0 }, params.data.yAxis);
  // 返回柱状图大小,高度等于0刻度距离-目标数值距离的差值
  return [barWidth / 2, zeroHeight - height];
}

yAxisData.forEach((v, i) => {
  //添加左侧渐变
  markPointData.push({
    xAxis: xAxisData[i],
    yAxis: v,
    symbol: 'rect',
    symbolSize: computedBarHeight,
    symbolOffset: ['-50%', '50%'],
    itemStyle: {
      color: {
        type: 'linear',
        x: 0,
        y: 0,
        x2: 0,
        y2: 1,
        colorStops: [
          {
            offset: 0,
            color: 'rgba(40, 169, 162,1)'
          },
          {
            offset: 1,
            color: 'rgba(40, 169, 162,0)'
          }
        ],
        global: false
      }
    }
  });

  //添加右侧渐变
  markPointData.push({
    xAxis: xAxisData[i],
    yAxis: v,
    symbol: 'rect',
    symbolSize: computedBarHeight,
    symbolOffset: ['50%', '50%'],
    itemStyle: {
      color: {
        type: 'linear',
        x: 0,
        y: 0,
        x2: 0,
        y2: 1,
        colorStops: [
          {
            offset: 0,
            color: 'rgba(53, 255, 244,1)'
          },
          {
            offset: 1,
            color: 'rgba(53, 255, 244,0)'
          }
        ],
        global: false
      }
    }
  });

  //添加顶部渐变
  markPointData.push({
    xAxis: xAxisData[i],
    yAxis: v,
    symbol: 'path://M 0 5 L 15 10 L 30 5 L 15 0 L 0 5 Z',//菱形图标
    symbolSize: [barWidth, barWidth * 0.5],
    itemStyle: {
      color: 'rgba(126, 255, 248,1)'
    }
  });
});

option = {
  title: {
    text: '立体柱状图示例'
  },
  tooltip: {},
  xAxis: {
    data: xAxisData
  },
  yAxis: {},
  series: [
    {
      name: '销量',
      type: 'bar',
      data: yAxisData,
      barWidth: barWidth,
      markPoint: {
        data: markPointData,
        silent: true
      },
      z: 3,
      itemStyle: {
        color: 'rgba(255, 53, 64,0)'
      }
    }
  ]
};

option && myChart.setOption(option);

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Space Chars

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值