图表组件(Echarts)封装
一.依赖
echarts官网:Apache Echarts
常用的echarts网站:
PPChart:http://ppchart.com/
Make A Pie:https://madeapie.com/
Isqqw:https://www.isqqw.com/
echarts官网示例:https://echarts.apache.org/examples/zh/index.html#chart-type-bar
1.安装
//echarts插件
npm install echarts --save
//3D地图插件
npm install echarts-gl --save
//水球插件
npm install echarts-liquidfill --save
2.版本
echarts 、echarts-gl、echarts-liquidfill版本需要对应起来,不然会报错
echarts 4.0
“echarts”: “^4.9.0”,
“echarts-gl”: “^1.1.2”,
"echarts-liquidfill": "2.0.6",
echarts 5.0
"echarts": "5.4.0",
"echarts-gl": "^2.0.8",
"echarts-liquidfill": "3.0.0",
二.封装Char组件
1.Chart 组件
chart.vue
<template>
<div :id="id" :class="className" :style="{ height: height, width: width }" />
</template>
<script>
import * as echarts from "echarts"; //引入echarts5
// import Echarts from 'echarts' //引入echarts4
import "echarts-gl"; //引入echarts-gl,3D地图插件
import "echarts-liquidfill"; // 水球插件
import { graphic } from "echarts"; //引入graphic
require("echarts/theme/macarons"); // echarts主题
import resize from "./mixins/resize"; //重绘图表函数并自适应
export default {
name: "Echart",
mixins: [resize],
props: {
// class名
className: {
type: String,
default: "chart",
},
// id,自定义 且必须唯一,不能重复
id: {
type: String,
default: "chart",
},
// 图表宽度
width: {
type: String,
default: "100%",
},
// 图标高度
height: {
type: String,
default: "100%",
},
// 传入的数据
options: {
type: Object,
// required: true,
default: () => ({}),
},
},
data() {
return {
chart: null, //chart实列
};
},
//监听图表数据时候变化,重新渲染图表
watch: {
options: {
handler(val) {
// 存在chart时,对chart重绘
if (this.chart) {
this.chart.setOption(val, true);
}
},
deep: true,
},
},
mounted() {
// 初始化chart
this.$nextTick(() => {
this.initChart();
});
},
//页面销毁前,销毁事件和实例
beforeDestroy() {
if (!this.chart) {
return;
}
this.chart.dispose();
this.chart = null;
},
methods: {
/**
* @description 初始化chart
*/
initChart() {
if (this.chart) {
this.chart.dispose();
}
this.$nextTick(() => {
setTimeout(() => {
//使用 macarons 主题
//配置为 svg 形式,预防页面缩放而出现模糊问题;图表过于复杂时建议使用 Canvas
this.chart = echarts.init(this.$el, "macarons", { renderer: "svg" });
this.chart.setOption(this.options, true);
}, 200);
});
},
},
};
</script>
<style></style>
2.resize.js
import { debounce } from '@/utils' // 引入防抖函数
export default {
data() {
return {
$_sidebarElm: null,
$_resizeHandler: null
}
},
mounted() {
this.initListener()
},
activated() {
if (!this.$_resizeHandler) {
// 避免重复 init
this.initListener()
}
// 防止 keep-alive 之后图表变形
// 当 keep-alive 图表激活时,自动调整大小
this.resize()
},
//页面销毁前,销毁事件和实例
beforeDestroy() {
this.destroyListener()
},
deactivated() {
this.destroyListener()
},
methods: {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.$_resizeHandler()
}
},
/**
* @description 自适应不同屏幕时改变图表尺寸
*/
initListener() {
//设置防抖,保证无论拖动窗口大小,只执行一次获取浏览器宽高的方法
this.$_resizeHandler = debounce(() => {
this.resize()
}, 100)
window.addEventListener('resize', this.$_resizeHandler)
this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
},
/**
* @description 销毁事件和实例
*/
destroyListener() {
window.removeEventListener('resize', this.$_resizeHandler)
this.$_resizeHandler = null
this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
},
/**
* @description 重绘图表函数
*/
resize() {
const { chart } = this
chart && chart.resize()
}
}
}
3.防抖
/**
* @description 防抖函数
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
const later = function () {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}
return function (...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
三.chart组件使用
<template>
<div class="echarts-box" style="width: 100%; height: 100%">
<Echart :options="options" id="barChart1"></Echart>
</div>
</template>
<script>
import { graphic } from "echarts";
export default {
data() {
return {
options: {},
};
},
mounted() {
this.init();
},
methods: {
init() {
//柱状图
this.options = {
backgroundColor: "#03213D",
tooltip: {
trigger: "axis",
backgroundColor: "rgba(0,0,0,.6)",
borderColor: "rgba(147, 235, 248, 0)",
textStyle: {
color: "#FFF",
},
},
grid: {
left: "10%",
top: "5%",
right: "5%",
bottom: "15%",
},
xAxis: {
data: [
"宜宾第五初级中学",
"王场初级中学",
"商周镇小学",
"凉水井中学",
"正兴中学",
"文昌中学",
"富加中学",
],
axisLine: {
show: true, //隐藏X轴轴线
lineStyle: {
color: "#163a5f",
width: 2,
},
},
axisTick: {
show: false, //隐藏X轴刻度
alignWithLabel: true,
},
axisLabel: {
show: true,
color: "#ddd", //X轴文字颜色
fontSize: 12,
interval: 0,
formatter: function (value) {
var ret = ""; //拼接加\n返回的类目项
var maxLength = 4; //每项显示文字个数
var valLength = value.length; //X轴类目项的文字个数
var rowN = Math.ceil(valLength / maxLength); //类目项需要换行的行数
if (rowN > 1) {
//如果类目项的文字大于5,
for (var i = 0; i < rowN; i++) {
var temp = ""; //每次截取的字符串
var start = i * maxLength; //开始截取的位置
var end = start + maxLength; //结束截取的位置
//这里也可以加一个是否是最后一行的判断,但是不加也没有影响,那就不加吧
temp = value.substring(start, end) + "\n";
ret += temp; //凭借最终的字符串
}
return ret;
} else {
return value;
}
},
},
},
yAxis: [
{
splitArea: { show: false },
type: "value",
name: "",
nameTextStyle: {
color: "#ddd",
fontSize: 12,
},
splitLine: {
show: false,
lineStyle: {
color: "rgba(255, 255, 255, 0.15)",
// type: 'dashed', // dotted 虚线
},
},
axisTick: {
show: false,
},
axisLine: {
show: true, //隐藏X轴轴线
lineStyle: {
color: "#163a5f",
width: 1,
},
},
axisLabel: {
show: true,
color: "#ddd",
fontSize: 12,
},
},
{
type: "value",
splitArea: { show: false },
name: "",
nameTextStyle: {
color: "#ddd",
fontSize: 12,
},
splitLine: {
show: false,
lineStyle: {
width: 1,
color: "#CED2DB",
},
},
axisTick: {
show: false,
},
axisLine: {
show: false, //隐藏X轴轴线
lineStyle: {
color: "#163a5f",
width: 2,
},
},
axisLabel: {
show: false,
color: "#797A7F",
fontSize: 12,
},
},
],
series: [
{
name: "下载数",
type: "bar",
barWidth: 15,
itemStyle: {
// color: new graphic.LinearGradient(0, 0, 0, 1, [
// {
// offset: 0,
// color: "#00A2FF",
// },
// {
// offset: 1,
// color: "#00CCD2",
// },
// ]),
color: {
type: "linear",
x: 0, //右
y: 0, //下
x2: 0, //左
y2: 1, //上
colorStops: [
{
offset: 0.1,
color: "#13D5FC", // 0% 处的颜色
},
{
offset: 1,
color: "#2059B8", // 100% 处的颜色
},
],
},
borderRadius: [20, 20, 20, 20],
},
label: {
show: true,
position: "top",
distance: 0,
color: "#1ACDDC",
formatter: "{c}" + "次",
},
data: [63, 99, 65, 85, 75, 78, 55],
},
{
// name: '背景',
type: "bar",
barWidth: "15px",
xAxisIndex: 0,
yAxisIndex: 1,
barGap: "-110%",
data: [100, 100, 100, 100, 100, 100, 100], //背景阴影长度
itemStyle: {
color: "rgba(255,255,255,0.039)",
borderRadius: [20, 20, 20, 20],
},
tooltip: {
show: false,
},
zlevel: 9,
},
],
};
},
},
};
</script>
<style lang="scss" scoped></style>