echarts--3d柱状图
在工作中,常常需要用到一些echarts图表,普通的图表直接从echarts官网的示例中获取,本期来总结下自己所实现的3d柱状图
安装echarts
npm install echarts --save
一个基础的bar
我们根据echarts官网示例代码,实现一个普通的柱状图
<template>
<div class="bar" ref="myBar"></div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted } from "vue"
import * as echarts from "echarts"
type EChartsOption = echarts.EChartsOption
const myBar = ref<HTMLDivElement>()
onMounted(() => {
init()
})
const option = reactive<EChartsOption>({
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
yAxis: {
type: "value",
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: "bar",
},
],
})
function init() {
let myChart = echarts.init(myBar.value)
myChart.setOption(option)
}
</script>
<style scoped>
.bar {
width: 400px;
height: 300px;
}
</style>
效果如下:
3d bar分解
首先,我们要知道,echarts是基于canvas进行绘制的,canvas画布是从左上角作为起始点,往右下方进行绘制
canvas的坐标点也是从左上角往下
立体柱状图,我们需要绘制的是三个面,左、右、顶,这三个面我们需要计算出周边的
点位坐标,然后利用canvas进行绘制
-
左侧面
左侧面的坐标信息为: P1:【基础X轴坐标点-侧面宽度,顶部Y轴坐标点-斜角高度】 P2:【基础X轴坐标点-侧面宽度,底部Y轴坐标点】 P3:【基础X轴坐标点,底部Y轴坐标点】 P4:【基础X轴坐标点,顶部Y轴坐标点】
-
右侧面
右侧面的坐标信息为
P1:【基础X轴坐标点,顶部Y轴坐标点】
P2:【基础X轴坐标点,底部Y轴坐标点】
P3:【基础X轴坐标点 +侧面宽度 ,底部Y轴坐标点】
P4:【基础X轴坐标点 +侧面宽度,顶部Y轴坐标点 - 斜角高度】
-
顶部
顶面的坐标信息为
P1:【基础X轴坐标点,顶部Y轴坐标点】
P2:【基础X轴坐标点+侧面宽度,顶部Y轴坐标点- 斜角高度】
P3:【基础X轴坐标点 ,顶部Y轴坐标点- 斜角高度*2】
P4:【基础X轴坐标点 -侧面宽度,顶部Y轴坐标点 - 斜角高度】
3d bar创建
我们需要使用series里的renderItem来自定义面,主要api和作用看注释
option={
...//默认的属性
series: [
{
type: "custom",
data: [30, 50, 30, 30, 30],
barWidth: 30,
renderItem(params, api) {
//api.value(0)可以获取到x轴柱子对应的Index,value(1)获取到对应y轴的value值
//api.coord可以获取到某个点的坐标 需要传入一个数组:[x轴柱子对应的index,柱子value值]
// console.log("coord", api.coord([api.value(0), 0]))
const basicsCoord = api.coord([api.value(0), api.value(1)])
//basicCoord:获取x轴每根柱子顶端的坐标
// 顶部基础 y 轴距离, 注意:是从顶部空白处到头部的距离
const topBasicsYAxis = basicsCoord[1]
// console.log("topBasic", topBasicsYAxis)
// 基础 x 轴
const basicsXAxis = basicsCoord[0]
// 底部 y 轴 注意:y轴的距离都是从canvas左上角开始,到当前点位的距离
const bottomYAxis = api.coord([api.value(0), 0])[1]
console.log("bottomYAxis", bottomYAxis)
return {
type: "group",
children: [
{
type: "leftShape",
//type是自定义的面名,shape是传递的参数
shape: {
topBasicsYAxis,
basicsXAxis,
bottomYAxis,
},
style: {
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "rgb(0, 192, 238,0.8)",
},
{
offset: 0.8,
color: "rgb(0, 194, 239,0.2)",
},
{
offset: 1,
color: "rgb(0, 194, 239,0)",
},
]),
emphasis: {
fill: "yellow", // 鼠标高亮时的填充颜色
},
},
},
{
type: "rightShape",
shape: {
topBasicsYAxis,
basicsXAxis,
bottomYAxis,
},
style: {
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "#00CCF5 ",
},
{
offset: 0.8,
color: "rgb(4, 88, 115,0.8)",
},
{
offset: 1,
color: "rgb(4, 88, 115,0.6)",
},
]),
},
},
{
type: "topShape",
shape: {
topBasicsYAxis,
basicsXAxis,
bottomYAxis,
},
style: {
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0.3,
color: "#6DF0FF",
},
{
offset: 1,
color: "#6DF0FF",
},
]),
},
},
],
}
},
},
],
}
通过echarts.graphic.extendShape绘制三个图形。
function setBar() {
const leftShape = echarts.graphic.extendShape({
buildPath(ctx, shape) {
console.log("shape", shape)
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape
// 侧面宽度
const WIDTH = 15
// 斜角高度
const OBLIQUE_ANGLE_HEIGHT = 8
const p1 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
const p2 = [basicsXAxis - WIDTH, bottomYAxis]
const p3 = [basicsXAxis, bottomYAxis]
const p4 = [basicsXAxis, topBasicsYAxis]
ctx.moveTo(p1[0], p1[1])
ctx.lineTo(p2[0], p2[1])
ctx.lineTo(p3[0], p3[1])
ctx.lineTo(p4[0], p4[1])
},
})
const rightShape = echarts.graphic.extendShape({
buildPath(ctx, shape) {
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape
// 侧面宽度
const WIDTH = 15
// 斜角高度
const OBLIQUE_ANGLE_HEIGHT = 8
const p1 = [basicsXAxis, topBasicsYAxis]
const p2 = [basicsXAxis, bottomYAxis]
const p3 = [basicsXAxis + WIDTH, bottomYAxis]
const p4 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
ctx.moveTo(p1[0], p1[1])
ctx.lineTo(p2[0], p2[1])
ctx.lineTo(p3[0], p3[1])
ctx.lineTo(p4[0], p4[1])
},
})
const topShape = echarts.graphic.extendShape({
buildPath(ctx, shape) {
const { topBasicsYAxis, basicsXAxis } = shape
// 侧面宽度
const WIDTH = 15
// 斜角高度
const OBLIQUE_ANGLE_HEIGHT = 8
const p1 = [basicsXAxis, topBasicsYAxis]
const p2 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
const p3 = [basicsXAxis, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT * 2]
const p4 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
ctx.moveTo(p1[0], p1[1])
ctx.lineTo(p2[0], p2[1])
ctx.lineTo(p3[0], p3[1])
ctx.lineTo(p4[0], p4[1])
},
})
echarts.graphic.registerShape("leftShape", leftShape)
echarts.graphic.registerShape("rightShape", rightShape)
echarts.graphic.registerShape("topShape", topShape)
myChart.value.setOption(option)
}
完整代码
<template>
<div class="bar" id="main"></div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted } from "vue"
import * as echarts from "echarts"
const option = reactive({
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
xAxis: [
{
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
axisTick: {
alignWithLabel: true,
},
},
],
yAxis: [
{
type: "value",
},
],
series: [
{
type: "custom",
data: [30, 50, 30, 30, 30],
barWidth: 30,
renderItem(params, api) {
// 基础坐标
//api.value(0)可以获取到对应的Index,value(1)获取到y轴value值
//api.coord可以获取到某个点(索引,value)的坐标
// console.log("coord", api.coord([api.value(0), 0]))
const basicsCoord = api.coord([api.value(0), api.value(1)])
console.log("basic", basicsCoord)
// 顶部基础 y 轴,注意:是从顶部空白处到头部的距离
const topBasicsYAxis = basicsCoord[1]
// console.log("topBasic", topBasicsYAxis)
// 基础 x 轴
const basicsXAxis = basicsCoord[0]
// 底部 y 轴 注意:y轴的距离都是从canvas左上角开始,到当前点位的距离
const bottomYAxis = api.coord([api.value(0), 0])[1]
console.log("bottomYAxis", bottomYAxis)
return {
type: "group",
children: [
{
type: "leftShape",
shape: {
topBasicsYAxis,
basicsXAxis,
bottomYAxis,
},
style: {
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "rgb(0, 192, 238,0.8)",
},
{
offset: 0.8,
color: "rgb(0, 194, 239,0.2)",
},
{
offset: 1,
color: "rgb(0, 194, 239,0)",
},
]),
emphasis: {
fill: "yellow", // 鼠标高亮时的填充颜色
},
},
},
{
type: "rightShape",
shape: {
topBasicsYAxis,
basicsXAxis,
bottomYAxis,
},
style: {
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "#00CCF5 ",
},
{
offset: 0.8,
color: "rgb(4, 88, 115,0.8)",
},
{
offset: 1,
color: "rgb(4, 88, 115,0.6)",
},
]),
},
},
{
type: "topShape",
shape: {
topBasicsYAxis,
basicsXAxis,
bottomYAxis,
},
style: {
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0.3,
color: "#6DF0FF",
},
{
offset: 1,
color: "#6DF0FF",
},
]),
},
},
],
}
},
},
],
})
const myChart = ref(null)
onMounted(() => {
init()
})
function init() {
const chartDom = document.getElementById("main")
myChart.value = echarts.init(chartDom)
setBar()
}
function setBar() {
const leftShape = echarts.graphic.extendShape({
buildPath(ctx, shape) {
console.log("shape", shape)
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape
// 侧面宽度
const WIDTH = 15
// 斜角高度
const OBLIQUE_ANGLE_HEIGHT = 8
const p1 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
const p2 = [basicsXAxis - WIDTH, bottomYAxis]
const p3 = [basicsXAxis, bottomYAxis]
const p4 = [basicsXAxis, topBasicsYAxis]
ctx.moveTo(p1[0], p1[1])
ctx.lineTo(p2[0], p2[1])
ctx.lineTo(p3[0], p3[1])
ctx.lineTo(p4[0], p4[1])
},
})
const rightShape = echarts.graphic.extendShape({
buildPath(ctx, shape) {
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape
// 侧面宽度
const WIDTH = 15
// 斜角高度
const OBLIQUE_ANGLE_HEIGHT = 8
const p1 = [basicsXAxis, topBasicsYAxis]
const p2 = [basicsXAxis, bottomYAxis]
const p3 = [basicsXAxis + WIDTH, bottomYAxis]
const p4 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
ctx.moveTo(p1[0], p1[1])
ctx.lineTo(p2[0], p2[1])
ctx.lineTo(p3[0], p3[1])
ctx.lineTo(p4[0], p4[1])
},
})
const topShape = echarts.graphic.extendShape({
buildPath(ctx, shape) {
const { topBasicsYAxis, basicsXAxis } = shape
// 侧面宽度
const WIDTH = 15
// 斜角高度
const OBLIQUE_ANGLE_HEIGHT = 8
const p1 = [basicsXAxis, topBasicsYAxis]
const p2 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
const p3 = [basicsXAxis, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT * 2]
const p4 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
ctx.moveTo(p1[0], p1[1])
ctx.lineTo(p2[0], p2[1])
ctx.lineTo(p3[0], p3[1])
ctx.lineTo(p4[0], p4[1])
},
})
echarts.graphic.registerShape("leftShape", leftShape)
echarts.graphic.registerShape("rightShape", rightShape)
echarts.graphic.registerShape("topShape", topShape)
myChart.value.setOption(option)
}
</script>
<style lang="scss" scoped>
.bar {
width: 400px;
height: 300px;
}
</style>
最终效果