282 lines
6.8 KiB
Vue
282 lines
6.8 KiB
Vue
<template>
|
|
<div class="polyline-chart">
|
|
<loading v-if="!status" />
|
|
|
|
<div class="canvas-container">
|
|
<canvas :ref="ref" />
|
|
</div>
|
|
|
|
<label-line :label="labelLine" :colors="drawColors" />
|
|
|
|
<for-slot><slot></slot></for-slot>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import colorsMixin from '../../mixins/colorsMixin.js'
|
|
|
|
import canvasMixin from '../../mixins/canvasMixin.js'
|
|
|
|
import axisMixin from '../../mixins/axisMixin.js'
|
|
|
|
export default {
|
|
name: 'PolylineChart',
|
|
mixins: [colorsMixin, canvasMixin, axisMixin],
|
|
props: ['data', 'labelLine', 'colors'],
|
|
data () {
|
|
return {
|
|
ref: `polyline-chart-${(new Date()).getTime()}`,
|
|
|
|
status: false,
|
|
|
|
// axis base config
|
|
boundaryGap: false,
|
|
mulValueAdd: false,
|
|
horizon: false,
|
|
|
|
defaultLineDash: [2, 2],
|
|
defaultPointRadius: 2,
|
|
|
|
defaultValueFontSize: 10,
|
|
defaultValueColor: '#999',
|
|
|
|
valuePointPos: []
|
|
}
|
|
},
|
|
watch: {
|
|
data (d) {
|
|
const { checkData, draw } = this
|
|
|
|
checkData() && draw()
|
|
}
|
|
},
|
|
methods: {
|
|
async init () {
|
|
const { initCanvas, initColors } = this
|
|
|
|
await initCanvas()
|
|
|
|
initColors()
|
|
|
|
const { checkData, draw } = this
|
|
|
|
checkData() && draw()
|
|
},
|
|
checkData () {
|
|
const { data } = this
|
|
|
|
this.status = false
|
|
|
|
if (!data || !data.series) return false
|
|
|
|
this.status = true
|
|
|
|
return true
|
|
},
|
|
draw () {
|
|
const { clearCanvas } = this
|
|
|
|
clearCanvas()
|
|
|
|
const { initAxisConfig, initAxis, drawAxis } = this
|
|
|
|
initAxisConfig()
|
|
|
|
initAxis()
|
|
|
|
drawAxis()
|
|
|
|
const { calcValuePointPos, drawLines, drawFills } = this
|
|
|
|
calcValuePointPos()
|
|
|
|
drawLines()
|
|
|
|
drawFills()
|
|
|
|
const { drawPoints, drawValues } = this
|
|
|
|
drawPoints()
|
|
|
|
drawValues()
|
|
},
|
|
initAxisConfig () {
|
|
const { data: { boundaryGap } } = this
|
|
|
|
this.boundaryGap = boundaryGap
|
|
},
|
|
calcValuePointPos () {
|
|
const { valueAxisMaxMin, axisOriginPos, axisWH, labelAxisTagPos, horizon } = this
|
|
|
|
const { data: { series }, getAxisPointsPos } = this
|
|
|
|
this.valuePointPos = series.map(({ value, againstAxis }) =>
|
|
getAxisPointsPos(
|
|
valueAxisMaxMin,
|
|
value,
|
|
axisOriginPos,
|
|
axisWH,
|
|
labelAxisTagPos,
|
|
horizon
|
|
))
|
|
},
|
|
drawLines () {
|
|
const { data: { series }, valuePointPos, drawLine } = this
|
|
|
|
series.forEach((line, i) => drawLine(line, valuePointPos[i], i))
|
|
},
|
|
drawLine ({ type, lineType, lineDash, lineColor }, points, i) {
|
|
const { ctx, drawColors, defaultLineDash } = this
|
|
|
|
const { canvas: { drawPolyline, drawSmoothline } } = this
|
|
|
|
const { color: { hexToRgb } } = this
|
|
|
|
let drawLineFun = drawPolyline
|
|
type === 'smoothline' && (drawLineFun = drawSmoothline)
|
|
|
|
let color = hexToRgb(drawColors[i], 0.8)
|
|
lineColor && (color = lineColor)
|
|
|
|
let tureLineType = lineType || 'line'
|
|
const tureLineDash = tureLineType === 'dashed' ? (lineDash || defaultLineDash) : [10, 0]
|
|
|
|
drawLineFun(ctx, points, 1, color, false, tureLineDash, true, true)
|
|
},
|
|
drawFills () {
|
|
const { data: { series }, valuePointPos, drawFill } = this
|
|
|
|
series.forEach((line, i) => drawFill(line, valuePointPos[i]))
|
|
},
|
|
drawFill ({ fillColor, type, value }, points) {
|
|
if (!fillColor) return
|
|
|
|
const { canvas: { drawPolylinePath, drawSmoothlinePath } } = this
|
|
|
|
const { ctx, getGradientColor, filterNull, axisOriginPos: [, y] } = this
|
|
|
|
let drawLineFun = drawPolylinePath
|
|
type === 'smoothline' && (drawLineFun = drawSmoothlinePath)
|
|
|
|
const maxValue = Math.max(...filterNull(value))
|
|
const maxValueIndex = value.findIndex(v => v === maxValue)
|
|
|
|
const color = getGradientColor(points[maxValueIndex], fillColor)
|
|
|
|
const lastPoint = points[points.length - 1]
|
|
|
|
ctx.fillStyle = color
|
|
|
|
drawLineFun(ctx, points, false, true, true)
|
|
|
|
ctx.lineTo(lastPoint[0], y)
|
|
ctx.lineTo(points[0][0], y)
|
|
|
|
ctx.closePath()
|
|
|
|
ctx.fill()
|
|
},
|
|
getGradientColor (value, colors) {
|
|
const { data: { localGradient }, axisAnglePos, horizon } = this
|
|
|
|
const { ctx, canvas: { getLinearGradientColor } } = this
|
|
|
|
if (localGradient) {
|
|
return getLinearGradientColor(ctx,
|
|
...(horizon
|
|
? [value, [axisAnglePos.leftTop[0], value[1]]]
|
|
: [value, [value[0], axisAnglePos.leftBottom[1]]]),
|
|
colors)
|
|
} else {
|
|
return getLinearGradientColor(ctx,
|
|
...(horizon
|
|
? [axisAnglePos.leftTop, axisAnglePos.rightTop]
|
|
: [axisAnglePos.leftTop, axisAnglePos.leftBottom]),
|
|
colors)
|
|
}
|
|
},
|
|
drawPoints () {
|
|
const { data: { series }, valuePointPos, drawPoint } = this
|
|
|
|
series.forEach((line, i) => drawPoint(line, valuePointPos[i], i))
|
|
},
|
|
drawPoint ({ pointColor }, points, i) {
|
|
const { ctx, drawColors, defaultPointRadius } = this
|
|
|
|
const { canvas: { drawPoints: drawPFun } } = this
|
|
|
|
const color = pointColor || drawColors[i]
|
|
|
|
drawPFun(ctx, points, defaultPointRadius, color)
|
|
},
|
|
drawValues () {
|
|
const { ctx, data: { series, showValueText, valueTextOffset, valueTextFontSize } } = this
|
|
|
|
if (!showValueText) return
|
|
|
|
const { defaultValueFontSize, drawValue } = this
|
|
|
|
const offset = valueTextOffset || [0, -5]
|
|
|
|
ctx.font = `${valueTextFontSize || defaultValueFontSize}px Arial`
|
|
|
|
ctx.textAlign = 'center'
|
|
ctx.textBaseline = 'bottom'
|
|
|
|
series.forEach((line, i) => drawValue(line, i, offset))
|
|
},
|
|
drawValue ({ value, valueTextColor, lineColor, pointColor, fillColor }, i, offset) {
|
|
const { ctx, getOffsetPoints, valuePointPos, drawColors, defaultValueColor } = this
|
|
|
|
const { data: { valueTextColor: outerValueTC } } = this
|
|
|
|
const drawColorsNum = drawColors.length
|
|
|
|
let currentColor = valueTextColor
|
|
currentColor === 'inherit' && (currentColor = pointColor || lineColor || fillColor || drawColors[i % drawColorsNum])
|
|
currentColor instanceof Array && (currentColor = currentColor[0])
|
|
|
|
ctx.fillStyle = currentColor || outerValueTC || defaultValueColor
|
|
|
|
const pointsPos = valuePointPos[i]
|
|
|
|
getOffsetPoints(pointsPos, offset).forEach((pos, i) => {
|
|
if (!value[i] && value[i] !== 0) return
|
|
|
|
ctx.fillText(value[i], ...pos)
|
|
})
|
|
},
|
|
getOffsetPoint ([x, y], [ox, oy]) {
|
|
return [x + ox, y + oy]
|
|
},
|
|
getOffsetPoints (points, offset) {
|
|
const { getOffsetPoint } = this
|
|
|
|
return points.map(point => point ? getOffsetPoint(point, offset) : false)
|
|
}
|
|
},
|
|
mounted () {
|
|
const { init } = this
|
|
|
|
init()
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="less">
|
|
.polyline-chart {
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
.canvas-container {
|
|
flex: 1;
|
|
|
|
canvas {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
}
|
|
}
|
|
</style>
|