add radar chart and new decoration
This commit is contained in:
parent
0981b9e6e8
commit
8e06964260
|
@ -275,22 +275,24 @@ export default {
|
||||||
polyline[0] && drawPolyline(ctx, polyline, 2, color[i % colorNum], false, [10, 0], true))
|
polyline[0] && drawPolyline(ctx, polyline, 2, color[i % colorNum], false, [10, 0], true))
|
||||||
},
|
},
|
||||||
drawLabelText () {
|
drawLabelText () {
|
||||||
const { ctx, labelLinePoints, data: { data, labelFontSize }, totalValue, defaultLabelFontSize, arcOriginPos: [x] } = this
|
const { ctx, labelLinePoints, data: { data, labelFontSize, fixed }, totalValue, defaultLabelFontSize, arcOriginPos: [x] } = this
|
||||||
|
|
||||||
ctx.font = `${labelFontSize || defaultLabelFontSize}px Arial`
|
ctx.font = `${labelFontSize || defaultLabelFontSize}px Arial`
|
||||||
|
|
||||||
ctx.fillStyle = '#fff'
|
ctx.fillStyle = '#fff'
|
||||||
|
|
||||||
|
let totalPercent = 0
|
||||||
|
|
||||||
|
const dataLast = data.length - 1
|
||||||
|
|
||||||
data.forEach(({ value, title }, i) => {
|
data.forEach(({ value, title }, i) => {
|
||||||
if (!value && totalValue) return
|
if (!value && totalValue) return
|
||||||
|
|
||||||
let currentPercent = value / totalValue * 100
|
let currentPercent = (value / totalValue * 100).toFixed(fixed || 1)
|
||||||
|
|
||||||
currentPercent = (currentPercent > 1 ? Math.trunc(currentPercent) : currentPercent.toFixed(2))
|
i === dataLast && (currentPercent = (100 - totalPercent).toFixed(fixed || 1))
|
||||||
|
|
||||||
currentPercent += '%'
|
!totalValue && (currentPercent = 0)
|
||||||
|
|
||||||
!totalValue && (currentPercent = '0%')
|
|
||||||
|
|
||||||
const textPos = labelLinePoints[i][2]
|
const textPos = labelLinePoints[i][2]
|
||||||
|
|
||||||
|
@ -300,11 +302,13 @@ export default {
|
||||||
|
|
||||||
ctx.textBaseline = 'bottom'
|
ctx.textBaseline = 'bottom'
|
||||||
|
|
||||||
ctx.fillText(currentPercent, ...textPos)
|
ctx.fillText(`${currentPercent}%`, ...textPos)
|
||||||
|
|
||||||
ctx.textBaseline = 'top'
|
ctx.textBaseline = 'top'
|
||||||
|
|
||||||
ctx.fillText(title, ...textPos)
|
ctx.fillText(title, ...textPos)
|
||||||
|
|
||||||
|
totalPercent += Number(currentPercent)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
|
@ -0,0 +1,22 @@
|
||||||
|
<template>
|
||||||
|
<div class="decoration-5">
|
||||||
|
<img src="./img/decoration.gif" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Decoration5'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.decoration-5 {
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: -21%;
|
||||||
|
margin-bottom: -25%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,7 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<pre><code class="highlight-code" :ref="ref">
|
<div class="highlight-code">
|
||||||
|
<pre><code :ref="ref">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -41,7 +43,10 @@ export default {
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.highlight-code {
|
.highlight-code {
|
||||||
font-family: 'code';
|
|
||||||
background-color: transparent;
|
code {
|
||||||
|
font-family: 'code';
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -10,12 +10,14 @@ import decoration1 from './decoration1/index'
|
||||||
import decoration2 from './decoration2/index'
|
import decoration2 from './decoration2/index'
|
||||||
import decoration3 from './decoration3/index'
|
import decoration3 from './decoration3/index'
|
||||||
import decoration4 from './decoration4/index'
|
import decoration4 from './decoration4/index'
|
||||||
|
import decoration5 from './decoration5/index'
|
||||||
|
|
||||||
import capsuleChart from './capsuleChart/index.vue'
|
import capsuleChart from './capsuleChart/index.vue'
|
||||||
import ringChart from './ringChart/index.vue'
|
import ringChart from './ringChart/index.vue'
|
||||||
import polylineChart from './polylineChart/index.vue'
|
import polylineChart from './polylineChart/index.vue'
|
||||||
import concentricArcChart from './concentricArcChart/index.vue'
|
import concentricArcChart from './concentricArcChart/index.vue'
|
||||||
import arcRingChart from './arcRingChart/index.vue'
|
import arcRingChart from './arcRingChart/index.vue'
|
||||||
|
import radarChart from './radarChart/index.vue'
|
||||||
|
|
||||||
import numberShow from './numberShow/index.vue'
|
import numberShow from './numberShow/index.vue'
|
||||||
|
|
||||||
|
@ -33,15 +35,20 @@ export default function (Vue) {
|
||||||
Vue.component('borderBox5', borderBox5)
|
Vue.component('borderBox5', borderBox5)
|
||||||
Vue.component('borderBox6', borderBox6)
|
Vue.component('borderBox6', borderBox6)
|
||||||
Vue.component('borderBox7', borderBox7)
|
Vue.component('borderBox7', borderBox7)
|
||||||
|
|
||||||
Vue.component('decoration1', decoration1)
|
Vue.component('decoration1', decoration1)
|
||||||
Vue.component('decoration2', decoration2)
|
Vue.component('decoration2', decoration2)
|
||||||
Vue.component('decoration3', decoration3)
|
Vue.component('decoration3', decoration3)
|
||||||
Vue.component('decoration4', decoration4)
|
Vue.component('decoration4', decoration4)
|
||||||
|
Vue.component('decoration5', decoration5)
|
||||||
|
|
||||||
Vue.component('capsuleChart', capsuleChart)
|
Vue.component('capsuleChart', capsuleChart)
|
||||||
Vue.component('polylineChart', polylineChart)
|
Vue.component('polylineChart', polylineChart)
|
||||||
Vue.component('ringChart', ringChart)
|
Vue.component('ringChart', ringChart)
|
||||||
Vue.component('concentricArcChart', concentricArcChart)
|
Vue.component('concentricArcChart', concentricArcChart)
|
||||||
Vue.component('arcRingChart', arcRingChart)
|
Vue.component('arcRingChart', arcRingChart)
|
||||||
|
Vue.component('radarChart', radarChart)
|
||||||
|
|
||||||
Vue.component('numberShow', numberShow)
|
Vue.component('numberShow', numberShow)
|
||||||
Vue.component('scrollBoard', scrollBoard)
|
Vue.component('scrollBoard', scrollBoard)
|
||||||
Vue.component('loading', loading)
|
Vue.component('loading', loading)
|
||||||
|
|
|
@ -0,0 +1,289 @@
|
||||||
|
<template>
|
||||||
|
<div class="radar-chart">
|
||||||
|
|
||||||
|
<div class="canvas-container">
|
||||||
|
<canvas :ref="ref" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="label-line">
|
||||||
|
<div class="label-item"
|
||||||
|
v-for="(label, i) in data.labelLine"
|
||||||
|
:key="label">
|
||||||
|
<div :style="`background-color: ${colors[i % colors.length]};`"></div>
|
||||||
|
<div>{{ label }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'RadarChart',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
ref: `radar-chart-${(new Date()).getTime()}`,
|
||||||
|
canvasDom: '',
|
||||||
|
canvasWH: [0, 0],
|
||||||
|
ctx: '',
|
||||||
|
|
||||||
|
arcOriginPos: [],
|
||||||
|
|
||||||
|
defaultRadius: 0.8,
|
||||||
|
defaultCircleNum: 4,
|
||||||
|
defaultCircleColor: '#666',
|
||||||
|
defaultRayLineColor: '#666',
|
||||||
|
defaultRayLineOffset: Math.PI * -0.5,
|
||||||
|
defaultLabelColor: '#fff',
|
||||||
|
defaultLabelFS: 10,
|
||||||
|
|
||||||
|
radius: '',
|
||||||
|
rayLineRadianData: [],
|
||||||
|
valuePointData: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: ['data', 'colors'],
|
||||||
|
watch: {
|
||||||
|
data (d) {
|
||||||
|
const { reDraw } = this
|
||||||
|
|
||||||
|
reDraw(d)
|
||||||
|
},
|
||||||
|
color (d) {
|
||||||
|
const { reDraw } = this
|
||||||
|
|
||||||
|
reDraw(d)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
const { $nextTick, initCanvas, calcOriginPos, data, draw } = this
|
||||||
|
|
||||||
|
$nextTick(e => {
|
||||||
|
initCanvas()
|
||||||
|
|
||||||
|
calcOriginPos()
|
||||||
|
|
||||||
|
data && draw()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
initCanvas () {
|
||||||
|
const { $refs, ref, labelRef, canvasWH } = this
|
||||||
|
|
||||||
|
const canvas = this.canvasDom = $refs[ref]
|
||||||
|
|
||||||
|
this.labelDom = $refs[labelRef]
|
||||||
|
|
||||||
|
canvasWH[0] = canvas.clientWidth
|
||||||
|
canvasWH[1] = canvas.clientHeight
|
||||||
|
|
||||||
|
canvas.setAttribute('width', canvasWH[0])
|
||||||
|
canvas.setAttribute('height', canvasWH[1])
|
||||||
|
|
||||||
|
this.ctx = canvas.getContext('2d')
|
||||||
|
},
|
||||||
|
calcOriginPos () {
|
||||||
|
const { canvasWH, arcOriginPos } = this
|
||||||
|
|
||||||
|
arcOriginPos[0] = canvasWH[0] / 2
|
||||||
|
arcOriginPos[1] = canvasWH[1] / 2
|
||||||
|
},
|
||||||
|
draw () {
|
||||||
|
const { calcRadarRadius, drawRadarCircle, drawRayLine } = this
|
||||||
|
|
||||||
|
calcRadarRadius()
|
||||||
|
|
||||||
|
drawRadarCircle()
|
||||||
|
|
||||||
|
drawRayLine()
|
||||||
|
|
||||||
|
const { drawLable, caclValuePointData, fillRadar } = this
|
||||||
|
|
||||||
|
drawLable()
|
||||||
|
|
||||||
|
caclValuePointData()
|
||||||
|
|
||||||
|
fillRadar()
|
||||||
|
},
|
||||||
|
calcRadarRadius () {
|
||||||
|
const { canvasWH, data: { radius }, defaultRadius } = this
|
||||||
|
|
||||||
|
this.radius = Math.min(...canvasWH) * (radius || defaultRadius) * 0.5
|
||||||
|
},
|
||||||
|
drawRadarCircle () {
|
||||||
|
const { ctx, arcOriginPos, radius, data, defaultCircleNum, defaultCircleColor } = this
|
||||||
|
|
||||||
|
const { circleNum, circleColor } = data
|
||||||
|
|
||||||
|
const trueCircleNum = circleNum || defaultCircleNum
|
||||||
|
|
||||||
|
const gap = radius / trueCircleNum
|
||||||
|
|
||||||
|
ctx.strokeStyle = circleColor || defaultCircleColor
|
||||||
|
|
||||||
|
ctx.setLineDash([5, 5])
|
||||||
|
|
||||||
|
ctx.lineWidth = 1
|
||||||
|
|
||||||
|
new Array(trueCircleNum).fill(0).forEach((t, i) => {
|
||||||
|
ctx.beginPath()
|
||||||
|
|
||||||
|
ctx.arc(...arcOriginPos, gap * (i + 1), 0, Math.PI * 2)
|
||||||
|
|
||||||
|
ctx.stroke()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
drawRayLine () {
|
||||||
|
const { ctx, radius, arcOriginPos, data: td, defaultRayLineColor, defaultRayLineOffset, canvas } = this
|
||||||
|
|
||||||
|
const { label: { data }, rayLineColor, rayLineOffset } = td
|
||||||
|
|
||||||
|
const { getCircleRadianPoint } = canvas
|
||||||
|
|
||||||
|
const labelNum = data.length
|
||||||
|
|
||||||
|
const gapRadian = Math.PI * 2 / labelNum
|
||||||
|
|
||||||
|
const radianOffset = rayLineOffset || defaultRayLineOffset
|
||||||
|
|
||||||
|
ctx.strokeStyle = rayLineColor || defaultRayLineColor
|
||||||
|
|
||||||
|
ctx.lineWidth = 1
|
||||||
|
|
||||||
|
ctx.setLineDash([10, 0])
|
||||||
|
|
||||||
|
const rayLineRadianData = this.rayLineRadianData = []
|
||||||
|
|
||||||
|
new Array(labelNum).fill(0).forEach((t, i) => {
|
||||||
|
const currentRadian = gapRadian * (i + 1) + radianOffset
|
||||||
|
|
||||||
|
rayLineRadianData.push(currentRadian)
|
||||||
|
|
||||||
|
ctx.beginPath()
|
||||||
|
|
||||||
|
ctx.moveTo(...arcOriginPos)
|
||||||
|
|
||||||
|
ctx.lineTo(...getCircleRadianPoint(...arcOriginPos, radius, currentRadian))
|
||||||
|
|
||||||
|
ctx.stroke()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
drawLable () {
|
||||||
|
const { ctx, radius, arcOriginPos, canvas, data: td, defaultLabelColor, defaultLabelFS, rayLineRadianData } = this
|
||||||
|
|
||||||
|
const { label: { data, color, fontSize } } = td
|
||||||
|
|
||||||
|
const { getCircleRadianPoint } = canvas
|
||||||
|
|
||||||
|
const endRadius = radius + 10
|
||||||
|
|
||||||
|
ctx.font = `${fontSize || defaultLabelFS}px Arial`
|
||||||
|
|
||||||
|
ctx.fillStyle = color || defaultLabelColor
|
||||||
|
|
||||||
|
ctx.textBaseline = 'middle'
|
||||||
|
|
||||||
|
data.forEach((label, i) => {
|
||||||
|
const currentEndPointPos = getCircleRadianPoint(...arcOriginPos, endRadius, rayLineRadianData[i])
|
||||||
|
|
||||||
|
ctx.textAlign = 'start'
|
||||||
|
|
||||||
|
currentEndPointPos[0] < arcOriginPos[0] && (ctx.textAlign = 'end')
|
||||||
|
|
||||||
|
ctx.fillText(label, ...currentEndPointPos)
|
||||||
|
|
||||||
|
ctx.fill()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
caclValuePointData () {
|
||||||
|
const { data: { data }, arcOriginPos, radius, canvas, rayLineRadianData } = this
|
||||||
|
|
||||||
|
const { getCircleRadianPoint } = canvas
|
||||||
|
|
||||||
|
const maxValue = Math.max(...data.map(({ data: td }) => Math.max(...td)))
|
||||||
|
|
||||||
|
const valueRadius = data.map(({ data: td }) =>
|
||||||
|
td.map(value =>
|
||||||
|
Number.isFinite(value)
|
||||||
|
? value / maxValue * radius : false))
|
||||||
|
|
||||||
|
this.valuePointData = valueRadius.map(td =>
|
||||||
|
td.map((r, i) =>
|
||||||
|
r ? getCircleRadianPoint(...arcOriginPos, r, rayLineRadianData[i]) : false))
|
||||||
|
},
|
||||||
|
fillRadar () {
|
||||||
|
const { ctx, data: { data }, valuePointData, colors, canvas, color, filterNull } = this
|
||||||
|
|
||||||
|
const { drawPolylinePath } = canvas
|
||||||
|
|
||||||
|
const { hexToRgb } = color
|
||||||
|
|
||||||
|
const colorNum = colors.length
|
||||||
|
|
||||||
|
valuePointData.forEach((line, i) => {
|
||||||
|
const lineColor = data[i].color || colors[i % colorNum]
|
||||||
|
|
||||||
|
data[i].dashed ? ctx.setLineDash([5, 5]) : ctx.setLineDash([10, 0])
|
||||||
|
|
||||||
|
drawPolylinePath(ctx, filterNull(line), 1, true, true)
|
||||||
|
|
||||||
|
ctx.strokeStyle = lineColor
|
||||||
|
|
||||||
|
ctx.stroke()
|
||||||
|
|
||||||
|
ctx.fillStyle = hexToRgb(lineColor, 0.5)
|
||||||
|
|
||||||
|
ctx.fill()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
reDraw (d) {
|
||||||
|
const { draw } = this
|
||||||
|
|
||||||
|
d && draw()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
const { init } = this
|
||||||
|
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.radar-chart {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.canvas-container {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-line {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 10px;
|
||||||
|
|
||||||
|
.label-item {
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0px 5px 5px 5px;
|
||||||
|
|
||||||
|
:nth-child(1) {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue