起因:
之前使用uniapp + vue3 + js重构项目, 由于之前做的pc项目使用一直是echarts,所以这次我起先也毫不犹豫的选择使用echarts来做仪表盘
用echarts完成之后,想着uniapp + vue3可以一套代码可以多端使用,所以尝试将其运行到微信小程序,发现报错
报错的原因是echarts在获取dom容器时,只能使用document.querySelect()
,但是小程序要想获取dom元素只能使用uni.createSelectorQuery(),最后查uniapp官网得知echarts不支持多端使用,要想使用多端使用可以选择ucharts
最后使用echarts和ucharts制作的仪表盘效果如下:
echarts仪表盘
echarts和ucharts制作仪表盘的本质都是通过不同图表类型拼接而成, 由于echarts配置项较多,大多数时候我会选择通过一些echarts demo 网站选择自己想要的表格,然后在进行修改, 此次使用通过demo网站找的的仪表图demo放这里了
最终将echarts deom 封装成组件, echarts-gauge.vue 代码如下:
<template>
<view :id="pid" :style="style"></view>
</template>
<script setup>
import useResizeChart from '@/utils/useResizeChart.js'
import echarts from '@/utils/echarts.js'
import {
nextTick,
watch
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app';
const props = defineProps({
pid: {
type: String,
required: true
},
data: {
type: Number,
required: true
},
title: {
type: String
},
max: {
type: Number,
required: true
},
colors: {
type: Object,
// default: ,
default: function() {
return {
// 内部进度条-刻度和刻度值之间的半圆 && 内部进度条--最里面的一层半圆
outsideColor: '#22B95E',
// 环形渐变圆
shadowColor1: 'rgba(45,230,150,0)',
shadowColor2: 'rgba(45,230,150,0.2)',
shadowColor3: 'rgba(45,230,150,1)',
// 渐变环形圆上面一层颜色较深的圆
insideColor: '#30DBBA'
}
}
},
style: {
type: Object,
default: function() {
return {
height: '520rpx',
width: '100%'
}
}
}
});
var color1 = {
type: "linear",
x: 0,
y: 0,
x2: 1,
y2: 1,
colorStops: [{
offset: 0,
color: "rgba(0,0,0,0.1)"
},
{
offset: 1,
color: "rgba(0,0,0,0.3)"
}
],
global: false
}
var color2 = {
type: "linear",
x: 0,
y: 0,
x2: 1,
y2: 1,
colorStops: [{
offset: 0,
color: "#30DBBA"
},
{
offset: 1,
color: "#2DE696"
}
],
global: false
}
// dom元素
let container = null;
// echarts实例
let myChart = null;
let option = {
// backgroundColor: 'pink',
// tooltip: {
// formatter: "{a} <br/>{b} : {c}%"
// },
series: [{
name: "内部进度条-刻度和刻度值之间的半圆",
type: "gauge",
// center: ['20%', '50%'],
radius: '78%',
splitNumber: 10,
axisLine: {
// 刻度和刻度值之间的半圆颜色
lineStyle: {
color: [
[props.data / props.max, props.colors.outsideColor],
[1, props.colors.outsideColor]
],
width: 2
}
},
axisLabel: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
itemStyle: {
// 指针外部颜色
color: "#000",
},
detail: {
show: false,
offsetCenter: [0, 67],
padding: [0, 0, 0, 0],
fontSize: 18,
fontWeight: '700',
color: '#fff'
},
//标题
title: {
show: false,
// x, y,单位px
offsetCenter: [0, 46],
color: "#ffffff",
// 表盘上的标题文字大小
fontSize: 14,
fontFamily: 'PingFangSC'
},
data: [{
name: "",
// value: props.data,
value: (props.data / props.max) * 100,
}],
pointer: {
show: true,
length: '70%',
radius: '20%',
//指针粗细
width: 3
},
animationDuration: 4000,
},
{
// 阴影部分的半圆
name: "内部阴影-渐变阴影部分的环形圆",
type: "gauge",
radius: '56%',
splitNumber: 10,
axisLine: {
lineStyle: {
color: [
[props.data / props.max, new echarts.graphic.LinearGradient(
0, 1, 0, 0, [{
offset: 0,
color: props.colors.shadowColor1,
}, {
offset: 0.5,
color: props.colors.shadowColor2,
},
{
offset: 1,
color: props.colors.shadowColor3,
}
]
)],
[
1, 'rgba(28,128,245,0)'
]
],
width: 45
},
},
axisLabel: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
itemStyle: {
show: false,
},
},
{
name: "渐变环形半圆上面一层半圆",
type: "gauge",
radius: '60%',
splitNumber: 10,
axisLine: {
lineStyle: {
color: [
[props.data / props.max, props.colors.insideColor],
[1, "rgba(0,0,0,0)"]
],
width: 5
}
},
axisLabel: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
itemStyle: {
show: false,
},
},
{
name: '外部刻度',
type: 'gauge',
// center: ['20%', '50%'],
radius: '76%',
//最小刻度
min: 0,
//最大刻度
max: props.max,
//刻度数量
splitNumber: 5,
startAngle: 225,
endAngle: -45,
axisLine: {
show: true,
lineStyle: {
width: 1,
color: [
[1, 'rgba(0,0,0,0)']
]
}
},
//仪表盘轴线
axisLabel: {
show: true,
// 仪表刻度值颜色
color: '#000',
fontSize: 14,
fontFamily: 'SourceHanSansSC-Regular',
fontWeight: 'bold',
// position: "top",
distance: -20,
},
//刻度标签。
axisTick: {
show: true,
splitNumber: 3,
lineStyle: {
//用颜色渐变函数不起作用
color: color1,
width: 1,
},
// 刻度的长度
length: -6
},
//刻度样式
splitLine: {
show: true,
length: -11,
lineStyle: {
//用颜色渐变函数不起作用
color: color1,
}
}, //分隔线样式
detail: {
show: false
}
},
{
name: "内部进度条--最里面的一层半圆",
type: "gauge",
// center: ['20%', '50%'],
radius: '20%',
splitNumber: 10,
axisLine: {
lineStyle: {
color: [
[props.data / props.max, props.colors.outsideColor],
[1, props.colors.outsideColor]
],
width: 1
}
},
axisLabel: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
itemStyle: {
color: "#000"
},
detail: {
formatter: function(value) {
if (props.max === 100) {
if (value !== 0) {
var num = Math.round(value);
return parseInt(num).toFixed(0) + "%";
} else {
return 0;
}
} else {
return value / 100
}
},
offsetCenter: [0, 67],
padding: [0, 0, 0, 0],
fontSize: 18,
// value 字体颜色
color: "#000"
// }
},
// 标题
title: {
show: true,
// x, y,单位px
offsetCenter: [0, 46],
// name: 颜色
color: "#000",
//表盘上的标题文字大小
fontSize: 14,
fontWeight: 400,
fontFamily: 'MicrosoftYaHei'
// }
},
data: [{
name: props.title,
value: (props.data / props.max) * 100,
itemStyle: {
// 指针内部颜色
color: "#000",
fontFamily: "MicrosoftYaHei",
fontSize: 14
}
}],
pointer: {
show: true,
length: '70%',
radius: '20%',
//指针粗细
width: 3
},
animationDuration: 4000,
},
{ //指针上的圆
type: 'pie',
tooltip: {
show: false
},
// hoverAnimation: false,
legendHoverLink: false,
radius: ['0%', '4%'],
center: ['50%', '50%'],
label: {
show: false
},
labelLine: {
show: false
},
data: [{
value: 120,
itemStyle: {
// 指针上的圆,颜色
color: "#000",
}
}]
},
]
};
function renderChart() {
if (container) {
myChart = echarts.init(container)
myChart.setOption(option)
useResizeChart(container, myChart)
}
}
nextTick(() => {
container = document.querySelector('#' + props.pid)
renderChart()
})
watch(() => props, () => {
option = initOption();
renderChart();
}, {
deep: true,
}, )
function exportImg() {
const src = (myChart).getDataURL({
pixelRatio: 2,
backgroundColor: '#fff',
});
const a = document.createElement('a');
a.href = src;
a.download = 'chart-img';
a.click();
};
defineExpose({
exportImg,
});
</script>
<style lang="scss" scoped>
</style>
ucharts仪表盘
ucharts相比于echarts,它的配置项没有那么复杂,几乎是将 ucharts官网 demo上的代码粘贴过去就能直接用(前提是安装了啊), 最后更具自己的需要调整图表样式就可以了
使用ucharts我属于新手上路,下面将我在其学的到的知识点几率一下,在po代码
vue3 组合式式获取组件实例
- vue2 或者 vue3 我们使用的是选项式API (Options API )时, 组件内可以使用this,而this就是组件实例,
- 但是当vue3使用setup时,我们不能使用this,此时想要获取组件实例我们可以使用
getCurrentInstance
import { getCurrentInstance } from 'vue'
const test = getCurrentInstance()
console.log('组件实例', test)
之所以会获取组件实例, 是因为ucharts有原生和组件两种使用方式, 使用原生会使用到组件实例
仪表盘刻度值浮点数问题
真的,我觉得这个就是ucharts的bug,我找到码云翻到有人提过这个问题, 甚至把解决方案的代码都发出来了,作者都不改,作者说可以用format,结果我用了format根本没用!!! 最后我甚至还提交了issues, 写的时候真的是火大, 最后根据别人提交的issues,修改了u-charts.js,这个问题才被解决, 具体修改方法放在这里
最后使用ucharts封装的仪表盘组件代码如下:
<template>
<view class="charts" :style="style">
<!-- 最外层半圆 -->
<view class="charts-box-outside">
<qiun-data-charts type="arcbar" :opts="arcbarOpts" :chartData="arcbarChartData" />
</view>
<!-- 深颜色进度条 -->
<view class="charts-box-Progress">
<qiun-data-charts type="arcbar" :opts="progressOpts" :chartData="ProgressChartData" />
</view>
<!-- 渐变进度条 -->
<view class="charts-box-color">
<qiun-data-charts type="arcbar" :opts="colorOpts" :chartData="colorChartData" />
</view>
<!-- 最内部半圆 -->
<view class="charts-box-inside">
<qiun-data-charts type="arcbar" :opts="arcbarOpts" :chartData="arcbarChartData" />
</view>
<!-- 仪表盘 -->
<view class="charts-box">
<qiun-data-charts type="gauge" :opts="opts" :chartData="chartData" />
</view>
</view>
</template>
<script setup>
import {
getCurrentInstance,
ref,
nextTick
} from 'vue'
import uCharts from '@/utils/u-charts.js';
import {
onReady
} from '@dcloudio/uni-app'
const props = defineProps({
subtitle: {
type: String,
},
unit: {
type: String,
default: ""
},
data: {
type: Number,
required: true
},
max: {
type: Number,
default: 100
},
style: {
type: Object,
default: function() {
return {
width: '100%',
height: '300px'
}
}
},
colors: {
type: Object,
default: function () {
return {
sideColor: "#1890FF",
progressColor: "#00bfff",
ColorCustom: ["#4ed3ff", "#00bfff"]
}
}
}
})
const chartData = ref({})
// 最外层半圆 and 最内部半圆
const arcbarChartData = ref({})
// 渐变进度条
const colorChartData = ref({})
const ProgressChartData = ref({})
// 仪表盘配置
const opts = ref({
padding: undefined,
title: {
name: props.data + props.unit,
fontSize: 16,
color: "#666666",
offsetY: 100
},
subtitle: {
name: props.subtitle,
fontSize: 14,
color: "#666666",
offsetY: 60
},
extra: {
gauge: {
type: "default",
width: 15,
labelColor: "#000",
startAngle: 0.75,
endAngle: 0.25,
startNumber: 0,
endNumber: props.max,
splitLine: {
fixRadius: 0,
splitNumber: 5,
width: 15,
color: "#000",
childNumber: 5,
childWidth: 12,
},
labelOffset: -30,
pointer: {
width: 10,
color: "#000000"
},
// formatter: (val, index, opts) => {
// console.log('111111111',val, index, opts )
// // return val.toFixed(1)
// }
}
}
})
// 进度条配置: 最外层半圆 and 最内部半圆
const arcbarOpts = ref({
padding: undefined,
title: {
name: "",
},
subtitle: {
name: "",
},
extra: {
arcbar: {
type: "default",
width: 2,
backgroundColor: "#E9E9E9",
startAngle: 0.75,
endAngle: 0.25,
gap: 2,
linearType: "none",
lineCap: "butt"
}
}
})
// 深颜色进度条配置
const progressOpts = ref({
padding: undefined,
title: {
name: "",
},
subtitle: {
name: "",
},
extra: {
arcbar: {
type: "default",
width: 8,
backgroundColor: "rgba(0,0,0,0.001)",
startAngle: 0.75,
endAngle: 0.25,
gap: 2,
linearType: "none",
lineCap: "butt"
}
}
})
// 渐变进度条配置
const colorOpts = ref({
padding: undefined,
title: {
name: "",
fontSize: 35,
color: "#2fc25b"
},
subtitle: {
name: "",
fontSize: 25,
color: "#666666"
},
extra: {
arcbar: {
type: "default",
width: 52,
backgroundColor: "rgba(0,0,0,0.001)",
startAngle: 0.75,
endAngle: 0.25,
gap: 2,
linearType: "custom",
custom: props.colors.ColorCustom,
lineCap: "butt"
}
}
})
// 仪表盘数据
function getChartData() {
let res = {
categories: [{
"value": 0.2,
"color": "rgba(0,0,0,0.01)"
}, {
"value": 0.8,
"color": "rgba(0,0,0,0.01)"
}, {
"value": 1,
"color": "rgba(0,0,0,0.01)"
}, ],
series: [{
name: "完成率",
data: props.data / props.max,
}],
};
chartData.value = JSON.parse(JSON.stringify(res));
}
// 获取组件实例
// 在 setup 和其他 Composition API 中没有 this
// 可通过 getCurrentInstance 获取当前实例
// 若使用 Options API 可照常使用 this
// const test = getCurrentInstance()
// console.log('组件实例', test)
// 最外层半圆 and 最内部半圆 进度条数据固定
function getArcbarChartData() {
let res = {
series: [{
name: "最外层半圆 and 最内部半圆",
color: props.colors.sideColor,
data: 1
}]
};
arcbarChartData.value = JSON.parse(JSON.stringify(res));
}
function getProgressChartData() {
let res = {
series: [{
name: "深颜色进度条",
color: props.colors.progressColor,
data: props.data / props.max
}]
};
ProgressChartData.value = JSON.parse(JSON.stringify(res));
}
function getColorChartData() {
let res = {
series: [{
name: "渐变进度条",
color: "#eafcf4",
data: props.data / props.max
}]
};
colorChartData.value = JSON.parse(JSON.stringify(res));
}
nextTick(() => {
getChartData()
getArcbarChartData()
getProgressChartData()
getColorChartData()
})
</script>
<style lang="scss">
.charts {
position: relative;
.charts-box-outside {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90%;
height: 90%;
}
.charts-box {
width: 87%;
height: 87%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
// background-color: aquamarine;
}
.charts-box-Progress {
width: 70%;
height: 70%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.charts-box-color {
width: 65%;
height: 65%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.charts-box-inside {
width: 30%;
height: 30%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
</style>