echarts-3d柱状图

3,562 阅读6分钟

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>

效果如下:

image.png

3d bar分解

首先,我们要知道,echarts是基于canvas进行绘制的,canvas画布是从左上角作为起始点,往右下方进行绘制

image.png

canvas的坐标点也是从左上角往下

立体柱状图,我们需要绘制的是三个面,左、右、顶,这三个面我们需要计算出周边的 点位坐标,然后利用canvas进行绘制

image.png

  • 左侧面

image.png

左侧面的坐标信息为: P1:【基础X轴坐标点-侧面宽度,顶部Y轴坐标点-斜角高度】 P2:【基础X轴坐标点-侧面宽度,底部Y轴坐标点】 P3:【基础X轴坐标点,底部Y轴坐标点】 P4:【基础X轴坐标点,顶部Y轴坐标点】

  • 右侧面

image.png 右侧面的坐标信息为 P1:【基础X轴坐标点,顶部Y轴坐标点】 P2:【基础X轴坐标点,底部Y轴坐标点】 P3:【基础X轴坐标点 +侧面宽度 ,底部Y轴坐标点】 P4:【基础X轴坐标点 +侧面宽度,顶部Y轴坐标点 - 斜角高度】

  • 顶部

image.png 顶面的坐标信息为 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>

最终效果

image.png