前言
项目中很多时候都带有dark
/light
两中主题类型,通过switch
标签控制,但是echarts
图形是通过canvas
标签绘制,其背景颜色和字体样式并不会随着项目主题类型的切换而切换。所以需要额外设置监听主题事件,主要实现思路如下:
- 配置
echarts
主题,并引入注册 - 配置选项并绘制图形
- 监听系统主题变化,根据其改变
echarts
主题 - 监听屏幕变化,实现大小屏切换
resize
效果
实现效果
dark主题:
light主题:
项目环境
配置echarts主题
其实最新的echarts(5.x.x)
已支持dark
/light
模式两种主题,但是很多时候还是满足不了项目需求,比如主题下的primary
、warnning
、error
等样式,就可以使用**registerThemeAPI**注册主题样式,先配置主题样式(地址:主题编辑器),然后进行注册,这个官网已经给出了教程
vuetify
的dark
样式其实和echarts
的dark
样式相似,故不用使用**registerTheme**注册,主要区别在于backgroundColor
和color
的不同,所以使用在init()
之前使用条件判断,如下:
let baseOptions = {};
// vm.$vuetify.theme.dark:是否是dark模式
if (vm.$vuetify.theme.dark) {
baseOptions = {
backgroundColor: themeConfig.themes.dark.backgroundColor,
textStyle: {
color: themeConfig.themes.dark.color,
},
};
}
绘制echarts图形
主要是通过onDrawLine()
实现,实现思路如下:
- 判断
vm.$vuetify.theme.dark
(项目dark
主题)是否为true
- 如果为
true
,配置backgroundColor
和textStyle.color
,使用echarts
的dark
主题 - 如果为
false
,不配置backgroundColor
和textStyle.color
,使用echarts
中的light
(即默认主题)
let lineChart = {};
const splitLine = {
show: true,
lineStyle: {
// 实线
// type: 'solid',
// width: 0.5,
// opacity: 0.8,
// 虚线
type: [5, 5],
dashOffset: 0,
shadowBlur: 0,
},
};
const axisLine = { show: false };
const axisTick = { show: false };
const onDrawLine = () => {
lineChart = vm.$echarts.init(
document.getElementById("line-chart"),
vm.$vuetify.theme.dark ? "dark" : "light"
);
let baseOptions = {};
if (vm.$vuetify.theme.dark) {
baseOptions = {
backgroundColor: themeConfig.themes.dark.backgroundColor,
textStyle: {
color: themeConfig.themes.dark.color,
},
};
}
const option = {
...baseOptions,
color: [
$themeColors.error,
$themeColors.danger,
$themeColors.warning,
$themeColors.low,
],
title: {
// text: 'Stacked Line'
},
tooltip: {
trigger: "axis",
},
legend: {
data: ["严重", "高", "中", "低"],
// 放在底部
bottom: 0,
},
grid: {
top: "5%",
left: "2%",
right: "3%",
bottom: "10%",
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
data: [
"10/09",
"10/10",
"10/11",
"10/12",
"10/13",
"10/14",
"10/15",
"10/16",
"10/17",
"10/18",
"10/19",
"10/20",
"10/21",
"10/22",
"10/23",
],
splitLine,
axisLine,
axisTick,
},
yAxis: {
type: "value",
splitLine,
interval: 90,
max: 450,
axisLine,
},
// 数据值
series: [
{
name: "严重",
type: "line",
lineStyle: {
width: 5,
},
// 显示点为实圆心
stack: "Total",
showSymbol: false,
symbol: "none",
data: [
80, 150, 180, 270, 210, 160, 160, 202, 265, 210, 270, 255, 290, 360,
375,
],
},
{
name: "高",
type: "line",
showSymbol: false,
symbol: "none",
lineStyle: {
width: 5,
},
data: [
80, 125, 105, 130, 215, 195, 140, 160, 230, 300, 220, 170, 210, 200,
280,
],
},
{
name: "中",
type: "line",
showSymbol: false,
symbol: "none",
lineStyle: {
width: 5,
},
data: [
80, 99, 82, 90, 115, 115, 74, 75, 130, 155, 125, 90, 140, 130, 180,
],
},
{
name: "低",
type: "line",
showSymbol: false,
symbol: "none",
lineStyle: {
width: 5,
},
data: [
10, 19, 32, 50, 15, 105, 14, 55, 30, 145, 115, 80, 200, 100, 150,
],
},
],
};
lineChart.setOption(option)
};
注意:这里的lineChart
不用使用ref
声明,因为不需要做响应式
术语速查手册
echarts的配置项真的很多,有时候不知道哪个是哪个意思,找起来比较费时间,官网给出了术语速查手册,有助于理解记忆
实现动态切换
实现echart图形切换主要思路如下:
- 监听项目主题(
vm.$vuetify.theme.dark
) - 注销图形(
dispose()
) - 重绘图形
主要代码如下:
watch(
() => vm.$vuetify.theme.dark,
(value) => {
lineChart.dispose();
onDrawLine();
},
{
// immediate: true,
deep: true,
}
);
**注意:**不能immediate
不能为true
,组件初始化时,lineChart
还没有init()
,所以会报错,故第一次绘制需要在onMounted
,主要实现如下:
onMounted(() => {
onDrawLine();
});
实现大小屏切换
主要通过[window.onresize](https://juejin.cn/post/7168860905625796616)
实现,因为项目需求,有时候会进行大小屏切换,主要实现代码如下:
onMounted(() => {
onDrawLine();
window.onresize = function () {
lineChart.resize();
};
});
onBeforeUnmount(() => {
window.onresize = null;
});
注意: 在组件实例销毁时,必须注销window.onresize
,原因如下:
window.onresize
是全局组件,不注销会影响其他组件的功能- 系统性能变差,因为
window.onresize
只要屏幕有改变就会触发,如果不注销又多个组件使用,屏幕变化一次,window.onresize
会被执行多次
vue3版本代码实现
<!--
* @Description:告警时间趋势 折线图
* @version:1.0.0
* @Author: yuanyu
* @Date: 2022-11-03 10:22:55
* @LastEditors: yuanyu
* @LastEditTime: 2022-12-09 16:17:23
-->
<template>
<!-- <vue-apex-charts
type="line"
height="280"
:options="apexChatData.lineChartSimple.chartOptions"
:series="apexChatData.lineChartSimple.series"
/> -->
<div id="line-chart" style="min-height:295px;width:100%"></div>
</template>
<script>
import VueApexCharts from 'vue-apexcharts'
import {
onMounted, getCurrentInstance, onBeforeUnmount, watch,
} from 'vue-demi'
import themeConfig from '@themeConfig'
import apexChatData from './apexChartData'
const $themeColors = themeConfig.themes.light
export default {
components: { VueApexCharts },
setup() {
const vm = getCurrentInstance().proxy
console.log('document', document.getElementById('line-chart'))
let lineChart = {}
const splitLine = {
show: true,
lineStyle: {
// 实线
// type: 'solid',
// width: 0.5,
// opacity: 0.8,
// 虚线
type: [5, 5],
dashOffset: 0,
shadowBlur: 0,
},
}
const axisLine = { show: false }
const axisTick = { show: false }
const onDrawLine = () => {
lineChart = vm.$echarts.init(document.getElementById('line-chart'), vm.$vuetify.theme.dark ? 'dark' : 'light')
let baseOptions = {}
if (vm.$vuetify.theme.dark) {
baseOptions = {
backgroundColor: themeConfig.themes.dark.backgroundColor,
textStyle: {
color: themeConfig.themes.dark.color,
},
}
}
const option = {
...baseOptions,
color: [$themeColors.error, $themeColors.danger, $themeColors.warning, $themeColors.low],
title: {
// text: 'Stacked Line'
},
tooltip: {
trigger: 'axis',
},
legend: {
data: ['严重', '高', '中', '低'],
// 放在底部
bottom: 0,
},
grid: {
top: '5%',
left: '2%',
right: '3%',
bottom: '10%',
containLabel: true,
},
xAxis: {
type: 'category',
boundaryGap: false,
data: [
'10/09',
'10/10',
'10/11',
'10/12',
'10/13',
'10/14',
'10/15',
'10/16',
'10/17',
'10/18',
'10/19',
'10/20',
'10/21',
'10/22',
'10/23',
],
splitLine,
axisLine,
axisTick,
},
yAxis: {
type: 'value',
splitLine,
interval: 90,
max: 450,
axisLine,
},
// 数据值
series: [
{
name: '严重',
type: 'line',
lineStyle: {
width: 5,
},
// 显示点为实圆心
stack: 'Total',
showSymbol: false,
symbol: 'none',
data: [80, 150, 180, 270, 210, 160, 160, 202, 265, 210, 270, 255, 290, 360, 375],
},
{
name: '高',
type: 'line',
showSymbol: false,
symbol: 'none',
lineStyle: {
width: 5,
},
data: [80, 125, 105, 130, 215, 195, 140, 160, 230, 300, 220, 170, 210, 200, 280],
},
{
name: '中',
type: 'line',
showSymbol: false,
symbol: 'none',
lineStyle: {
width: 5,
},
data: [80, 99, 82, 90, 115, 115, 74, 75, 130, 155, 125, 90, 140, 130, 180],
},
{
name: '低',
type: 'line',
showSymbol: false,
symbol: 'none',
lineStyle: {
width: 5,
},
data: [10, 19, 32, 50, 15, 105, 14, 55, 30, 145, 115, 80, 200, 100, 150],
},
],
}
// option = {
// ...apexChatData.lineChartSimple.chartOptions,
// series: apexChatData.lineChartSimple.series.map(item => Object.assign(item, { type: 'line' })),
// }
console.log('apexChatData', apexChatData)
lineChart.setOption(option)
window.onresize = function () {
console.log('改变了', vm.$vuetify.theme.dark)
lineChart.resize()
}
}
onMounted(() => {
onDrawLine()
})
onBeforeUnmount(() => {
window.onresize = null
})
watch(
() => vm.$vuetify.theme.dark,
value => {
lineChart.dispose()
onDrawLine()
},
{
// immediate: true,
deep: true,
},
)
return {
apexChatData,
}
},
}
</script>
vue2版本代码实现
<!--
* @Description:告警时间趋势 折线图
* @version:1.0.0
* @Author: xxxxxx
* @Date: 2022-12-09 14:50:38
* @LastEditors: xxxxxx
* @LastEditTime: 2022-12-09 16:41:23
-->
<template>
<div id="line-chart" :style="`height:${height};width:${width}`"></div>
</template>
<script>
import themeConfig from '@themeConfig'
const $themeColors = themeConfig.themes.light
let lineChart = {}
const splitLine = {
show: true,
lineStyle: {
// 实线
// type: 'solid',
// width: 0.5,
// opacity: 0.8,
// 虚线
type: [5, 5],
dashOffset: 0,
shadowBlur: 0,
},
}
const axisLine = { show: false }
const axisTick = { show: false }
export default {
props: {
// 高度
height: {
type: [String],
require: true,
},
// 宽度
width: {
type: String,
default: '100%',
},
option: {
type: Object,
default: {}
},
},
data() {
return {}
},
watch: {
'$vuetify.theme.dark': {
handler(value) {
lineChart.dispose()
this.onDrawLine()
},
deep: true,
},
},
mounted() {
this.onDrawLine()
},
beforeDestroy() {
window.onresize = null
},
methods: {
onDrawLine() {
lineChart = this.$echarts.init(document.getElementById('line-chart'), this.$vuetify.theme.dark ? 'dark' : 'light')
let baseOptions = {}
if (this.$vuetify.theme.dark) {
baseOptions = {
backgroundColor: themeConfig.themes.dark.backgroundColor,
textStyle: {
color: themeConfig.themes.dark.color,
},
}
}
const option = {
...baseOptions,
color: [$themeColors.error, $themeColors.danger, $themeColors.warning, $themeColors.low],
title: {
// text: 'Stacked Line'
},
tooltip: {
trigger: 'axis',
},
legend: {
data: ['严重', '高', '中', '低'],
// 放在底部
bottom: 0,
},
grid: {
top: '5%',
left: '2%',
right: '3%',
bottom: '10%',
containLabel: true,
},
xAxis: {
type: 'category',
boundaryGap: false,
data: [
'10/09',
'10/10',
'10/11',
'10/12',
'10/13',
'10/14',
'10/15',
'10/16',
'10/17',
'10/18',
'10/19',
'10/20',
'10/21',
'10/22',
'10/23',
],
splitLine,
axisLine,
axisTick,
},
yAxis: {
type: 'value',
splitLine,
interval: 90,
max: 450,
axisLine,
},
// 数据值
series: [
{
name: '严重',
type: 'line',
lineStyle: {
width: 5,
},
// 显示点为实圆心
stack: 'Total',
showSymbol: false,
symbol: 'none',
data: [80, 150, 180, 270, 210, 160, 160, 202, 265, 210, 270, 255, 290, 360, 375],
},
{
name: '高',
type: 'line',
showSymbol: false,
symbol: 'none',
lineStyle: {
width: 5,
},
data: [80, 125, 105, 130, 215, 195, 140, 160, 230, 300, 220, 170, 210, 200, 280],
},
{
name: '中',
type: 'line',
showSymbol: false,
symbol: 'none',
lineStyle: {
width: 5,
},
data: [80, 99, 82, 90, 115, 115, 74, 75, 130, 155, 125, 90, 140, 130, 180],
},
{
name: '低',
type: 'line',
showSymbol: false,
symbol: 'none',
lineStyle: {
width: 5,
},
data: [10, 19, 32, 50, 15, 105, 14, 55, 30, 145, 115, 80, 200, 100, 150],
},
],
}
lineChart.setOption(option)
window.onresize = function () {
console.log('改变了', this.$vuetify.theme.dark)
lineChart.resize()
}
},
},
}
</script>
<style scoped lang='scss'>
</style>
求解
在项目中,echarts图形会不止一个,如果每次使用watch监听主题变化,代码会变得非常冗余,目前的思路是封装一个echarts公共组件,不知道有没有大佬有更好的思路,求指点
参考链接
https://echarts.apache.org/examples/zh/editor.html?c=line-stack