引言
有在移动端实现光滑曲线图的需求,选择了AntV的库,但官方文档不太行网上案例更少,找起来很麻烦。就把自己做的记录下来,主要是麻烦在AntV的官方文档写得不行,不好查找,给的官方案例又简单又少,api介绍的又比较混乱
我这里主要涉及到对曲线图上的点及点标签的自定义样式
legend不想查组件库的api,所以自己拿div拼了一个
还有就是chart的刷新及传值要深拷贝问题
另外chart有id,这id一定要是唯一的,否则会互相影响
效果
需求
- 光滑曲线
- 最低点绿色标签,绿色点;最高点红色标签,红色点;其余灰色(主要是自定义标签需要回调函数麻烦一些)
- 无Y轴
- 百分数/数字的分别处理
- 数据更新
使用代码
项目是vue的,封装成了一个组件,在别的地方可以直接用,使用如下
<line-chart title="实际趋势图" :infos="JSON.parse(JSON.stringify(history))" id="chart1" type="1"></line-chart>
- title: 图的标题
- id: 一定要有个id,并且父组件用的时候不同图用不同的id,否则会互相影响如果id是变量,则写成加:id=“idname”
- infos: 数据,一定要用深拷贝,不然子组件watch不到传过来的数据的变化,watch的只是引用地址
- type: 0小标题 1大标题 这里是做了两个样式的处理
数据格式
传过去的数据格式如下
history: [
{
name: 'W21',
value: '40'
}, {
name: 'W22',
value: '38'
}, {
name: 'W23',
value: '64'
}, {
name: 'W24',
value: '56'
}, {
name: 'W25',
value: '44'
}, {
name: 'W26',
value: '77.3'
}
],
同样也支持百分数,如下
history: [
{
name: 'W21',
value: '40%'
}, {
name: 'W22',
value: '38%'
}, {
name: 'W23',
value: '64%'
}, {
name: 'W24',
value: '56%'
}, {
name: 'W25',
value: '44%'
}, {
name: 'W26',
value: '77.3%'
}
],
组件代码
我template写的是pug,css写的是scss
<!--详情页使用-->
<!--趋势图组件-->
<!--传参type 0:小标题 1:大标题-->
<template lang="pug">
div.line-chart(:class="lineChartClass")
div.chart-title(:class="titleClass") {{title}}
div.chart-legend
div.up-icon.legend-icon
div.legend-text 最高值
div.down-icon.legend-icon
div.legend-text 最低值
div.chart-content(:id="id")
</template>
<script>
export default {
name: 'line-chart',
props: {
id: String,
title: String,
infos: {
type: Array,
default () {
return [{}]
}
},
type: String
},
data () {
return {
pointColor: [],
maxValue: null,
minValue: null
}
},
computed: {
lineChartClass () {
if (this.type == 1) { return 'big-line-chart' } else {
return null
}
},
titleClass () {
if (this.type == 1) { return 'big-title' } else {
return null
}
},
chartData () {
return this.infos.map((item) => {
if (item.value.indexOf('%') >= 0) { // 有百分号
return {
name: item.name,
value: Number(item.value.replace('%', '')),
type: 1 // 值类型 0:值,1:百分数
}
} else {
return {
name: item.name,
value: Number(item.value),
type: 0 // 值类型 0:值,1:百分数
}
}
})
}
},
watch: {
infos (val) {
if (!this.$chart) {
this.initChart()
} else {
this.$chart.destroy()
this.initChart()
}
}
},
mounted () {
this.initChart()
},
methods: {
initChart () {
this.calMinMax()
this.creatChart()
},
calMinMax () {
let valueArray = this.chartData.map((item) => {
return item.value
})
valueArray.sort((a, b) => {
return a - b
})
this.minValue = valueArray[0]
this.maxValue = valueArray[valueArray.length - 1]
},
creatChart () {
this.$chart = new window.G2.Chart({
container: this.id,
forceFit: true,
height: 200,
width: 450
})
this.$chart.source(this.chartData)
// this.$chart.tooltip({
// crosshairs: {
// type: 'line'
// }
// })
this.$chart.legend(false)// 不要legend
this.$chart.tooltip(false)
this.$chart.axis('value', false)// 不要y轴
this.$chart.axis('name', {
line: {
stroke: '#EEEEEE'
},
label: {
textStyle: {
textAlign: 'center', // 文本对齐方向,可取值为: start center end
fill: '#666666', // 文本的颜色
fontSize: '12' // 文本大小
}
},
tickLine: null
})
this.$chart.line().position('name*value').shape('smooth')// 曲线图
this.$chart.point().position('name*value').size(3).color('value', (value) => { // 多个参数,通过回调函数
if (value === this.minValue) {
return '#389E0D'
} else if (value === this.maxValue) {
return '#F5212D'
} else {
return '#666666'
}
}).shape('circle').style({
// stroke: '#fff',
// lineWidth: 1
}).label('name*value', {
htmlTemplate: (name, value) => {
let color
const pretext = '<div style="background-color: '
const midtext = `;color:#FFFFFF;
border-radius: 2px; margin-top: 12px;
font-size:10px; font-family:PingFangSC-Medium;
padding: 2px 4px 2px 4px; transform: scale(.9);">
<span style="display: block; transform: scale(.95)">`
const suftext = '</span></div>'
if (value.point.value == this.minValue) {
color = '#389E0D'
if (value.point.type == 1) { // 百分数时
return `${pretext}${color}${midtext}${value.point.value}%${suftext}`
} else { // 数值时
return `${pretext}${color}${midtext}${value.point.value}${suftext}`
}
} else if (value.point.value == this.maxValue) {
color = '#F5212D'
if (value.point.type == 1) {
return `${pretext}${color}${midtext}${value.point.value}%${suftext}`
} else {
return `${pretext}${color}${midtext}${value.point.value}${suftext}`
}
} else {
color = '#666'
if (value.point.type == 1) {
return `${pretext}${color}${midtext}${value.point.value}%${suftext}`
} else {
return `${pretext}${color}${midtext}${value.point.value}${suftext}`
}
}
} })
this.$chart.render()
}
}
}
</script>
<style scoped lang="scss">
.big-line-chart{
height: 380px!important;
}
.big-title{
color: #222222!important;
font-size: 32px!important;
font-family: PingFangSC-Medium!important;
line-height: 32px!important;
}
.line-chart{
height: 370px;
background-color: white;
width: 690px;
padding-bottom: 20px;
.chart-title {
color: #666666;
font-size: 20px;
text-align: left;
margin-left: 24px;
padding-top: 24px;
}
.chart-legend{
margin-left: 24px;
display: flex;
align-items: center;
justify-content: left;
margin-top: 20px;
margin-bottom: 14px;
.legend-icon{
display: inline-block;
width: 10px;
height: 10px;
border-radius: 5px;
}
.legend-text{
display: inline-block;
color: #999999;
font-size: 20px;
}
.up-icon{
background-color: #F5212D;
margin-right: 10px;
}
.down-icon{
background-color: #389E0D;
margin: 0 10px 0 10px;
}
}
.max-color{
color: #F5212D;
}
.min-color{
color: #389E0D;
}
.chart-content{
margin-left: -124px;
}
}
</style>