2018-12-09 16:50:02 +08:00
|
|
|
<template>
|
|
|
|
<div class="concentric-arc-chart">
|
2019-01-16 16:56:11 +08:00
|
|
|
<loading v-if="!status" />
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
<div class="canvas-container">
|
|
|
|
<canvas :ref="ref" />
|
|
|
|
</div>
|
2018-12-09 16:50:02 +08:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2019-01-16 16:56:11 +08:00
|
|
|
import colorsMixin from '../../mixins/colorsMixin.js'
|
|
|
|
import canvasMixin from '../../mixins/canvasMixin.js'
|
|
|
|
|
2018-12-09 16:50:02 +08:00
|
|
|
export default {
|
|
|
|
name: 'ConcentricArcChart',
|
2019-01-16 16:56:11 +08:00
|
|
|
props: ['data', 'colors'],
|
|
|
|
mixins: [colorsMixin, canvasMixin],
|
2018-12-09 16:50:02 +08:00
|
|
|
data () {
|
|
|
|
return {
|
|
|
|
ref: `concentric-arc-chart-${(new Date()).getTime()}`,
|
2019-01-16 16:56:11 +08:00
|
|
|
|
|
|
|
status: false,
|
2018-12-09 16:50:02 +08:00
|
|
|
|
|
|
|
arcOriginPos: [],
|
|
|
|
|
|
|
|
defaultArcRadiusArea: [0.2, 0.8],
|
|
|
|
defaultArcGap: 3,
|
2018-12-11 14:11:12 +08:00
|
|
|
defaultArcColor: ['#00c0ff', '#3de7c9'],
|
2018-12-09 16:50:02 +08:00
|
|
|
|
|
|
|
arcRadius: [],
|
|
|
|
arcRadian: [],
|
|
|
|
arcLineWidth: 0,
|
|
|
|
arcColor: ''
|
|
|
|
}
|
|
|
|
},
|
|
|
|
watch: {
|
2019-01-16 16:56:11 +08:00
|
|
|
data () {
|
|
|
|
const { checkData, draw } = this
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
checkData() && draw()
|
2018-12-09 16:50:02 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
methods: {
|
2019-01-16 16:56:11 +08:00
|
|
|
async init () {
|
|
|
|
const { initCanvas, initColors, checkData, draw } = this
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
await initCanvas()
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
initColors()
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
checkData() && draw()
|
2018-12-09 16:50:02 +08:00
|
|
|
},
|
2019-01-16 16:56:11 +08:00
|
|
|
checkData () {
|
|
|
|
const { data } = this
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
this.status = false
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
if (!data || !data.series) return false
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
this.status = true
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
return true
|
2018-12-09 16:50:02 +08:00
|
|
|
},
|
|
|
|
draw () {
|
2019-01-16 16:56:11 +08:00
|
|
|
const { clearCanvas, calcArcRadius, calcArcRadian, calcArcColor, drawArc, drawTitle } = this
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
clearCanvas()
|
2018-12-09 16:50:02 +08:00
|
|
|
|
|
|
|
calcArcRadius()
|
|
|
|
|
|
|
|
calcArcRadian()
|
|
|
|
|
|
|
|
calcArcColor()
|
|
|
|
|
|
|
|
drawArc()
|
|
|
|
|
|
|
|
drawTitle()
|
|
|
|
},
|
|
|
|
calcArcRadius () {
|
2019-01-16 16:56:11 +08:00
|
|
|
const { data: { series, arcArea, arcGap }, centerPos, defaultArcRadiusArea, defaultArcGap } = this
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
const arcNum = series.length
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
const fullRadius = (centerPos[0] > centerPos[1] ? centerPos[1] : centerPos[0])
|
2018-12-09 16:50:02 +08:00
|
|
|
|
|
|
|
const currentArcArea = arcArea || defaultArcRadiusArea
|
|
|
|
|
|
|
|
const maxRadius = fullRadius * Math.max(...currentArcArea)
|
|
|
|
const minRadius = fullRadius * Math.min(...currentArcArea)
|
|
|
|
|
|
|
|
const currentArcGap = arcGap || defaultArcGap
|
|
|
|
|
|
|
|
const arcLineWidth = this.arcLineWidth = (maxRadius - minRadius - currentArcGap * (arcNum - 1)) / arcNum
|
|
|
|
|
|
|
|
const fullArcLineWidth = arcLineWidth + currentArcGap
|
|
|
|
const halfArcLineWidth = arcLineWidth / 2
|
|
|
|
|
|
|
|
this.arcRadius = new Array(arcNum).fill(0).map((t, i) => maxRadius - halfArcLineWidth - fullArcLineWidth * i)
|
|
|
|
},
|
|
|
|
calcArcRadian () {
|
2019-01-16 16:56:11 +08:00
|
|
|
const { data: { series } } = this
|
2018-12-09 16:50:02 +08:00
|
|
|
|
|
|
|
const fullRadian = Math.PI / 2 * 3
|
|
|
|
|
|
|
|
const offsetRadian = Math.PI * 0.5
|
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
this.arcRadian = new Array(series.length).fill(0).map((t, i) => series[i].value * fullRadian - offsetRadian)
|
2018-12-09 16:50:02 +08:00
|
|
|
},
|
|
|
|
calcArcColor () {
|
2018-12-11 14:11:12 +08:00
|
|
|
const { ctx, arcLineWidth, defaultArcColor, canvas: { getLinearGradientColor } } = this
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
const { drawColors, arcRadius: [ radius ], centerPos: [x, y] } = this
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
const colors = drawColors || defaultArcColor
|
2018-12-09 16:50:02 +08:00
|
|
|
|
2018-12-11 14:11:12 +08:00
|
|
|
this.arcColor = getLinearGradientColor(ctx,
|
|
|
|
[x, y - radius - arcLineWidth],
|
|
|
|
[x, y + radius + arcLineWidth], colors)
|
2018-12-09 16:50:02 +08:00
|
|
|
},
|
|
|
|
drawArc () {
|
2019-01-16 16:56:11 +08:00
|
|
|
const { ctx, arcRadius, arcRadian, centerPos, arcLineWidth, arcColor } = this
|
2018-12-09 16:50:02 +08:00
|
|
|
|
|
|
|
const offsetRadian = Math.PI / -2
|
|
|
|
|
|
|
|
ctx.lineWidth = arcLineWidth
|
|
|
|
ctx.strokeStyle = arcColor
|
|
|
|
|
|
|
|
arcRadius.forEach((radius, i) => {
|
|
|
|
ctx.beginPath()
|
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
ctx.arc(...centerPos, radius, offsetRadian, arcRadian[i])
|
2018-12-09 16:50:02 +08:00
|
|
|
|
|
|
|
ctx.stroke()
|
|
|
|
})
|
|
|
|
},
|
|
|
|
drawTitle () {
|
2019-01-16 16:56:11 +08:00
|
|
|
const { ctx, data: { series, fontSize }, arcRadius, centerPos: [ x, y ], arcLineWidth } = this
|
2018-12-09 16:50:02 +08:00
|
|
|
|
|
|
|
const textEndX = x - 10
|
|
|
|
|
|
|
|
ctx.font = `${fontSize || arcLineWidth}px Arial`
|
|
|
|
ctx.textAlign = 'right'
|
|
|
|
ctx.textBaseline = 'middle'
|
|
|
|
ctx.fillStyle = '#fff'
|
|
|
|
|
|
|
|
ctx.beginPath()
|
|
|
|
|
2019-01-16 16:56:11 +08:00
|
|
|
series.forEach(({ title }, i) => {
|
2018-12-09 16:50:02 +08:00
|
|
|
ctx.fillText(title, textEndX, y - arcRadius[i])
|
|
|
|
})
|
|
|
|
}
|
|
|
|
},
|
|
|
|
mounted () {
|
|
|
|
const { init } = this
|
|
|
|
|
|
|
|
init()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="less">
|
|
|
|
.concentric-arc-chart {
|
|
|
|
position: relative;
|
2019-01-16 16:56:11 +08:00
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
color: #fff;
|
|
|
|
|
|
|
|
.canvas-container {
|
|
|
|
position: relative;
|
|
|
|
flex: 1;
|
|
|
|
}
|
2018-12-09 16:50:02 +08:00
|
|
|
|
|
|
|
canvas {
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|