Vue3 Echarts 3D圆形柱状图实现教程以及封装一个可复用的组件

文章目录

  • 前言
  • 一、实现原理
  • [二、series ------type: "pictorialBar" 简介](#二、series ——type: "pictorialBar" 简介)
    • [2.1 常用属性](#2.1 常用属性)
  • 三、代码实战
    • [3.1 封装一个echarts通用组件 echarts.vue](#3.1 封装一个echarts通用组件 echarts.vue)
    • [3.2 首先实现一个基础柱状图](#3.2 首先实现一个基础柱状图)
    • [3.3 添加上下2个椭圆面](#3.3 添加上下2个椭圆面)
    • [3.4 进阶封装一个可复用的3D圆形柱状图组件](#3.4 进阶封装一个可复用的3D圆形柱状图组件)
  • 总结

前言

在前端开发的数据可视化场景中,ECharts 是一个强大且灵活的工具,它能创建各种复杂而美观的图表。本文将详细阐述如何利用 ECharts实现3D圆形柱状图,并封装一个可复用的组件。


一、实现原理

3D圆形柱状图实现原理很简单,可以看成普通的柱状图+上下两个圆面组合合成。模拟人眼斜俯视视角,上下两个面呈现椭圆形,再把普通柱状图调成一定透明度使得底部圆面也能被看到,从而把简单2D平面呈现出一定3D效果。

如上图所示上下2个独立圆面往普通柱状图靠,调到合适位置变成如下效果:

上下两个椭圆形可通过 echarts ------series------ type: "pictorialBar" 自定义图形实现

二、series ------type: "pictorialBar" 简介

type: "pictorialBar" 是 ECharts 中的一种特殊柱状图类型,它允许使用自定义图形(如图片、形状)来代替传统的柱状条,为数据可视化增添更多创意和灵活性。

type: "pictorialBar"也被叫作象形柱图。它首先是个柱状图,但是柱状图的柱子并不显示。这些柱子我们称为『基准柱(reference bar)』,根据基准柱来定位和显示各种象形图形(包括图片)。

每个象形图形根据基准柱的定位,是通过 symbolPosition、symbolOffset 来调整其于基准柱的相对位置。

2.1 常用属性

  • symbol 图形类型,默认圆形

    可选值 'circle'(圆形), 'rect'(直角长方形), 'roundRect'(圆角长方形), 'triangle'(三角形), 'diamond'(菱形), 'pin'(漏斗), 'arrow'(箭头), 'none'(无)

  • symbolSize 图形的大小,默认值 ['100%', '100%']

可以用数组分开表示宽和高,例如 [20, 10] 表示标记宽为20,高为10,也可以设置成诸如 10 这样单一的数字,表示 [10, 10]。

可以设置成绝对值(如 10),也可以设置成百分比(如 '120%'、['55%', 23])。

  • symbolPosition 图形的定位位置

可选择 start':图形边缘与柱子开始的地方内切,'end':图形边缘与柱子结束的地方内切。'center':图形在柱子里居中。

  • symbolOffset 图形相对于原本位置的偏移,默认[0, 0]

  • data 系列中的数据内容数组。数组项通常为具体的数据项。

更多属性可以查阅官方文档

本例需求通过上面几个属性我们就能构建一个椭圆形面

例如:

javascript 复制代码
 {
      type: "pictorialBar",
      symbolSize: [50, 22],//50x22尺寸
      symbolOffset: [0, -8],//向上偏移8px
      symbolPosition:'end',//位于顶部
 }

三、代码实战

以vue3为代码为示例

3.1 封装一个echarts通用组件 echarts.vue

echarts.vue

javascript 复制代码
<template>
	<div class="echarts-box">
		<div ref="echartRef" class="charts" ></div>
	</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, watch, nextTick, markRaw } from 'vue';
import * as echarts from 'echarts';
const props = defineProps({
	// 图表配置
	data: {
		type: Object,
		default: () => {},
	},
});
const echartRef = ref();

let dom = null;

//设置图表配置
const setOptions = (options) => {
	//清除画布
	dom && dom.clear();
	//重新渲染
	dom && dom.setOption(options);
};


watch(
	() => props.data,
	(val) => {
		nextTick(() => {
			//默认关闭动画
			setOptions({animation: false,...val});
		});
	},
	{ deep: true, immediate: true }
);
const emits= defineEmits(['click'])
onMounted(() => {
	//初始化
	dom = markRaw(echarts.init(echartRef.value));
	//点击事件
    dom.on('click',  (param)=> {
		emits('click',param)
	  } )
});
onBeforeUnmount(() => {
	//离开销毁
	echarts.dispose(dom);
	dom = null;
});

defineExpose({
	setOptions,
});
</script>
<style lang="scss" scoped>
.echarts-box {
	width: 100%;
	height: 100%;
	box-sizing: border-box;
}

.charts {
	width: 100%;
	height: 100%;
}
</style>

上述代码封装了一个echarts通用组件,只需传入data图表配置数据就会重新渲染,需要注意的是组件默认继承父元素的宽高(100%),所以父元素需要设置尺寸。

3.2 首先实现一个基础柱状图

demo.vue

javascript 复制代码
<template>
  <div class="container">
    <div class="echarts-view">
      <Echarts :data="data" />
    </div>
  </div>
</template>
<script setup>
import Echarts from "./echarts.vue";
import { ref } from "vue";

const data = ref({
  //位置
  grid: {
    left: "5%",
    right: "5%",
    bottom: "10%",
    top: "15%",
  },
  //提示框
  tooltip: {},
  //图例
  legend: {
    show: true,
    right: "5%",
    textStyle: {
      color: "#fff",
      fontSize: 14,
    },
  },
  //x轴
  xAxis: {
    type: "category",
    //坐标轴轴线
    axisLine: {
      show: true,
      lineStyle: {
        color: "rgba(0, 176, 255,0.3)",
      },
    },
    //刻度
    axisTick: {
      show: false,
    },
    //分割线
    splitLine: {
      show: false,
    },
    // x轴文字
    axisLabel: {
      color: "#D8E6FF",
      fontSize: 14,
      margin: 15,
    },
    data: [
      "星期一",
      "星期二",
      "星期三",
      "星期四",
      "星期五",
      "星期六",
      "星期日",
    ],
  },
  //y轴
  yAxis: {
    name: "单位:元", //单位文字
    nameTextStyle: {
      //单位样式
      color: "#BCD0F4",
      nameLocation: "start",
      fontSize: 14,
    },
    nameGap: 40, //单位与y轴距离
    type: "value",
    //分割线
    splitLine: {
      show: true,
      lineStyle: {
        color: "rgba(0, 176, 255, 0.2)",
        type: "dashed",
      },
    },
    //坐标轴轴线
    axisLine: {
      show: false,
    },
    //刻度数值文字样式
    axisLabel: {
      color: "#BCD0F4",
      fontSize: 14,
    },
  },
  //数据
  series: [
    //中间柱状图
    {
      type: "bar",
      barWidth: 50, //柱子宽度
      itemStyle: {
        //柱子样式
        color: {
          //渐变颜色
          type: "linear",
          x: 0,
          y: 0,
          x2: 0,
          y2: 1,
          colorStops: [
            { offset: 0, color: "#00B7FF" },
            { offset: 1, color: "rgba(34,68,172,0.3)" },
          ],
        },
      },
      name: "收入",
      data: [200, 500, 300, 250, 360, 700, 556],
    },
  ],
});
</script>
<style scoped>
.container {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background-color: #0a2270;
  align-items: center;
}
.echarts-view {
  height: 700px;
  width: 1200px;
}
</style>

运行效果:

上述代码渲染一个最基础的柱状图

3.3 添加上下2个椭圆面

新增如下代码

javascript 复制代码
//数据
 series:[
  //中间柱状图
  ....
  ....
  ....
  //顶部圆面
    {
      type: "pictorialBar",
      symbolSize: [50, 22],
      symbolOffset: [0, -8],
      symbolPosition: "end",
      z: 12,//层级
      itemStyle: {
        color:'#21F3FF',
      },
      name: "收入",
      data: [200, 500, 300, 250, 360, 700, 556],
    },
    //底部圆面
    {
      type: "pictorialBar",
      symbolSize: [50, 22],
      symbolOffset: [0, 8],
      z: 12,//层级
      itemStyle: {
        color: 'rgba(31,97,234,0.4)',
      },
      name: "收入",
      data: [200, 500, 300, 250, 360, 700, 556],
    }
   ]

运行效果:

上述代码通过象形柱状图自定义2个椭圆面,其中一个椭圆面通过symbolPosition: "end"设置为顶部显示另一个默认显示在底部,并通过symbolOffset调整椭圆面位置偏移量使得与基础柱状图融合为一体。

3.4 进阶封装一个可复用的3D圆形柱状图组件

bar-3d.vue

javascript 复制代码
<!-- 3D柱状图 -->
<template>
  <div class="bar-wrap">
    <Echarts :data="data" />
  </div>
</template>

<script setup>
import Echarts from "../components/echarts.vue";
import { computed } from "vue";
const props = defineProps({
//位置
  grid: {
    type: Object,
    default: () => ({
      left: "5%",
      right: "5%",
      bottom: "10%",
      top: "15%",
    }),
  },
  //图例
    legend:{
    type: Object,
    default: () => {},
  },
  //数据
  series: {
    type: Object,
    default: () => {},
  },
  //x轴配置
  xAxis: {
    type: Array,
    default: () => {},
  },
 //y轴配置
  yAxis: {
    type: Object,
    default: () => {},
  },
  //单位
  unit: {
    type: String,
    default: "",
  },
  //上下圆面颜色
  pictorialBarColor: {
    type: Array,
    default: () => ["#21F3FF", "rgba(31,97,234,0.4)"],
  },
});

//echarts配置
const data = computed(() => {
  let { name, data } = props.series;

  //series配置处理
  let seriesData = [
    //中间柱状图
    {
      type: "bar",
      barWidth: 50, //柱子宽度
      itemStyle: {
        //柱子样式
        color: {
          //渐变颜色
          type: "linear",
          x: 0,
          y: 0,
          x2: 0,
          y2: 1,
          colorStops: [
            { offset: 0, color: "#00B7FF" },
            { offset: 1, color: "rgba(34,68,172,0.3)" },
          ],
        },
      },
      ...props.series,
    },
    //顶部圆面
    {
      type: "pictorialBar",
      symbolSize: [50, 22],
      symbolOffset: [0, -8],
      symbolPosition:'end',
      z: 12,
      itemStyle: {
        color: props.pictorialBarColor[0],
      },
      name,
      data: data.map((item) => {
        return {
          value: item,
          itemStyle: {
            opacity: item > 0 ? 1 : 0, //值为0时隐藏
          },
        };
      }),
    },
    //底部圆面
    {
      type: "pictorialBar",
      symbolSize: [50, 22],
      symbolOffset: [0, 8],
      z: 12,
      itemStyle: {
        color: props.pictorialBarColor[1],
      },
      name,
      data: data.map((item) => {
        return {
          value: item,
          itemStyle: {
            opacity: item > 0 ? 1 : 0,
          },
        };
      }),
    },
  ];

  return {
    grid: props.grid,
    tooltip: {},
    legend: {
      show: true,
      right: "5%",
      textStyle: {
        color: "#fff",
        fontSize: 14,
      },
     ...props.legend
    },
    //x轴
    xAxis: {
      type: "category",
      axisLine: {
        show: true,
        lineStyle: {
          color: "rgba(0, 176, 255,0.3)",
        },
      },
      axisTick: {
        show: false,
      },
      splitLine: {
        show: false,
      },
      // x轴文字
      axisLabel: {
        color: "#D8E6FF",
        fontSize: 14,
        margin: 15,
      },
      data: props.xAxis,
    },
    //y轴
    yAxis: {
      name: props.unit ? `单位:${props.unit}` : "", //单位文字
      nameTextStyle: {
        //单位样式
        color: "#BCD0F4",
        nameLocation: "start",
        fontSize: 14,
      },
      nameGap: 40, //单位与y轴距离
      type: "value",
      splitLine: {
        show: true,
        lineStyle: {
          color: "rgba(0, 176, 255, 0.2)",
          type: "dashed",
        },
      },
      axisLine: {
        show: false,
      },
      //刻度数值样式
      axisLabel: {
        color: "#BCD0F4",
        fontSize: 14,
      },
      ...props.yAxis,
    },
    series: seriesData,
  };
});
</script>
<style scoped lang="scss">
.bar-wrap {
  width: 100%;
  height: 100%;
}
</style>

上述代码封装了一个3D圆形柱状图组件,预设了默认样式,同时也暴露了x轴、y轴、图例、圆形柱状图颜色等配置属性可根据需求自定义进行样式覆盖。

页面调用:

demo.vue

javascript 复制代码
<template>
  <div class="container">
    <div class="echarts-view">
      <Bar3D unit="元" :xAxis="xAxisData" :series="seriesData" />
    </div>
  </div>
</template>
<script setup>
import Bar3D from "./components/bar-3d.vue";
import { ref } from "vue";

//x轴标签
const xAxisData = ref([
  "星期一",
  "星期二",
  "星期三",
  "星期四",
  "星期五",
  "星期六",
  "星期日",
]);

//数据
const seriesData = ref({
  name: "收入",
  data: [200, 500, 300, 250, 360, 700, 556],
});




</script>
<style scoped>
.container {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background-color: #0a2270;
  align-items: center;
}
.echarts-view {
  height: 700px;
  width: 1200px;
}
</style>

运行效果:

默认渐变色样式

修改圆柱配色

javascript 复制代码
<template>
  <div class="container">
    <div class="echarts-view">
      <Bar3D unit="元" :xAxis="xAxisData" :series="seriesData" :pictorialBarColor="pictorialBarColor"/>
    </div>
  </div>
</template>
<script setup>
import Bar3D from "./bar-3d.vue";
import { ref } from "vue";

//x轴标签
const xAxisData = ref([
  "星期一",
  "星期二",
  "星期三",
  "星期四",
  "星期五",
  "星期六",
  "星期日",
]);

//数据
const seriesData = ref({
  name: "收入",
  data: [200, 500, 300, 250, 360, 700, 556],
  //柱状图颜色
  itemStyle: {
    color: "#14b1eb",
    opacity: 0.7,
  },
});

//上下圆面颜色
const pictorialBarColor=ref(['#14b1eb','#14b1eb'])


</script>
<style scoped>
.container {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background-color: #0a2270;
  align-items: center;
}
.echarts-view {
  height: 700px;
  width: 1200px;
}
</style>

运行效果:


总结

通过组合使用 type: bar 和 type: "pictorialBar",我们成功实现了 3D 圆形柱状图。同时,了解了 type: "pictorialBar" 的简单使用,它为我们在数据可视化方面提供了更多的创意和可能性。你可以根据实际需求,进一步调整图表的样式和配置,创造出更加美观和实用的可视化效果。

实际开发中可根据使用场景进阶封装,例如在echarts中加入对屏幕尺寸变化监听改变图表尺寸以达到不同分辨率响应式效果以及空数据的显示优化,使得组件更加健壮。

相关推荐
JarvanMo2 分钟前
借助FlutterFire CLI实现Flutter与Firebase的多环境配置
前端·flutter
Jedi Hongbin16 分钟前
echarts自定义图表--仪表盘
前端·javascript·echarts
凯哥197021 分钟前
Sciter.js指南 - 桌面GUI开发时使用第三方模块
前端
边洛洛21 分钟前
对Electron打包的exe文件进行反解析
前端·javascript·electron
财神爷亲闺女21 分钟前
js 实现pc端鼠标横向拖动滚动
前端
用户20311966009621 分钟前
sheet在SwiftUI中的基本用法
前端
晴殇i22 分钟前
一行代码搞定防抖节流:JavaScript新特性解析
前端·javascript
David凉宸25 分钟前
HTML表单(二)
前端