立体柱状图分为:
- 纯色立体柱状图
- 渐变立体柱状图
常用实现方式
纯色立体柱状图
纯色立体柱状图,使用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);