diff --git a/src/components/polylineChart/index.vue b/src/components/polylineChart/index.vue index aa867f0..c1d443f 100644 --- a/src/components/polylineChart/index.vue +++ b/src/components/polylineChart/index.vue @@ -14,18 +14,31 @@ export default { props: ['data'], data () { return { - ref: `ring-chart-${(new Date()).getTime()}`, + ref: `polyline-chart-${(new Date()).getTime()}`, canvasDom: '', canvasWH: [0, 0], - ctx: '' + ctx: '', + + defaultFontSize: 10, + defaultAxisColor: 'rgba(250, 250, 250, 0.25)', + + xAxisData: [], + yAxisData: [], + axisWH: [], + axisOriginPos: [], + yAxisMinMax: [], + rowColumnSize: [], + valuePointsData: [] } }, methods: { init () { - const { $nextTick, initCanvas } = this + const { $nextTick, initCanvas, data, draw } = this $nextTick(e => { initCanvas() + + data && draw() }) }, initCanvas () { @@ -40,6 +53,170 @@ export default { canvas.setAttribute('height', canvasWH[1]) this.ctx = canvas.getContext('2d') + }, + draw () { + const { ctx, canvasWH, getXAxisData, getYAxisData, calcAxisPos, drawAxis } = this + + ctx.clearRect(0, 0, ...canvasWH) + + getXAxisData() + + getYAxisData() + + calcAxisPos() + + drawAxis() + + const { calcValuePointsData, drawValuePolyline, drawValueSmoothline, drawValuePoints } = this + + calcValuePointsData() + + drawValuePolyline() + + drawValueSmoothline() + + drawValuePoints() + }, + getXAxisData () { + const { data: { x: { data } } } = this + + this.xAxisData = data + }, + getYAxisData () { + let { data: { data, y: { data: yAxisData, max, min, num, fixed } }, filterNull, yAxisMinMax } = this + + if (yAxisData) { + this.yAxisData = yAxisData + + return + } + + let [unsetMax, unsetMin] = [false, false] + + !max && (max = Math.max(...data.map(({ data: td }) => Math.max(...td)))) && (unsetMax = true) + !min && (min = Math.min(...data.map(({ data: td }) => Math.min(...filterNull(td))))) && (unsetMin = true) + + let minus = max - min + + unsetMax && (max += minus / 2) && (max = Math.ceil(max)) + unsetMin && (min -= minus / 2) && (min = Math.floor(min)) + + yAxisMinMax[0] = min + yAxisMinMax[1] = max + + minus = max - min + + !num && (num = minus < 10 ? minus : 10) + + const gapNum = minus / (num - 1) + + this.yAxisData = new Array(num).fill(0).map((t, i) => (max - gapNum * i).toFixed(fixed || 0)).reverse() + }, + calcAxisPos () { + const { ctx, canvasWH, xAxisData, yAxisData, axisWH, axisOriginPos, rowColumnSize, data } = this + + const { x: { unit: xUT, offset: xOF }, y: { unit: yUT, fontSize: yFS, offset: yOF }, boundaryGap } = data + + ctx.font = `${yFS}px Arial` + + let yAxisMaxWidth = Math.max(...yAxisData.map(v => ctx.measureText(v).width)) + + const yAxisUnitWidth = ctx.measureText(yUT || '').width + + yAxisMaxWidth < yAxisUnitWidth && (yAxisMaxWidth = yAxisUnitWidth) + + const yAxisRowHeight = (canvasWH[1] - (xOF || 20)) / (yAxisData.length + 1) + const xAxisColumnWidth = (canvasWH[0] - (yOF || (yAxisMaxWidth + 10)) - ctx.measureText(xUT || '').width - 10) / (xAxisData.length + 1) + + axisWH[0] = xAxisColumnWidth * xAxisData.length + axisWH[1] = yAxisRowHeight * yAxisData.length + + rowColumnSize[0] = axisWH[1] / (yAxisData.length - 1) + rowColumnSize[1] = axisWH[0] / (boundaryGap ? xAxisData.length : xAxisData.length - 1) + + axisOriginPos[0] = (yOF || yAxisMaxWidth) + 10 + axisOriginPos[1] = canvasWH[1] - (xOF || 20) + }, + drawAxis () { + const { ctx, xAxisData, yAxisData, axisOriginPos, axisWH, canvasWH } = this + + const { defaultAxisColor, rowColumnSize, defaultFontSize, data: { x, y, boundaryGap } } = this + + const { unit: xUT, lineColor: xLC, color: xC, fontSize: xFS } = x + + const { unit: yUT, lineColor: yLC, color: yC, fontSize: yFS } = y + + const { canvas: { drawLine } } = this + + drawLine(ctx, axisOriginPos, [axisOriginPos[0] + axisWH[0], axisOriginPos[1]], 1, (xLC || defaultAxisColor)) + drawLine(ctx, axisOriginPos, [axisOriginPos[0], axisOriginPos[1] - axisWH[1]], 1, (yLC || defaultAxisColor)) + + ctx.fillStyle = xC || 'rgba(250, 250, 250, 0.6)' + + ctx.font = `${xFS || defaultFontSize}px Microsoft Yahei` + + ctx.textAlign = 'center' + ctx.textBaseline = 'top' + + const columnWidth = rowColumnSize[1] + + const columnOffset = boundaryGap ? columnWidth / 2 : 0 + + xAxisData.forEach((text, i) => ctx.fillText(text, axisOriginPos[0] + (i * columnWidth) + columnOffset, axisOriginPos[1] + 3)) + xUT && (ctx.textAlign = 'end') && ctx.fillText(xUT, canvasWH[0], axisOriginPos[1] + 3) + + ctx.font = `${yFS || defaultFontSize}px Microsoft Yahei` + + ctx.fillStyle = yC || 'rgba(250, 250, 250, 0.6)' + + ctx.textAlign = 'right' + ctx.textBaseline = 'middle' + + const rowHeight = rowColumnSize[0] + + yAxisData.forEach((text, i) => ctx.fillText(text, axisOriginPos[0] - 7, axisOriginPos[1] - (i * rowHeight))) + yUT && (ctx.textBaseline = 'top') && ctx.fillText(yUT, axisOriginPos[0] - 7, 0) + }, + calcValuePointsData () { + const { axisWH, axisOriginPos, yAxisMinMax: [min, max], rowColumnSize } = this + + const { data: { data, x, boundaryGap } } = this + + const columnWidth = rowColumnSize[1] + + const xOffset = (boundaryGap ? columnWidth / 2 : 0) + + const xPos = new Array(x.data.length).fill(0).map((v, i) => axisOriginPos[0] + (columnWidth * i) + xOffset) + + const minus = max - min + + this.valuePointsData = + data.map(({ data: td }) => + td.map((v, i) => + Number.isFinite(v) + ? [xPos[i], axisOriginPos[1] - (v - min) / minus * axisWH[1]] + : false)) + }, + drawValuePolyline () { + const { ctx, valuePointsData, canvas: { drawPolyline }, color: { hexToRgb } } = this + + const { data: { data, color } } = this + + const colorNum = color.length + + data.forEach(({ lineColor, dashed, type }, i) => type !== 'smoothline' && + drawPolyline(ctx, valuePointsData[i], 1, + hexToRgb(lineColor || color[i % colorNum], 0.8), false, (dashed ? [5, 5] : [10, 0]))) + }, + drawValueSmoothline () { + + }, + drawValuePoints () { + const { ctx, valuePointsData, data: { data, color }, canvas: { drawPoints } } = this + + const colorNum = color.length + + data.forEach(({ pointColor }, i) => drawPoints(ctx, valuePointsData[i], 4, pointColor || color[i % colorNum])) } }, mounted () {