1 认识ECharts
ECharts
,一个基于 JavaScript 的开源可视化
图表库
国内Echarts使用手册网站:
https://www.w3cschool.cn/echarts_tutorial/echarts_tutorial-d5b128yu.html
示例网站:
https://www.isqqw.com/
http://chart.majh.top/
https://www.ppchart.com/#/
2 为什么需要封装echarts
- 每个开发者在制作图表时都需要从头到尾书写一遍完整的option配置,十分冗余
- 在同一个项目中,各类图表设计十分相似,甚至是相同,没必要一直做重复工作
- 可能有一些开发者忘记考虑echarts更新数据的特性,以及窗口缩放时的适应问题。这样导致数据更新了echarts视图却没有更新,窗口缩放引起echarts图形变形问题
2.0 Echarts/mixins/resize.js
export default {
mounted() {
window.addEventListener('resize', this.resizeHandler)
},
activated() {
this.resizeHandler()
},
methods: {
resizeHandler() {
if (this['myChart' + this.domId]) {
this['myChart' + this.domId].resize()
}
}
}
}
2.1 封装柱状/折线 一体图(1网格)
<!--柱状/折线 一体图(1grid)-->
<template>
<div
:id="domId"
:key="domId"
:style="{ height: height, width: width,backgroundColor: backgroundColor }"
/>
</template>
<script>
import * as echarts from 'echarts'
import resize from './mixins/resize'
import 'echarts/lib/component/dataZoom'
export default {
mixins: [resize],
props: {
domId: { type: String, default: 'chartId' },
height: { type: String, default: '300px' },
width: { type: String, default: '100%' },
backgroundColor: { type: String, default: '#fff' },
data: { type: Object, default: null }
},
watch: {
data: {
handler(val) {
// 这里使用nextTick解决Error: Initialize failed: invalid dom
this.$nextTick(() => {
this.initChart(val)
})
},
deep: true
}
},
mounted() {
this.initChart(this.data)
},
methods: {
initChart(data) {
if (!data) {
return false
}
// const yAxis = this.setInterval(data.yAxis, data.series)
this['myChart' + this.domId] = echarts.init(
document.getElementById(this.domId)
)
this['myChart' + this.domId].clear() // 清空画布,防止缓存
this['myChart' + this.domId].setOption({
grid: data.grid || {
left: '10%',
top: '15%',
right: '10%',
bottom: '25%'
},
toolbox: data.toolbox,
axisPointer: data.axisPointer || null,
title: data.title
? data.title
: {
text: null,
textStyle: {
fontSize: 10,
color: '#666666'
},
left: '5%'
},
legend: data.legend
? data.legend
: {
data: data.legendData,
top: 0,
textStyle: {
fontSize: 9,
color: '#666666'
},
formatter: function(name) {
return name.length > 12 ? name.substr(0, 12) + '...' : name
}
},
tooltip: data.tooltip
? data.tooltip
: {
trigger: 'axis'
},
xAxis: data.xAxis,
// yAxis: yAxis,
yAxis: data.yAxis,
dataZoom: data.dataZoom,
visualMap: data.visualMap,
series: data.series
})
}
// 根据data来判断y轴的间隔
// setInterval(yAxis, series) {
// let types = new Set()
// series.forEach((el) => {
// types.add(el.type)
// })
// types = Array.from(types)
// const maxArray = []
// const minArray = []
// types.forEach((m) => {
// let data = []
// series.forEach((n) => {
// if (m === n.type) {
// data = [...data, ...n.data]
// }
// })
// let max = Math.max(...data)
// const min = Math.min(...data)
// if (max > 0) {
// max = parseInt(Math.ceil(max))
// } else {
// max = parseInt(Math.floor(max))
// }
// if (max.toString().length === 1) {
// // 个位数
// if (max > 5) {
// max = 10
// }
// // else {
// // max = 5
// // }
// } else if (max < 1) {
// max = 1
// } else {
// // 两位以上
// let first = ('' + max)[0]
// const second = ('' + max)[1]
// if (second > 5) {
// first = parseInt(first) + 1
// for (let i = 0; i < max.toString().length - 1; i++) {
// first = first + '0'
// }
// } else {
// first = first + '5'
// for (let i = 0; i < max.toString().length - 2; i++) {
// first = first + '0'
// }
// }
// max = parseInt(first)
// }
// maxArray.push(max)
// minArray.push(min)
// })
// yAxis.map((v, i) => {
// v.max = Math.ceil(maxArray[i]) // 向上取整
// v.min = Math.floor(minArray[i]) // 向下取整
// if (v.min > 0) {
// v.min = 0
// }
// if (v.max < 0) {
// v.max = 0
// }
// v.interval = (v.max - v.min) / 5
// })
// return yAxis
// }
}
}
</script>
2.2 封装饼图
<!--饼图-->
<template>
<div :id="domId" :key="domId" :style="{ height: height, width: '100%',backgroundColor:'#fff' }" />
</template>
<script>
import * as echarts from 'echarts'
import resize from './mixins/resize'
import 'echarts/lib/component/dataZoom'
export default {
mixins: [resize],
props: {
domId: { type: String, default: 'chartId' },
height: { type: String, default: '300px' },
data: { type: Object, default: null }
},
watch: {
data: {
handler(val) {
// 这里使用nextTick解决Error: Initialize failed: invalid dom
this.$nextTick(() => {
this.initChart(val)
})
},
deep: true
}
},
mounted() {
this.initChart(this.data)
},
methods: {
initChart(data) {
if (!data) {
return false
}
const that = this
this['myChart' + this.domId] = echarts.init(
document.getElementById(this.domId)
)
this['myChart' + this.domId].setOption({
grid: {
left: '10%',
top: '15%',
right: '10%',
bottom: '25%'
},
toolbox: data.toolbox,
title: {
text: data.title ? data.title : null,
textStyle: {
fontSize: 10,
color: '#666666'
},
left: '5%'
},
legend: data.legend ? data.legend : {
data: data.legendData,
top: 0,
textStyle: {
fontSize: 9,
color: '#666666'
},
formatter: (name) => {
if (!name) return ''
return that.getEqualNewlineString(name, 10) // 根据需求修改参数
}
},
tooltip: data.tooltip ? data.tooltip : {
trigger: 'axis'
},
series: data.series
})
},
/**
* 超出换行的方法
* @param {String} params 要处理的字符串
* @param {Number} length 每行显示长度
* @returns {String}
*/
getEqualNewlineString(params, length) {
let text = ''
const count = Math.ceil(params.length / length) // 向上取整数
// 一行展示length个
if (count > 1) {
for (let z = 1; z <= count; z++) {
text += params.substr((z - 1) * length, length)
if (z < count) {
text += '\n'
}
}
} else {
text += params.substr(0, length)
}
return text
}
}
}
</script>
2.3柱状/折线 一体图(2网格)
<!--柱状/折线 一体图(2grid)-->
<template>
<div :id="domId" :key="domId" :style="{ height: height, width: '100%',backgroundColor:'#fff' }" />
</template>
<script>
import * as echarts from 'echarts'
import resize from './mixins/resize'
import 'echarts/lib/component/dataZoom'
export default {
mixins: [resize],
props: {
domId: { type: String, default: 'chartId' },
height: { type: String, default: '300px' },
data: { type: Object, default: null }
},
watch: {
data: {
handler(val) {
// 这里使用nextTick解决Error: Initialize failed: invalid dom
this.$nextTick(() => {
this.initChart(val)
})
},
deep: true
}
},
mounted() {
this.initChart(this.data)
},
methods: {
initChart(data) {
if (!data) {
return false
}
this['myChart' + this.domId] = echarts.init(
document.getElementById(this.domId)
)
this['myChart' + this.domId].clear() // 清空画布,防止缓存
this['myChart' + this.domId].setOption({
grid: data.grid
? data.grid
: [
{
left: '10%',
top: '15%',
right: '10%',
height: '35%'
},
{
left: '10%',
top: '50%',
right: '10%',
height: '35%'
}
],
toolbox: data.toolbox,
title: {
text: data.title ? data.title : null,
textStyle: {
fontSize: 10,
color: '#666666'
},
left: '5%'
},
legend: data.legend
? data.legend
: {
data: data.legendData,
top: 0,
textStyle: {
fontSize: 9,
color: '#666666'
},
formatter: function(name) {
return name.length > 12 ? name.substr(0, 12) + '...' : name
}
},
tooltip: data.tooltip
? data.tooltip
: {
trigger: 'axis'
},
xAxis: data.xAxis,
yAxis: data.yAxis,
dataZoom: data.dataZoom,
series: data.series
})
}
}
}
</script>
3 ECharts常用配置整理
例子参考
热力图参考例子:https://www.isqqw.com/viewer?id=31895
x轴 / y轴
xAxis: {
show: true, // 是否显示x轴
type: 'category', // 坐标轴类型,值category(类目轴)/value(数值轴),与y轴呼应,若x轴配置category则y轴配置value
inverse: false, // 是否是反向坐标轴
boundaryGap: ['20%', '20%'], // 坐标轴两边留白策略,也可以使用布尔值,默认true居中
axisLine: {
show: true, // 是否显示坐标轴轴线
},
axisTick: {
show: true, // 是否显示坐标轴刻度
alignWithLabel: true, //设置x轴刻度线与x轴文字对齐的
},
axisLabel: {
show: true, // 是否显示刻度标签
interval: '0', // 坐标轴刻度标签的显示间隔,在类目轴中有效.0显示所有
rotate: 90, // 刻度标签旋转的角度,在类目轴的类目标签显示不下的时候可以通过旋转防止标签之间重叠;旋转的角度从-90度到90度
margin: 10, // 刻度标签与轴线之间的距离
// formatter 刻度标签的内容格式器,支持字符串模板和回调函数两种形式
formatter: function (params) {
// 横坐标超长处理
let newParamsName = ''
const paramsNameNumber = params.length
const provideNumber = 15
const rowNumber = Math.ceil(paramsNameNumber / provideNumber)
if (paramsNameNumber > provideNumber) {
for (let p = 0; p < rowNumber; p++) {
let tempStr = ''
const start = p * provideNumber
const end = start + provideNumber
if (p == rowNumber - 1) {
tempStr = params.substring(start, paramsNameNumber)
} else {
tempStr = params.substring(start, end) + '\n'
}
newParamsName += tempStr
}
} else {
newParamsName = params
}
return newParamsName
},
color: '#FFF', // 刻度标签文字的颜色
fontStyle: 'normal', // 字体的风格(normal无样式;italic斜体;oblique倾斜字体)
// 字体的粗细(normal无样式;bold加粗;bolder加粗再加粗;lighter变细;数字定义粗细也可以取值范围100至700)
fontWeight: 'normal',
fontSize: '20', // 文字字体大小
align: 'left', // 文字水平对齐方式,默认自动(left/center/right)
verticalAlign: 'left', // 文字垂直对齐方式,默认自动(top/middle/bottom)
lineHeight: '50', // 行高
backgroundColor: 'red', // 文字块背景色,例:#123234, red, rgba(0,23,11,0.3)
},
splitLine: {
show: false // 是否显示网格线
}
},
legend 图例
legend: {
show: false
}
tooltip 提示框
tooltip: {
show: true, //是否显示提示框组件
trigger: 'axis', //触发类型,属性值:item数据项触发/axis坐标轴触发/none不触发
triggerOn: 'mousemove', //提示框触发条件,mousemove/click/mousemove|click/none。none时可通过action.tooltip.showTip和action.tooltip.hideTip来手动触发和隐藏。也可通过axisPointer.handle来触发或隐藏
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,属性值:line直线/shadow阴影/none/cross十字准星
},
padding: [5, 10],// 5
formatter: ()=>{}, //提示框浮层内容格式器,用这个可以修改提示框默认内容/
// formatter: '{a} <br/>{b} : {c} ({d}%)',
// formatter: (array) => {
// let str = ''
// if (array.length > 0) {
// array.forEach((v) => {
// str = `<span style="color:${v.color}">${str}${v.seriesName}: ${v.value}%</span><br />`
// })
// str = `${array[0].name}<br />${str}`
// }
// return str
// }
valueFormatter: (value: number | string) => string, //数值显示部分的格式化回调函数
// valueFormatter(value) {
// return `${value}%`
// },
}
series 数据项
series: [{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line', // 'bar'
symbol: 'none', //取消折点圆圈
smooth: true,
color: '#87a9d4',
tooltip: {
show: true,
valueFormatter(value) {
return `${value}%`
},
trigger: 'axis',
triggerOn: 'mousemove'
},
itemStyle: { color: '#c0e5ed' },//图形上的文本标签,可用于说明图形的一些数据信息
lineStyle: { // 线条样式
opacity: 0
},
markArea: { // 图表标域,常用于标记图表中某个范围的数据
itemStyle: {
color: '#c0e5ed'
},
data: [
{
xAxis: productStart,
itemStyle: { color: '#f9dec4' }
},
{
xAxis: productEnd
}
]
},
markPoint: { //标注打点
data: [
// {
// type: 'min',
// name: this.$t('maximumDrawdown'),
// label: {
// show: true,
// formatter: '{b}: {c}%'
// }
// }
{
xAxis: item.tradeDate,
yAxis: math.multiply(item.yield, 100),
label: {
name: this.$t('buyingAndSellingSignals'),
value: item.tradeDate,
show: true
},
itemStyle: {
color: item.adjustType === '1' ? '#ee6666' : '#91cc75'
}
}
],
symbolSize: 25,
itemStyle: {
// color: 'red'
},
tooltip: {}
// tooltip: {
// show: true,
// trigger: 'item', // 设置item才生效
// formatter(params) {
// if (buyDateArr.includes(params.data.xAxis)) {
// return params.data.xAxis + '</br>' + that.$t('signalBuy')
// } else if (sellDateArr.includes(params.data.xAxis)) {
// return params.data.xAxis + '</br>' + that.$t('signalSell')
// }
// }
// }
}
}]