update for v 2.0.0
|
@ -0,0 +1,26 @@
|
||||||
|
# 2.0.0-alpha (2019-06-25)
|
||||||
|
|
||||||
|
### Perfect
|
||||||
|
|
||||||
|
- **SVG:** Refactoring **borderbox** and **decoration** components with SVG (Volume dropped to 6%).
|
||||||
|
- **charts:** New charting component that supports **animation**.
|
||||||
|
- **compatibility:** Better data compatibility.
|
||||||
|
|
||||||
|
### New
|
||||||
|
|
||||||
|
- **digitalFlop**
|
||||||
|
- **flylineChart**
|
||||||
|
- **borderBox**
|
||||||
|
- **decoration**
|
||||||
|
|
||||||
|
### Components
|
||||||
|
|
||||||
|
- **prefix:** Components add **dv** prefix to prevent conflicts.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- v 1.x.x -->
|
||||||
|
<decoration-1 />
|
||||||
|
|
||||||
|
<!-- v 2.x.x -->
|
||||||
|
<dv-decoration-1 />
|
||||||
|
```
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2019-present, JiaMing
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
25
README.md
|
@ -1,15 +1,20 @@
|
||||||
# DataV
|
<h1 align="center">DataV</h1>
|
||||||
|
|
||||||
# Version 1.1.1
|
<p align="center">
|
||||||
|
<a href="https://github.com/jiaming743/datav/blob/master/LICENSE">
|
||||||
|
<img src="https://img.shields.io/github/license/jiaming743/datav.svg" alt="LICENSE" />
|
||||||
|
</a>x
|
||||||
|
<a href="https://www.npmjs.com/package/@jiaminghi/datav">
|
||||||
|
<img src="https://img.shields.io/npm/v/@jiaminghi/data-view.svg" alt="LICENSE" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
## Vue 大屏数据展示组件库
|
<h2 align="center">Vue large screen data display component library</h2>
|
||||||
|
|
||||||
## Vue Large screen data display component library
|
### Install with npm
|
||||||
|
|
||||||
[文档官网](http://datav.jiaminghi.com/)
|
```shell
|
||||||
|
$ npm install @jiaminghi/data-view
|
||||||
|
```
|
||||||
|
|
||||||
[Document of official website](http://datav.jiaminghi.com/)
|
Detailed documents and examples can be viewed on the [HomePage](http://datav.jiaminghi.com).
|
||||||
|
|
||||||
[项目地址](https://github.com/jiaming743/DataV)
|
|
||||||
|
|
||||||
[Project address](https://github.com/jiaming743/DataV)
|
|
||||||
|
|
|
@ -0,0 +1,302 @@
|
||||||
|
<template>
|
||||||
|
<div class="dv-active-ring-chart">
|
||||||
|
<div class="active-ring-chart-container" ref="active-ring-chart" />
|
||||||
|
<div class="active-ring-info">
|
||||||
|
<dv-digital-flop :config="digitalFlop" />
|
||||||
|
<div class="active-ring-name" :style="fontSize">{{ ringName }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Charts from '@jiaminghi/charts'
|
||||||
|
|
||||||
|
import dvDigitalFlop from '../digitalFlop'
|
||||||
|
|
||||||
|
import { deepMerge } from '@jiaminghi/charts/lib/util/index'
|
||||||
|
|
||||||
|
import { deepClone } from '@jiaminghi/c-render/lib/plugin/util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ActiveRingChart',
|
||||||
|
components: {
|
||||||
|
dvDigitalFlop
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
config: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
defaultConfig: {
|
||||||
|
/**
|
||||||
|
* @description Ring radius
|
||||||
|
* @type {String|Number}
|
||||||
|
* @default radius = '50%'
|
||||||
|
* @example radius = '50%' | 100
|
||||||
|
*/
|
||||||
|
radius: '50%',
|
||||||
|
/**
|
||||||
|
* @description Active ring radius
|
||||||
|
* @type {String|Number}
|
||||||
|
* @default activeRadius = '55%'
|
||||||
|
* @example activeRadius = '55%' | 110
|
||||||
|
*/
|
||||||
|
activeRadius: '55%',
|
||||||
|
/**
|
||||||
|
* @description Ring data
|
||||||
|
* @type {Array}
|
||||||
|
* @default data = []
|
||||||
|
*/
|
||||||
|
data: [],
|
||||||
|
/**
|
||||||
|
* @description Ring line width
|
||||||
|
* @type {Number}
|
||||||
|
* @default lineWidth = 20
|
||||||
|
*/
|
||||||
|
lineWidth: 20,
|
||||||
|
/**
|
||||||
|
* @description Active time gap (ms)
|
||||||
|
* @type {Number}
|
||||||
|
* @default activeTimeGap = 3000
|
||||||
|
*/
|
||||||
|
activeTimeGap: 3000,
|
||||||
|
/**
|
||||||
|
* @description Ring color (hex|rgb|rgba)
|
||||||
|
* @type {Array<String>}
|
||||||
|
* @default color = [Charts Default Color]
|
||||||
|
*/
|
||||||
|
color: [],
|
||||||
|
/**
|
||||||
|
* @description Digital flop style
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
digitalFlopStyle: {
|
||||||
|
fontSize: 25,
|
||||||
|
fill: '#fff'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description CRender animationCurve
|
||||||
|
* @type {String}
|
||||||
|
* @default animationCurve = 'easeOutCubic'
|
||||||
|
*/
|
||||||
|
animationCurve: 'easeOutCubic',
|
||||||
|
/**
|
||||||
|
* @description CRender animationFrame
|
||||||
|
* @type {String}
|
||||||
|
* @default animationFrame = 50
|
||||||
|
*/
|
||||||
|
animationFrame: 50
|
||||||
|
},
|
||||||
|
|
||||||
|
mergedConfig: null,
|
||||||
|
|
||||||
|
chart: null,
|
||||||
|
|
||||||
|
activeIndex: 0,
|
||||||
|
|
||||||
|
animationHandler: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
digitalFlop () {
|
||||||
|
const { mergedConfig, activeIndex } = this
|
||||||
|
|
||||||
|
if (!mergedConfig) return {}
|
||||||
|
|
||||||
|
const { digitalFlopStyle, data } = mergedConfig
|
||||||
|
|
||||||
|
const value = data.map(({ value }) => value)
|
||||||
|
|
||||||
|
const sum = value.reduce((all, v) => all + v, 0)
|
||||||
|
|
||||||
|
const percent = parseInt(value[activeIndex] / sum * 100)
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: '{nt}%',
|
||||||
|
number: [percent],
|
||||||
|
style: digitalFlopStyle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ringName () {
|
||||||
|
const { mergedConfig, activeIndex } = this
|
||||||
|
|
||||||
|
if (!mergedConfig) return ''
|
||||||
|
|
||||||
|
return mergedConfig.data[activeIndex].name
|
||||||
|
},
|
||||||
|
fontSize () {
|
||||||
|
const { mergedConfig, activeIndex } = this
|
||||||
|
|
||||||
|
if (!mergedConfig) return ''
|
||||||
|
|
||||||
|
return `font-size: ${mergedConfig.digitalFlopStyle.fontSize}px;`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
config () {
|
||||||
|
const { animationHandler, mergeConfig, setRingOption } = this
|
||||||
|
|
||||||
|
clearTimeout(animationHandler)
|
||||||
|
|
||||||
|
this.activeIndex = 0
|
||||||
|
|
||||||
|
mergeConfig()
|
||||||
|
|
||||||
|
setRingOption()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
const { initChart, mergeConfig, setRingOption } = this
|
||||||
|
|
||||||
|
initChart()
|
||||||
|
|
||||||
|
mergeConfig()
|
||||||
|
|
||||||
|
setRingOption()
|
||||||
|
},
|
||||||
|
initChart () {
|
||||||
|
const { $refs } = this
|
||||||
|
|
||||||
|
this.chart = new Charts($refs['active-ring-chart'])
|
||||||
|
},
|
||||||
|
mergeConfig () {
|
||||||
|
const { defaultConfig, config } = this
|
||||||
|
|
||||||
|
this.mergedConfig = deepMerge(deepClone(defaultConfig, true), config || {})
|
||||||
|
},
|
||||||
|
setRingOption () {
|
||||||
|
const { getRingOption, chart, ringAnimation } = this
|
||||||
|
|
||||||
|
const option = getRingOption()
|
||||||
|
|
||||||
|
chart.setOption(option)
|
||||||
|
|
||||||
|
ringAnimation()
|
||||||
|
},
|
||||||
|
getRingOption () {
|
||||||
|
const { mergedConfig, getRealRadius, chart } = this
|
||||||
|
|
||||||
|
const radius = getRealRadius()
|
||||||
|
|
||||||
|
mergedConfig.data.forEach(dataItem => {
|
||||||
|
dataItem.radius = radius
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
...mergedConfig,
|
||||||
|
outsideLabel: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getRealRadius (active = false) {
|
||||||
|
const { mergedConfig, chart } = this
|
||||||
|
|
||||||
|
const { radius, activeRadius, lineWidth } = mergedConfig
|
||||||
|
|
||||||
|
const maxRadius = Math.min(...chart.render.area) / 2
|
||||||
|
|
||||||
|
const halfLineWidth = lineWidth / 2
|
||||||
|
|
||||||
|
let realRadius = active ? activeRadius : radius
|
||||||
|
|
||||||
|
if (typeof realRadius !== 'number') realRadius = parseInt(realRadius) / 100 * maxRadius
|
||||||
|
|
||||||
|
const insideRadius = realRadius - halfLineWidth
|
||||||
|
const outSideRadius = realRadius + halfLineWidth
|
||||||
|
|
||||||
|
return [insideRadius, outSideRadius]
|
||||||
|
},
|
||||||
|
ringAnimation () {
|
||||||
|
let { animation, activeIndex, getRingOption, chart, getRealRadius } = this
|
||||||
|
|
||||||
|
const radius = getRealRadius()
|
||||||
|
const active = getRealRadius(true)
|
||||||
|
|
||||||
|
const option = getRingOption()
|
||||||
|
|
||||||
|
const { data } = option.series[0]
|
||||||
|
|
||||||
|
data.forEach((dataItem, i) => {
|
||||||
|
if (i === activeIndex) {
|
||||||
|
dataItem.radius = active
|
||||||
|
} else {
|
||||||
|
dataItem.radius = radius
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
chart.setOption(option)
|
||||||
|
|
||||||
|
const { activeTimeGap } = option.series[0]
|
||||||
|
|
||||||
|
this.animationHandler = setTimeout(foo => {
|
||||||
|
activeIndex += 1
|
||||||
|
|
||||||
|
if (activeIndex >= data.length) activeIndex = 0
|
||||||
|
|
||||||
|
this.activeIndex = activeIndex
|
||||||
|
|
||||||
|
this.ringAnimation()
|
||||||
|
}, activeTimeGap)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
const { init } = this
|
||||||
|
|
||||||
|
init()
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
const { animationHandler } = this
|
||||||
|
|
||||||
|
clearTimeout(animationHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.dv-active-ring-chart {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.active-ring-chart-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-ring-info {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.dv-digital-flop {
|
||||||
|
width: 100px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-ring-name {
|
||||||
|
width: 100px;
|
||||||
|
height: 30px;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,339 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="arc-ring-chart">
|
|
||||||
<loading v-if="!status" />
|
|
||||||
|
|
||||||
<div class="canvas-container">
|
|
||||||
<canvas :ref="ref" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label-line :label="dealAfterLabelLine" :colors="drawColors" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import colorsMixin from '../../mixins/colorsMixin.js'
|
|
||||||
import canvasMixin from '../../mixins/canvasMixin.js'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ArcRingChart',
|
|
||||||
props: ['data', 'labelLine', 'colors'],
|
|
||||||
mixins: [colorsMixin, canvasMixin],
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
ref: `concentric-arc-chart-${(new Date()).getTime()}`,
|
|
||||||
|
|
||||||
status: false,
|
|
||||||
|
|
||||||
defaultDecorationCircleRadius: 0.65,
|
|
||||||
defaultArcRadiusArea: [0.3, 0.4],
|
|
||||||
defaultArcWidthArea: [2, 10],
|
|
||||||
defaultLabelFontSize: 12,
|
|
||||||
|
|
||||||
decorationRadius: '',
|
|
||||||
|
|
||||||
radianOffset: Math.PI / -2,
|
|
||||||
|
|
||||||
totalValue: 0,
|
|
||||||
|
|
||||||
arcRadius: [],
|
|
||||||
arcRadian: [],
|
|
||||||
arcWidth: [],
|
|
||||||
|
|
||||||
labelLinePoints: [],
|
|
||||||
|
|
||||||
dealAfterLabelLine: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
data (d) {
|
|
||||||
const { checkData, draw } = this
|
|
||||||
|
|
||||||
checkData() && draw()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async init () {
|
|
||||||
const { initCanvas, initColors, checkData, draw } = this
|
|
||||||
|
|
||||||
await initCanvas()
|
|
||||||
|
|
||||||
initColors()
|
|
||||||
|
|
||||||
checkData() && draw()
|
|
||||||
},
|
|
||||||
checkData () {
|
|
||||||
const { data } = this
|
|
||||||
|
|
||||||
this.status = false
|
|
||||||
|
|
||||||
if (!data || !data.series) return false
|
|
||||||
|
|
||||||
this.status = true
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
draw () {
|
|
||||||
const { clearCanvas, calcLabelLineData, drawDecorationCircle } = this
|
|
||||||
|
|
||||||
clearCanvas()
|
|
||||||
|
|
||||||
calcLabelLineData()
|
|
||||||
|
|
||||||
drawDecorationCircle()
|
|
||||||
|
|
||||||
const { calcArcRadius, calcArcRadian, calcArcWidth, drawArc } = this
|
|
||||||
|
|
||||||
calcArcRadius()
|
|
||||||
|
|
||||||
calcArcRadian()
|
|
||||||
|
|
||||||
calcArcWidth()
|
|
||||||
|
|
||||||
drawArc()
|
|
||||||
|
|
||||||
const { calcLableLinePoints, drawLabelLine, drawLabelText } = this
|
|
||||||
|
|
||||||
calcLableLinePoints()
|
|
||||||
|
|
||||||
drawLabelLine()
|
|
||||||
|
|
||||||
drawLabelText()
|
|
||||||
},
|
|
||||||
calcLabelLineData () {
|
|
||||||
const { labelLine, deepClone, data: { series } } = this
|
|
||||||
|
|
||||||
if (!labelLine) return
|
|
||||||
|
|
||||||
const dealAfterLabelLine = this.dealAfterLabelLine = deepClone(labelLine)
|
|
||||||
|
|
||||||
if (labelLine.labels === 'inherit') dealAfterLabelLine.labels = series.map(({ title }) => title)
|
|
||||||
},
|
|
||||||
drawDecorationCircle () {
|
|
||||||
const { ctx, data: { decorationCircleRadius }, defaultDecorationCircleRadius, centerPos } = this
|
|
||||||
|
|
||||||
const radius = this.decorationRadius = Math.min(...centerPos) * (decorationCircleRadius || defaultDecorationCircleRadius)
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.strokeStyle = 'rgba(250, 250, 250, 0.2)'
|
|
||||||
|
|
||||||
ctx.lineWidth = 4
|
|
||||||
|
|
||||||
ctx.arc(...centerPos, radius, 0, Math.PI * 2)
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.lineWidth = 1
|
|
||||||
|
|
||||||
ctx.arc(...centerPos, radius - 7, 0, Math.PI * 2)
|
|
||||||
|
|
||||||
ctx.closePath()
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
},
|
|
||||||
calcArcRadius () {
|
|
||||||
const { data: { series, arcRadiusArea }, defaultArcRadiusArea, centerPos, randomExtend } = this
|
|
||||||
|
|
||||||
const fullRadius = Math.min(...centerPos)
|
|
||||||
|
|
||||||
const currentArcRaidusArea = arcRadiusArea || defaultArcRadiusArea
|
|
||||||
|
|
||||||
const maxRadius = fullRadius * Math.max(...currentArcRaidusArea)
|
|
||||||
const minRadius = fullRadius * Math.min(...currentArcRaidusArea)
|
|
||||||
|
|
||||||
this.arcRadius = series.map(t => randomExtend(minRadius, maxRadius))
|
|
||||||
},
|
|
||||||
calcArcRadian () {
|
|
||||||
const { data: { series }, multipleSum, radianOffset } = this
|
|
||||||
|
|
||||||
const valueSum = this.totalValue = multipleSum(...series.map(({ value }) => value))
|
|
||||||
|
|
||||||
let radian = radianOffset
|
|
||||||
|
|
||||||
const fullRadian = Math.PI * 2
|
|
||||||
|
|
||||||
const avgRadian = fullRadian / series.length
|
|
||||||
|
|
||||||
this.arcRadian = series.map(({ value }) => {
|
|
||||||
const valueRadian = valueSum === 0 ? avgRadian : value / valueSum * fullRadian
|
|
||||||
|
|
||||||
return [radian, (radian += valueRadian)]
|
|
||||||
})
|
|
||||||
},
|
|
||||||
calcArcWidth () {
|
|
||||||
const { data: { series, arcWidthArea }, defaultArcWidthArea, randomExtend } = this
|
|
||||||
|
|
||||||
const currentArea = arcWidthArea || defaultArcWidthArea
|
|
||||||
|
|
||||||
const maxWidth = Math.max(...currentArea)
|
|
||||||
const minWidth = Math.min(...currentArea)
|
|
||||||
|
|
||||||
this.arcWidth = series.map(t => randomExtend(minWidth, maxWidth))
|
|
||||||
},
|
|
||||||
drawArc () {
|
|
||||||
const { ctx, arcRadius, arcRadian, arcWidth, drawColors, centerPos } = this
|
|
||||||
|
|
||||||
const colorNum = drawColors.length
|
|
||||||
|
|
||||||
arcRadius.forEach((radius, i) => {
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.arc(...centerPos, radius, ...arcRadian[i])
|
|
||||||
|
|
||||||
ctx.strokeStyle = drawColors[i % colorNum]
|
|
||||||
|
|
||||||
ctx.lineWidth = arcWidth[i]
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
calcLableLinePoints () {
|
|
||||||
const { arcRadian, arcRadius, centerPos: [x, y], totalValue } = this
|
|
||||||
|
|
||||||
const { canvas: { getCircleRadianPoint }, data: { series } } = this
|
|
||||||
|
|
||||||
let [leftlabelLineNum, rightLabelLineNum] = [0, 0]
|
|
||||||
|
|
||||||
const arcMiddlePoints = arcRadian.map((radian, i) => {
|
|
||||||
const middleRadian = (radian[1] - radian[0]) / 2 + radian[0]
|
|
||||||
|
|
||||||
const point = getCircleRadianPoint(x, y, arcRadius[i], middleRadian)
|
|
||||||
|
|
||||||
point[0] > x && (series[i].value || !totalValue) && rightLabelLineNum++
|
|
||||||
point[0] <= x && (series[i].value || !totalValue) && leftlabelLineNum++
|
|
||||||
|
|
||||||
return point
|
|
||||||
})
|
|
||||||
|
|
||||||
const { getYPos, decorationRadius } = this
|
|
||||||
|
|
||||||
const labelLineYArea = [y - decorationRadius + 10, y + decorationRadius - 10]
|
|
||||||
|
|
||||||
const leftYPos = getYPos(labelLineYArea, leftlabelLineNum)
|
|
||||||
const rightYPos = getYPos(labelLineYArea, rightLabelLineNum)
|
|
||||||
|
|
||||||
const offsetX = decorationRadius + 10
|
|
||||||
|
|
||||||
const leftlabelLineEndX = x - offsetX
|
|
||||||
const rightLableLineEndX = x + offsetX
|
|
||||||
|
|
||||||
const maxRadius = Math.max(...arcRadius)
|
|
||||||
|
|
||||||
const leftNearRadiusX = x - maxRadius - 8
|
|
||||||
const rightNearRadiusX = x + maxRadius + 8
|
|
||||||
|
|
||||||
this.labelLinePoints = arcMiddlePoints.map(([px, py], i) => {
|
|
||||||
if (!series[i].value && totalValue) return [false, false, false, false]
|
|
||||||
|
|
||||||
if (px > x) {
|
|
||||||
const yPos = rightYPos.shift()
|
|
||||||
|
|
||||||
return [
|
|
||||||
[px, py],
|
|
||||||
[rightNearRadiusX, py],
|
|
||||||
[rightLableLineEndX - 10, yPos],
|
|
||||||
[rightLableLineEndX, yPos]
|
|
||||||
]
|
|
||||||
} else {
|
|
||||||
const yPos = leftYPos.pop()
|
|
||||||
|
|
||||||
return [
|
|
||||||
[px, py],
|
|
||||||
[leftNearRadiusX, py],
|
|
||||||
[leftlabelLineEndX + 10, yPos],
|
|
||||||
[leftlabelLineEndX, yPos]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getYPos (area, num) {
|
|
||||||
let gap = 0
|
|
||||||
|
|
||||||
const minus = area[1] - area[0]
|
|
||||||
|
|
||||||
if (num === 1) {
|
|
||||||
return [area[0] + minus / 2]
|
|
||||||
} else if (num === 2) {
|
|
||||||
const offset = minus * 0.1
|
|
||||||
|
|
||||||
return [area[0] + offset, area[1] - offset]
|
|
||||||
} else {
|
|
||||||
gap = minus / (num - 1)
|
|
||||||
|
|
||||||
return new Array(num).fill(0).map((t, i) => area[0] + i * gap)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
drawLabelLine () {
|
|
||||||
const { ctx, labelLinePoints, canvas: { drawPolyline }, drawColors } = this
|
|
||||||
|
|
||||||
const colorNum = drawColors.length
|
|
||||||
|
|
||||||
labelLinePoints.forEach((polyline, i) =>
|
|
||||||
polyline[0] && drawPolyline(ctx, polyline, 2, drawColors[i % colorNum], false, [10, 0], true))
|
|
||||||
},
|
|
||||||
drawLabelText () {
|
|
||||||
const { ctx, labelLinePoints, data: { series, labelFontSize, fixed }, totalValue, defaultLabelFontSize, centerPos: [x] } = this
|
|
||||||
|
|
||||||
ctx.font = `${labelFontSize || defaultLabelFontSize}px Arial`
|
|
||||||
|
|
||||||
ctx.fillStyle = '#fff'
|
|
||||||
|
|
||||||
let totalPercent = 0
|
|
||||||
|
|
||||||
const dataLast = series.length - 1
|
|
||||||
|
|
||||||
series.forEach(({ value, title }, i) => {
|
|
||||||
if (!value && totalValue) return
|
|
||||||
|
|
||||||
let currentPercent = (value / totalValue * 100).toFixed(fixed || 1)
|
|
||||||
|
|
||||||
i === dataLast && (currentPercent = (100 - totalPercent).toFixed(fixed || 1))
|
|
||||||
|
|
||||||
!totalValue && (currentPercent = 0)
|
|
||||||
|
|
||||||
const textPos = labelLinePoints[i][2]
|
|
||||||
|
|
||||||
const isLeft = textPos[0] < x
|
|
||||||
|
|
||||||
ctx.textAlign = isLeft ? 'end' : 'start'
|
|
||||||
|
|
||||||
ctx.textBaseline = 'bottom'
|
|
||||||
|
|
||||||
ctx.fillText(`${currentPercent}%`, ...textPos)
|
|
||||||
|
|
||||||
ctx.textBaseline = 'top'
|
|
||||||
|
|
||||||
ctx.fillText(title, ...textPos)
|
|
||||||
|
|
||||||
totalPercent += Number(currentPercent)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.arc-ring-chart {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.canvas-container {
|
|
||||||
position: relative;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
Before Width: | Height: | Size: 93 KiB |
|
@ -1,54 +1,99 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="dv-border-box-1">
|
<div class="dv-border-box-1" :ref="ref">
|
||||||
<img class="dv-flash-left-top" src="./img/border.gif">
|
<svg
|
||||||
<img class="dv-flash-right-top" src="./img/border.gif">
|
width="150px"
|
||||||
<img class="dv-flash-left-bottom" src="./img/border.gif">
|
height="150px"
|
||||||
<img class="dv-flash-right-bottom" src="./img/border.gif">
|
:key="item"
|
||||||
|
v-for="item in border"
|
||||||
|
:class="`${item} border`"
|
||||||
|
>
|
||||||
|
<polygon
|
||||||
|
fill="#4fd2dd"
|
||||||
|
points="6,66 6,18 12,12 18,12 24,6 27,6 30,9 36,9 39,6 84,6 81,9 75,9 73.2,7 40.8,7 37.8,10.2 24,10.2 12,21 12,24 9,27 9,51 7.8,54 7.8,63"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="fill"
|
||||||
|
values="#4fd2dd;#235fa7;#4fd2dd"
|
||||||
|
dur=".5s"
|
||||||
|
begin="0s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</polygon>
|
||||||
|
<polygon
|
||||||
|
fill="#235fa7"
|
||||||
|
points="27.599999999999998,4.8 38.4,4.8 35.4,7.8 30.599999999999998,7.8"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="fill"
|
||||||
|
values="#235fa7;#4fd2dd;#235fa7"
|
||||||
|
dur=".5s"
|
||||||
|
begin="0s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</polygon>
|
||||||
|
<polygon
|
||||||
|
fill="#4fd2dd"
|
||||||
|
points="9,54 9,63 7.199999999999999,66 7.199999999999999,75 7.8,78 7.8,110 8.4,110 8.4,66 9.6,66 9.6,54"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="fill"
|
||||||
|
values="#4fd2dd;#235fa7;#transparent"
|
||||||
|
dur="1s"
|
||||||
|
begin="0s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</polygon>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<div class="border-box-content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'BorderBox1'
|
name: 'BorderBox1',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
ref: 'border-box-1',
|
||||||
|
|
||||||
|
border: ['left-top', 'right-top', 'left-bottom', 'right-bottom']
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.dv-border-box-1 {
|
.dv-border-box-1 {
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
width: 100%;
|
||||||
min-width: 300px;
|
height: 100%;
|
||||||
min-height: 300px;
|
|
||||||
padding: 35px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.dv-flash-left-top, .dv-flash-right-top, .dv-flash-left-bottom, .dv-flash-right-bottom {
|
.border {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 250px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dv-flash-left-top {
|
.right-top {
|
||||||
top: 0px;
|
right: 0px;
|
||||||
left: -70px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dv-flash-right-top {
|
|
||||||
top: 0px;
|
|
||||||
right: -70px;
|
|
||||||
transform: rotateY(180deg);
|
transform: rotateY(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dv-flash-left-bottom {
|
.left-bottom {
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
left: -70px;
|
|
||||||
transform: rotateX(180deg);
|
transform: rotateX(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dv-flash-right-bottom {
|
.right-bottom {
|
||||||
|
right: 0px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
right: -70px;
|
transform: rotateX(180deg) rotateY(180deg);
|
||||||
transform: rotate(180deg);
|
}
|
||||||
|
|
||||||
|
.border-box-content {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -11,19 +11,21 @@
|
||||||
<circle cx="11" :cy="height - 11" r="1" />
|
<circle cx="11" :cy="height - 11" r="1" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<div class="border-box-content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import borderBoxMixin from '../../mixins/borderBoxMixin.js'
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BorderBox2',
|
name: 'BorderBox2',
|
||||||
mixins: [borderBoxMixin],
|
mixins: [autoResize],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
ref: `border-box-2-${(new Date()).getTime()}`
|
ref: 'border-box-2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,8 +34,8 @@ export default {
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.dv-border-box-2 {
|
.dv-border-box-2 {
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
width: 100%;
|
||||||
padding: 30px;
|
height: 100%;
|
||||||
|
|
||||||
.dv-border-svg-container {
|
.dv-border-svg-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -59,5 +61,11 @@ export default {
|
||||||
.dv-bb2-line2 {
|
.dv-bb2-line2 {
|
||||||
stroke: fade(#fff, 60);
|
stroke: fade(#fff, 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.border-box-content {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -11,19 +11,21 @@
|
||||||
:points="`22, 22 ${width - 4}, 22 ${width - 4}, ${height - 4} 22, ${height - 4} 22, 22`" />
|
:points="`22, 22 ${width - 4}, 22 ${width - 4}, ${height - 4} 22, ${height - 4} 22, 22`" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<div class="border-box-content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import borderBoxMixin from '../../mixins/borderBoxMixin.js'
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BorderBox3',
|
name: 'BorderBox3',
|
||||||
mixins: [borderBoxMixin],
|
mixins: [autoResize],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
ref: `border-box-3-${(new Date()).getTime()}`
|
ref: 'border-box-3'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,8 +34,8 @@ export default {
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.dv-border-box-3 {
|
.dv-border-box-3 {
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
width: 100%;
|
||||||
padding: 30px;
|
height: 100%;
|
||||||
|
|
||||||
.dv-border-svg-container {
|
.dv-border-svg-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -55,5 +57,11 @@ export default {
|
||||||
.dv-bb3-line2 {
|
.dv-bb3-line2 {
|
||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.border-box-content {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -15,30 +15,37 @@
|
||||||
<polyline class="dv-bb4-line-10" :points="`385, 17 ${width - 10}, 17`" />
|
<polyline class="dv-bb4-line-10" :points="`385, 17 ${width - 10}, 17`" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<div class="border-box-content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import borderBoxMixin from '../../mixins/borderBoxMixin.js'
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BorderBox4',
|
name: 'BorderBox4',
|
||||||
mixins: [borderBoxMixin],
|
mixins: [autoResize],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
ref: `border-box-4-${(new Date()).getTime()}`
|
ref: 'border-box-4'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: ['reverse']
|
props: {
|
||||||
|
reverse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.dv-border-box-4 {
|
.dv-border-box-4 {
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
width: 100%;
|
||||||
padding: 30px;
|
height: 100%;
|
||||||
|
|
||||||
.dv-reverse {
|
.dv-reverse {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
|
@ -124,5 +131,11 @@ export default {
|
||||||
.sw1;
|
.sw1;
|
||||||
stroke-dasharray: 80 270;
|
stroke-dasharray: 80 270;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.border-box-content {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -11,30 +11,37 @@
|
||||||
<polyline class="dv-bb5-line-6" :points="`15, ${height - 13} ${width - 110}, ${height - 13}`" />
|
<polyline class="dv-bb5-line-6" :points="`15, ${height - 13} ${width - 110}, ${height - 13}`" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<div class="border-box-content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import borderBoxMixin from '../../mixins/borderBoxMixin.js'
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BorderBox5',
|
name: 'BorderBox5',
|
||||||
mixins: [borderBoxMixin],
|
mixins: [autoResize],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
ref: `border-box-5-${(new Date()).getTime()}`
|
ref: 'border-box-5'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: ['reverse']
|
props: {
|
||||||
|
reverse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.dv-border-box-5 {
|
.dv-border-box-5 {
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
width: 100%;
|
||||||
padding: 20px;
|
height: 100%;
|
||||||
|
|
||||||
.dv-reverse {
|
.dv-reverse {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
|
@ -71,5 +78,10 @@ export default {
|
||||||
stroke: fade(#fff, 15);
|
stroke: fade(#fff, 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.border-box-content {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -19,19 +19,21 @@
|
||||||
<polyline :points="`${width - 7}, ${height - 30} ${width - 7}, ${height - 80}`" />
|
<polyline :points="`${width - 7}, ${height - 30} ${width - 7}, ${height - 80}`" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<div class="border-box-content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import borderBoxMixin from '../../mixins/borderBoxMixin.js'
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BorderBox6',
|
name: 'BorderBox6',
|
||||||
mixins: [borderBoxMixin],
|
mixins: [autoResize],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
ref: `border-box-6-${(new Date()).getTime()}`
|
ref: 'border-box-6'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,8 +42,8 @@ export default {
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.dv-border-box-6 {
|
.dv-border-box-6 {
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
width: 100%;
|
||||||
padding: 10px;
|
height: 100%;
|
||||||
|
|
||||||
.dv-svg-container {
|
.dv-svg-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -60,5 +62,11 @@ export default {
|
||||||
stroke: fade(#fff, 35);
|
stroke: fade(#fff, 35);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.border-box-content {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -12,19 +12,21 @@
|
||||||
<polyline class="dv-bb7-line-width-5" :points="`0, ${height - 10} 0, ${height} 10, ${height}`" />
|
<polyline class="dv-bb7-line-width-5" :points="`0, ${height - 10} 0, ${height} 10, ${height}`" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<div class="border-box-content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import borderBoxMixin from '../../mixins/borderBoxMixin.js'
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BorderBox7',
|
name: 'BorderBox7',
|
||||||
mixins: [borderBoxMixin],
|
mixins: [autoResize],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
ref: `border-box-7-${(new Date()).getTime()}`
|
ref: 'border-box-7'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,10 +37,10 @@ export default {
|
||||||
|
|
||||||
.dv-border-box-7 {
|
.dv-border-box-7 {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
box-shadow: inset 0 0 40px fade(@color, 30);
|
box-shadow: inset 0 0 40px fade(@color, 30);
|
||||||
box-sizing: border-box;
|
|
||||||
border: 1px solid @color;
|
border: 1px solid @color;
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.dv-svg-container {
|
.dv-svg-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -62,5 +64,11 @@ export default {
|
||||||
stroke: fade(gray, 50);
|
stroke: fade(gray, 50);
|
||||||
stroke-width: 5;
|
stroke-width: 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.border-box-content {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
<template>
|
||||||
|
<div class="dv-border-box-8" :ref="ref">
|
||||||
|
<svg class="dv-svg-container">
|
||||||
|
<defs>
|
||||||
|
<path
|
||||||
|
:id="path"
|
||||||
|
:d="`M2.5, 2.5 L${width - 2.5}, 2.5 L${width - 2.5}, ${height - 2.5} L2.5, ${height - 2.5} L2.5, 2.5`"
|
||||||
|
fill="transparent"
|
||||||
|
/>
|
||||||
|
<radialGradient
|
||||||
|
:id="gradient"
|
||||||
|
cx="50%" cy="50%" r="50%"
|
||||||
|
>
|
||||||
|
<stop
|
||||||
|
offset="0%" stop-color="#fff"
|
||||||
|
stop-opacity="1"
|
||||||
|
/>
|
||||||
|
<stop
|
||||||
|
offset="100%" stop-color="#fff"
|
||||||
|
stop-opacity="0"
|
||||||
|
/>
|
||||||
|
</radialGradient>
|
||||||
|
|
||||||
|
<mask :id="mask">
|
||||||
|
<circle cx="0" cy="0" r="150" :fill="`url(#${gradient})`">
|
||||||
|
<animateMotion
|
||||||
|
dur="3s"
|
||||||
|
:path="`M2.5, 2.5 L${width - 2.5}, 2.5 L${width - 2.5}, ${height - 2.5} L2.5, ${height - 2.5} L2.5, 2.5`"
|
||||||
|
rotate="auto"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
</mask>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<use
|
||||||
|
stroke="#235fa7"
|
||||||
|
stroke-width="1"
|
||||||
|
:xlink:href="`#${path}`"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<use
|
||||||
|
stroke="#4fd2dd"
|
||||||
|
stroke-width="3"
|
||||||
|
:xlink:href="`#${path}`"
|
||||||
|
:mask="`url(#${mask})`"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="stroke-dasharray"
|
||||||
|
:from="`0, ${length}`"
|
||||||
|
:to="`${length}, 0`"
|
||||||
|
dur="3s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</use>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<div class="border-box-content">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'BorderBox8',
|
||||||
|
mixins: [autoResize],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
ref: 'border-box-8',
|
||||||
|
path: `border-box-8-path-${(new Date()).getTime()}`,
|
||||||
|
gradient: `border-box-8-gradient-${(new Date()).getTime()}`,
|
||||||
|
mask: `border-box-8-mask-${(new Date()).getTime()}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
length () {
|
||||||
|
const { width, height } = this
|
||||||
|
|
||||||
|
return (width + height - 5) * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.dv-border-box-8 {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-box-content {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,167 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="capsule-chart">
|
|
||||||
|
|
||||||
<loading v-if="!status" />
|
|
||||||
|
|
||||||
<template v-else>
|
|
||||||
<div class="label-column">
|
|
||||||
<div v-for="item in data.series" :key="item.title">{{ item.title }}</div>
|
|
||||||
<div> </div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="capsule-container">
|
|
||||||
<div class="capsule-item" v-for="(capsule, index) in capsuleData" :key="index">
|
|
||||||
<div :style="`width: ${capsule * 100}%; background-color: ${drawColors[index % drawColors.length]};`"></div>
|
|
||||||
</div>
|
|
||||||
<div class="unit-label">
|
|
||||||
<div class="unit-container">
|
|
||||||
<div v-for="(unit, index) in unitData" :key="unit + index">{{ unit }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="unit-text">单位</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="for-solt">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import colorsMixin from '../../mixins/colorsMixin.js'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'CapsuleChart',
|
|
||||||
props: ['data', 'colors'],
|
|
||||||
mixins: [colorsMixin],
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
status: false,
|
|
||||||
|
|
||||||
capsuleData: [],
|
|
||||||
unitData: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
data () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
init () {
|
|
||||||
const { checkData, initColors, calcCapsuleAndUnitData } = this
|
|
||||||
|
|
||||||
initColors()
|
|
||||||
|
|
||||||
checkData() && calcCapsuleAndUnitData()
|
|
||||||
},
|
|
||||||
checkData () {
|
|
||||||
const { data } = this
|
|
||||||
|
|
||||||
this.status = false
|
|
||||||
|
|
||||||
if (!data || !data.series) return false
|
|
||||||
|
|
||||||
this.status = true
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
calcCapsuleAndUnitData () {
|
|
||||||
const { data: { series } } = this
|
|
||||||
|
|
||||||
const capsuleData = series.map(({ value }) => value)
|
|
||||||
|
|
||||||
const maxValue = Math.max(...capsuleData)
|
|
||||||
|
|
||||||
this.capsuleData = capsuleData.map(v => maxValue ? v / maxValue : 0)
|
|
||||||
|
|
||||||
const oneSixth = maxValue / 5
|
|
||||||
|
|
||||||
this.unitData = new Array(6).fill(0).map((v, i) => Math.ceil(i * oneSixth))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.capsule-chart {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 10px;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.label-column {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding-right: 10px;
|
|
||||||
text-align: right;
|
|
||||||
font-size: 12px;
|
|
||||||
|
|
||||||
div {
|
|
||||||
height: 20px;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.capsule-container {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.capsule-item {
|
|
||||||
box-shadow: 0 0 3px #999;
|
|
||||||
height: 10px;
|
|
||||||
margin: 5px 0px;
|
|
||||||
border-radius: 5px;
|
|
||||||
width: calc(~"100% - 50px");
|
|
||||||
|
|
||||||
div {
|
|
||||||
height: 8px;
|
|
||||||
margin-top: 1px;
|
|
||||||
border-radius: 5px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.unit-label {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
line-height: 20px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unit-text {
|
|
||||||
width: 40px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unit-container {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.for-solt {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
<template>
|
||||||
|
<div class="dv-charts-container" :ref="ref">
|
||||||
|
<div class="charts" :ref="chartRef" />
|
||||||
|
|
||||||
|
<div class="charts-slot-content">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
|
import Charts from '@jiaminghi/charts'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Charts',
|
||||||
|
mixins: [autoResize],
|
||||||
|
props: {
|
||||||
|
option: {
|
||||||
|
type: Object,
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
ref: `charts-container-${(new Date()).getTime()}`,
|
||||||
|
chartRef: `chart-${(new Date()).getTime()}`,
|
||||||
|
|
||||||
|
chart: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
option () {
|
||||||
|
let { chart, option } = this
|
||||||
|
|
||||||
|
if (!option) option = {}
|
||||||
|
|
||||||
|
chart.setOption(option)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
afterAutoResizeMixinInit () {
|
||||||
|
const { initChart } = this
|
||||||
|
|
||||||
|
initChart()
|
||||||
|
},
|
||||||
|
initChart () {
|
||||||
|
const { $refs, chartRef, option } = this
|
||||||
|
|
||||||
|
const chart = this.chart = new Charts($refs[chartRef])
|
||||||
|
|
||||||
|
if (!option) return
|
||||||
|
|
||||||
|
chart.setOption(option)
|
||||||
|
},
|
||||||
|
onResize () {
|
||||||
|
const { chart } = this
|
||||||
|
|
||||||
|
if (!chart) return
|
||||||
|
|
||||||
|
chart.resize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.dv-charts-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.charts {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.charts-slot-content {
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,620 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="column-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: 'ColumnChart',
|
|
||||||
mixins: [colorsMixin, canvasMixin, axisMixin],
|
|
||||||
props: ['data', 'labelLine', 'colors'],
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
ref: `radar-chart-${(new Date()).getTime()}`,
|
|
||||||
|
|
||||||
status: false,
|
|
||||||
|
|
||||||
// axis base config
|
|
||||||
boundaryGap: true,
|
|
||||||
mulValueAdd: true,
|
|
||||||
horizon: false,
|
|
||||||
|
|
||||||
echelonOffset: 10,
|
|
||||||
defaultColumnBGColor: 'rgba(100, 100, 100, 0.2)',
|
|
||||||
|
|
||||||
defaultValueFontSize: 10,
|
|
||||||
defaultValueColor: '#666',
|
|
||||||
|
|
||||||
columnData: [],
|
|
||||||
columnItemSeriesNum: 0,
|
|
||||||
columnItemAllWidth: 0,
|
|
||||||
columnItemWidth: 0,
|
|
||||||
columnBGWidth: 0,
|
|
||||||
columnItemOffset: [],
|
|
||||||
|
|
||||||
valueTextOffset: [],
|
|
||||||
|
|
||||||
valuePointPos: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
data () {
|
|
||||||
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 { calcHorizon, initAxis, drawAxis } = this
|
|
||||||
|
|
||||||
calcHorizon()
|
|
||||||
|
|
||||||
initAxis()
|
|
||||||
|
|
||||||
drawAxis()
|
|
||||||
|
|
||||||
const { switchNormalOrCenterOriginType } = this
|
|
||||||
|
|
||||||
switchNormalOrCenterOriginType()
|
|
||||||
},
|
|
||||||
calcHorizon () {
|
|
||||||
const { data: { horizon } } = this
|
|
||||||
|
|
||||||
this.horizon = horizon
|
|
||||||
},
|
|
||||||
switchNormalOrCenterOriginType () {
|
|
||||||
const { centerOrigin, drawNormalTypeColumnChart, drawCenterOriginTypeColumnChart } = this
|
|
||||||
|
|
||||||
if (centerOrigin) drawCenterOriginTypeColumnChart()
|
|
||||||
|
|
||||||
if (!centerOrigin) drawNormalTypeColumnChart()
|
|
||||||
},
|
|
||||||
drawNormalTypeColumnChart () {
|
|
||||||
const { calcColumnConfig, calcColumnItemOffset, calcValuePointPos } = this
|
|
||||||
|
|
||||||
calcColumnConfig()
|
|
||||||
|
|
||||||
calcColumnItemOffset()
|
|
||||||
|
|
||||||
calcValuePointPos()
|
|
||||||
|
|
||||||
const { drawColumnBG, drawFigure, drawValueText } = this
|
|
||||||
|
|
||||||
drawColumnBG()
|
|
||||||
|
|
||||||
drawFigure()
|
|
||||||
|
|
||||||
drawValueText()
|
|
||||||
},
|
|
||||||
calcColumnConfig () {
|
|
||||||
const { data: { series, spaceBetween }, labelAxisTagPos, axisOriginPos, horizon } = this
|
|
||||||
|
|
||||||
const columnData = this.columnData = series.filter(({ type }) =>
|
|
||||||
!(type === 'polyline' || type === 'smoothline'))
|
|
||||||
|
|
||||||
const columnItemSeriesNum = this.columnItemSeriesNum = columnData.length
|
|
||||||
|
|
||||||
const columnItemAllWidth = this.columnItemAllWidth = (horizon
|
|
||||||
? axisOriginPos[1] - labelAxisTagPos[0][1]
|
|
||||||
: labelAxisTagPos[0][0] - axisOriginPos[0]) * 2
|
|
||||||
|
|
||||||
const columnItemWidth = this.columnItemWidth = columnItemAllWidth / (columnItemSeriesNum + 1)
|
|
||||||
|
|
||||||
const spaceGap = columnItemWidth / (columnItemSeriesNum + 1)
|
|
||||||
|
|
||||||
let columnBGWidth = columnItemWidth * columnItemSeriesNum
|
|
||||||
|
|
||||||
spaceBetween && (columnBGWidth += spaceGap * (columnItemSeriesNum - 1))
|
|
||||||
|
|
||||||
this.columnBGWidth = columnBGWidth
|
|
||||||
},
|
|
||||||
calcColumnItemOffset () {
|
|
||||||
const { columnItemSeriesNum, columnItemAllWidth, columnItemWidth } = this
|
|
||||||
|
|
||||||
const { data: { spaceBetween, series } } = this
|
|
||||||
|
|
||||||
const halfColumnWidth = columnItemWidth / 2
|
|
||||||
|
|
||||||
const halfColumnItemAllWidth = columnItemAllWidth / 2
|
|
||||||
|
|
||||||
let columnItemOffset = new Array(columnItemSeriesNum).fill(0)
|
|
||||||
|
|
||||||
if (spaceBetween) {
|
|
||||||
const spaceGap = columnItemWidth / (columnItemSeriesNum + 1)
|
|
||||||
|
|
||||||
columnItemOffset = columnItemOffset.map((t, i) =>
|
|
||||||
spaceGap * (i + 1) + columnItemWidth * i + halfColumnWidth - halfColumnItemAllWidth)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!spaceBetween) {
|
|
||||||
columnItemOffset = columnItemOffset.map((t, i) =>
|
|
||||||
columnItemWidth * (i + 1) - halfColumnItemAllWidth)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.columnItemOffset = series.map(({ type }) =>
|
|
||||||
(type === 'polyline' || type === 'smoothline')
|
|
||||||
? 0
|
|
||||||
: columnItemOffset.shift())
|
|
||||||
},
|
|
||||||
calcValuePointPos () {
|
|
||||||
const { getAxisPointsPos, valueAxisMaxMin, agValueAxisMaxMin } = this
|
|
||||||
|
|
||||||
const { labelAxisTagPos, deepClone, filterNull, multipleSum } = this
|
|
||||||
|
|
||||||
const { data: { series }, axisOriginPos, axisWH, horizon } = this
|
|
||||||
|
|
||||||
const dealAfterData = deepClone(series).map(({ value, againstAxis }) => {
|
|
||||||
if (!(value[0] instanceof Array)) return { value, againstAxis }
|
|
||||||
|
|
||||||
const valueData = value.map(valueSeries => valueSeries.map((v, i) => {
|
|
||||||
if (!v && v !== 0) return false
|
|
||||||
|
|
||||||
return multipleSum(...filterNull(valueSeries.slice(0, i + 1)))
|
|
||||||
}))
|
|
||||||
|
|
||||||
return { value: valueData, againstAxis }
|
|
||||||
})
|
|
||||||
|
|
||||||
this.valuePointPos = dealAfterData.map(({ value, againstAxis }) =>
|
|
||||||
getAxisPointsPos(
|
|
||||||
againstAxis ? agValueAxisMaxMin : valueAxisMaxMin,
|
|
||||||
value,
|
|
||||||
axisOriginPos,
|
|
||||||
axisWH,
|
|
||||||
labelAxisTagPos,
|
|
||||||
horizon
|
|
||||||
))
|
|
||||||
},
|
|
||||||
drawColumnBG () {
|
|
||||||
const { ctx, data: { showColumnBG, columnBGColor, roundColumn } } = this
|
|
||||||
|
|
||||||
if (!showColumnBG) return
|
|
||||||
|
|
||||||
const { columnBGWidth, defaultColumnBGColor, horizon, axisWH: [w, h], labelAxisTagPos } = this
|
|
||||||
|
|
||||||
const trueColumnColor = columnBGColor || defaultColumnBGColor
|
|
||||||
|
|
||||||
ctx.lineWidth = columnBGWidth
|
|
||||||
ctx.strokeStyle = trueColumnColor
|
|
||||||
ctx.setLineDash([10, 0])
|
|
||||||
|
|
||||||
const { getRoundColumnPoint, labelAxisTag } = this
|
|
||||||
|
|
||||||
ctx.lineCap = roundColumn ? 'round' : 'butt'
|
|
||||||
|
|
||||||
labelAxisTagPos.forEach(([x, y], i) => {
|
|
||||||
if (!labelAxisTag[i] && labelAxisTag[i] !== 0) return
|
|
||||||
|
|
||||||
const topPoint = horizon ? [x + w, y] : [x, y - h]
|
|
||||||
let columnBGPoints = [[x, y], topPoint]
|
|
||||||
|
|
||||||
roundColumn && (columnBGPoints = getRoundColumnPoint(columnBGPoints, columnBGWidth))
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.moveTo(...columnBGPoints[0])
|
|
||||||
ctx.lineTo(...columnBGPoints[1])
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
drawFigure () {
|
|
||||||
const { data: { series }, valuePointPos } = this
|
|
||||||
|
|
||||||
const { drawColumn, drawEchelon, drawline } = this
|
|
||||||
|
|
||||||
series.forEach((valueSeries, i) => {
|
|
||||||
switch (valueSeries.type) {
|
|
||||||
case 'leftEchelon':
|
|
||||||
case 'rightEchelon': drawEchelon(valueSeries, valuePointPos[i], i)
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'polyline':
|
|
||||||
case 'smoothline': drawline(valueSeries, valuePointPos[i], i)
|
|
||||||
break
|
|
||||||
|
|
||||||
default: drawColumn(valueSeries, valuePointPos[i], i)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
drawColumn ({ fillColor }, points, i) {
|
|
||||||
const { ctx, columnItemWidth, drawColors } = this
|
|
||||||
|
|
||||||
ctx.setLineDash([10, 0])
|
|
||||||
ctx.lineWidth = columnItemWidth
|
|
||||||
|
|
||||||
const color = fillColor || drawColors[i]
|
|
||||||
const colorNum = color.length
|
|
||||||
const drawColorNum = drawColors.length
|
|
||||||
|
|
||||||
const { columnItemOffset, labelAxisTagPos, getOffsetPoint } = this
|
|
||||||
|
|
||||||
const currentOffset = columnItemOffset[i]
|
|
||||||
const offsetTagPos = labelAxisTagPos.map(p => getOffsetPoint(p, currentOffset))
|
|
||||||
|
|
||||||
const { getGradientColor, getRoundColumnPoint, data: { roundColumn } } = this
|
|
||||||
|
|
||||||
ctx.lineCap = roundColumn ? 'round' : 'butt'
|
|
||||||
|
|
||||||
const seriesColumn = points[0][0] instanceof Array
|
|
||||||
|
|
||||||
seriesColumn && points.forEach((series, j) => {
|
|
||||||
let lastEnd = offsetTagPos[j]
|
|
||||||
|
|
||||||
series.forEach((item, k) => {
|
|
||||||
if (!item && item !== 0) return
|
|
||||||
|
|
||||||
const currentPoint = getOffsetPoint(item, currentOffset)
|
|
||||||
|
|
||||||
let columnPoint = [lastEnd, currentPoint]
|
|
||||||
|
|
||||||
roundColumn && (columnPoint = getRoundColumnPoint(columnPoint))
|
|
||||||
|
|
||||||
if (typeof color === 'string') {
|
|
||||||
ctx.strokeStyle = drawColors[(i + k) % drawColorNum]
|
|
||||||
} else {
|
|
||||||
ctx.strokeStyle = color[k % colorNum]
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.moveTo(...columnPoint[0])
|
|
||||||
ctx.lineTo(...columnPoint[1])
|
|
||||||
ctx.stroke()
|
|
||||||
|
|
||||||
lastEnd = currentPoint
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
!seriesColumn && points.forEach((point, i) => {
|
|
||||||
if (!point && point !== 0) return
|
|
||||||
|
|
||||||
let columnPoint = [offsetTagPos[i], getOffsetPoint(point, currentOffset)]
|
|
||||||
|
|
||||||
roundColumn && (columnPoint = getRoundColumnPoint(columnPoint))
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.strokeStyle = getGradientColor(point, color)
|
|
||||||
ctx.moveTo(...columnPoint[0])
|
|
||||||
ctx.lineTo(...columnPoint[1])
|
|
||||||
ctx.stroke()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getOffsetPoint ([x, y], offset) {
|
|
||||||
const { horizon } = this
|
|
||||||
|
|
||||||
return horizon
|
|
||||||
? [x, y + offset]
|
|
||||||
: [x + offset, y]
|
|
||||||
},
|
|
||||||
getOffsetPoints (points, offset) {
|
|
||||||
const { getOffsetPoint } = this
|
|
||||||
|
|
||||||
return points.map(point => point ? getOffsetPoint(point, offset) : false)
|
|
||||||
},
|
|
||||||
getRoundColumnPoint ([pa, pb], cw = false) {
|
|
||||||
const { horizon, columnItemWidth: dciw } = this
|
|
||||||
|
|
||||||
const columnWidth = cw || dciw
|
|
||||||
|
|
||||||
const radius = columnWidth / 2
|
|
||||||
|
|
||||||
let [a, b, c, d] = [0, 0, 0, 0]
|
|
||||||
|
|
||||||
if (horizon) {
|
|
||||||
a = pa[0] + radius
|
|
||||||
b = pa[1]
|
|
||||||
c = pb[0] - radius
|
|
||||||
d = pb[1]
|
|
||||||
} else {
|
|
||||||
a = pa[0]
|
|
||||||
b = pa[1] - radius
|
|
||||||
c = pb[0]
|
|
||||||
d = pb[1] + radius
|
|
||||||
}
|
|
||||||
|
|
||||||
return horizon ? [
|
|
||||||
[a > c ? c : a, b],
|
|
||||||
[c, d]
|
|
||||||
] : [
|
|
||||||
[a, b],
|
|
||||||
[c, b > d ? d : b]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
drawline ({ lineColor, fillColor, pointColor, lineType, lineDash, type }, points, i) {
|
|
||||||
const { drawColors, ctx, axisOriginPos: [x, y], horizon } = this
|
|
||||||
|
|
||||||
const { color: { hexToRgb }, getGradientColor, getTopPoint } = this
|
|
||||||
|
|
||||||
const drawColorNum = drawColors.length
|
|
||||||
const currentColor = drawColors[i % drawColorNum]
|
|
||||||
|
|
||||||
let currentLineColor = hexToRgb(currentColor, 0.6)
|
|
||||||
let currentPointColor = currentColor
|
|
||||||
|
|
||||||
lineColor && (currentLineColor = lineColor)
|
|
||||||
pointColor && (currentPointColor = pointColor)
|
|
||||||
|
|
||||||
let currentLineType = lineType || 'line'
|
|
||||||
let currentLineDash = currentLineType === 'dashed' ? (lineDash || [5, 5]) : [10, 0]
|
|
||||||
|
|
||||||
ctx.strokeStyle = currentLineColor
|
|
||||||
|
|
||||||
const { canvas: { drawPolylinePath, drawPolyline, drawPoints } } = this
|
|
||||||
const { canvas: { drawSmoothlinePath, drawSmoothline } } = this
|
|
||||||
|
|
||||||
const lineFun = type === 'polyline' ? [drawPolylinePath, drawPolyline] : [drawSmoothlinePath, drawSmoothline]
|
|
||||||
|
|
||||||
if (fillColor) {
|
|
||||||
const lastPoint = points[points.length - 1]
|
|
||||||
|
|
||||||
ctx.fillStyle = getGradientColor(getTopPoint(points), fillColor)
|
|
||||||
|
|
||||||
lineFun[0](ctx, points, false, true, true)
|
|
||||||
ctx.lineTo(...(horizon ? [x, lastPoint[1]] : [lastPoint[0], y]))
|
|
||||||
ctx.lineTo(...(horizon ? [x, points[0][1]] : [points[0][0], y]))
|
|
||||||
|
|
||||||
ctx.closePath()
|
|
||||||
ctx.fill()
|
|
||||||
}
|
|
||||||
|
|
||||||
lineFun[1](ctx, points, 1, currentLineColor, false, currentLineDash, true, true)
|
|
||||||
|
|
||||||
drawPoints(ctx, points, 2, currentPointColor)
|
|
||||||
},
|
|
||||||
getTopPoint (points) {
|
|
||||||
const { horizon } = this
|
|
||||||
|
|
||||||
let topIndex = 0
|
|
||||||
|
|
||||||
const xPos = points.map(([x]) => x)
|
|
||||||
const yPos = points.map(([, y]) => y)
|
|
||||||
|
|
||||||
if (horizon) {
|
|
||||||
const top = Math.max(...xPos)
|
|
||||||
|
|
||||||
topIndex = xPos.findIndex(v => v === top)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!horizon) {
|
|
||||||
const top = Math.min(...yPos)
|
|
||||||
|
|
||||||
topIndex = yPos.findIndex(v => v === top)
|
|
||||||
}
|
|
||||||
|
|
||||||
return points[topIndex]
|
|
||||||
},
|
|
||||||
drawEchelon ({ fillColor, type }, points, i) {
|
|
||||||
const { data: { roundColumn } } = this
|
|
||||||
|
|
||||||
const seriesColumn = points[0][0] instanceof Array
|
|
||||||
|
|
||||||
if (seriesColumn || roundColumn) return
|
|
||||||
|
|
||||||
const { ctx, columnItemOffset, labelAxisTagPos, getOffsetPoint } = this
|
|
||||||
|
|
||||||
const currentOffset = columnItemOffset[i]
|
|
||||||
const offsetTagPos = labelAxisTagPos.map(p => getOffsetPoint(p, currentOffset))
|
|
||||||
|
|
||||||
const { drawColors, getGradientColor, getEchelonPoints } = this
|
|
||||||
|
|
||||||
const drawColorsNum = drawColors.length
|
|
||||||
|
|
||||||
const color = fillColor || drawColors[i % drawColorsNum]
|
|
||||||
|
|
||||||
const { canvas: { drawPolylinePath } } = this
|
|
||||||
|
|
||||||
points.forEach((point, i) => {
|
|
||||||
const topPoint = getOffsetPoint(point, currentOffset)
|
|
||||||
const bottomPoint = offsetTagPos[i]
|
|
||||||
|
|
||||||
const echelonPoints = getEchelonPoints(topPoint, bottomPoint, type)
|
|
||||||
|
|
||||||
drawPolylinePath(ctx, echelonPoints, true, true)
|
|
||||||
|
|
||||||
ctx.fillStyle = getGradientColor(point, color)
|
|
||||||
|
|
||||||
ctx.fill()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getEchelonPoints ([tx, ty], [bx, by], type) {
|
|
||||||
const { columnItemWidth, echelonOffset, horizon } = this
|
|
||||||
|
|
||||||
const halfWidth = columnItemWidth / 2
|
|
||||||
|
|
||||||
const echelonPoint = []
|
|
||||||
|
|
||||||
if (horizon) {
|
|
||||||
let enhance = tx - bx < echelonOffset
|
|
||||||
|
|
||||||
if (type === 'leftEchelon') {
|
|
||||||
echelonPoint[0] = [tx, ty + halfWidth]
|
|
||||||
echelonPoint[1] = [bx, ty + halfWidth]
|
|
||||||
echelonPoint[2] = [bx + echelonOffset, by - halfWidth]
|
|
||||||
echelonPoint[3] = [tx, ty - halfWidth]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'rightEchelon') {
|
|
||||||
echelonPoint[0] = [tx, ty - halfWidth]
|
|
||||||
echelonPoint[1] = [bx, ty - halfWidth]
|
|
||||||
echelonPoint[2] = [bx + echelonOffset, by + halfWidth]
|
|
||||||
echelonPoint[3] = [tx, ty + halfWidth]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enhance) echelonPoint.splice(2, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!horizon) {
|
|
||||||
let enhance = by - ty < echelonOffset
|
|
||||||
|
|
||||||
if (type === 'leftEchelon') {
|
|
||||||
echelonPoint[0] = [tx + halfWidth, ty]
|
|
||||||
echelonPoint[1] = [tx + halfWidth, by]
|
|
||||||
echelonPoint[2] = [tx - halfWidth, by - echelonOffset]
|
|
||||||
echelonPoint[3] = [tx - halfWidth, ty]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'rightEchelon') {
|
|
||||||
echelonPoint[0] = [tx - halfWidth, ty]
|
|
||||||
echelonPoint[1] = [tx - halfWidth, by]
|
|
||||||
echelonPoint[2] = [tx + halfWidth, by - echelonOffset]
|
|
||||||
echelonPoint[3] = [tx + halfWidth, ty]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enhance) echelonPoint.splice(2, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return echelonPoint
|
|
||||||
},
|
|
||||||
drawValueText () {
|
|
||||||
const { data: { showValueText }, horizon } = this
|
|
||||||
|
|
||||||
if (!showValueText) return
|
|
||||||
|
|
||||||
const { data: { valueTextFontSize, valueTextOffset, series } } = this
|
|
||||||
|
|
||||||
const { ctx, defaultValueFontSize } = this
|
|
||||||
|
|
||||||
const offset = horizon ? [5, 0] : [0, -5]
|
|
||||||
|
|
||||||
const trueOffset = valueTextOffset || offset
|
|
||||||
|
|
||||||
ctx.font = `${valueTextFontSize || defaultValueFontSize}px Arial`
|
|
||||||
|
|
||||||
ctx.textAlign = horizon ? 'left' : 'center'
|
|
||||||
ctx.textBaseline = horizon ? 'middle' : 'bottom'
|
|
||||||
|
|
||||||
const { drawSeriesTextValue } = this
|
|
||||||
|
|
||||||
series.forEach((seriesItem, i) => drawSeriesTextValue(seriesItem, i, trueOffset))
|
|
||||||
},
|
|
||||||
drawSeriesTextValue ({ value, valueTextColor, fillColor, lineColor }, i, trueOffset) {
|
|
||||||
const { ctx, valuePointPos, columnItemOffset, drawTexts, getOffsetPoints, drawColors } = this
|
|
||||||
|
|
||||||
const { data: { valueTextColor: outerValueTC }, defaultValueColor } = this
|
|
||||||
|
|
||||||
const drawColorsNum = drawColors.length
|
|
||||||
|
|
||||||
let currentColor = valueTextColor
|
|
||||||
currentColor === 'inherit' && (currentColor = fillColor || lineColor || drawColors[i % drawColorsNum])
|
|
||||||
const mulColor = currentColor instanceof Array
|
|
||||||
const colorNum = mulColor ? currentColor.length : 0
|
|
||||||
|
|
||||||
const currentPos = valuePointPos[i]
|
|
||||||
const currentOffset = columnItemOffset[i]
|
|
||||||
|
|
||||||
const mulSeries = value[0] instanceof Array
|
|
||||||
|
|
||||||
if (mulSeries) {
|
|
||||||
value.forEach((item, j) => {
|
|
||||||
const pointPos = getOffsetPoints(currentPos[j], currentOffset)
|
|
||||||
|
|
||||||
item.forEach((v, l) => {
|
|
||||||
!currentColor && (ctx.fillStyle = defaultValueColor)
|
|
||||||
currentColor && (ctx.fillStyle = mulColor ? currentColor[l % colorNum] : currentColor)
|
|
||||||
drawTexts(ctx, [item[l]], [pointPos[l]], trueOffset)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mulSeries) {
|
|
||||||
mulColor && (currentColor = currentColor[0])
|
|
||||||
|
|
||||||
ctx.fillStyle = currentColor || outerValueTC || defaultValueColor
|
|
||||||
drawTexts(ctx, value, getOffsetPoints(currentPos, currentOffset), trueOffset)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
drawTexts (ctx, values, points, [x, y] = [0, 0]) {
|
|
||||||
values.forEach((v, i) => {
|
|
||||||
if (!v && v !== 0) return
|
|
||||||
|
|
||||||
ctx.fillText(v, points[i][0] + x, points[i][1] + y)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
drawCenterOriginTypeColumnChart () {}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.column-chart {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.canvas-container {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,179 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="concentric-arc-chart">
|
|
||||||
<loading v-if="!status" />
|
|
||||||
|
|
||||||
<div class="canvas-container">
|
|
||||||
<canvas :ref="ref" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import colorsMixin from '../../mixins/colorsMixin.js'
|
|
||||||
import canvasMixin from '../../mixins/canvasMixin.js'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ConcentricArcChart',
|
|
||||||
props: ['data', 'colors'],
|
|
||||||
mixins: [colorsMixin, canvasMixin],
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
ref: `concentric-arc-chart-${(new Date()).getTime()}`,
|
|
||||||
|
|
||||||
status: false,
|
|
||||||
|
|
||||||
arcOriginPos: [],
|
|
||||||
|
|
||||||
defaultArcRadiusArea: [0.2, 0.8],
|
|
||||||
defaultArcGap: 3,
|
|
||||||
defaultArcColor: ['#00c0ff', '#3de7c9'],
|
|
||||||
|
|
||||||
arcRadius: [],
|
|
||||||
arcRadian: [],
|
|
||||||
arcLineWidth: 0,
|
|
||||||
arcColor: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
data () {
|
|
||||||
const { checkData, draw } = this
|
|
||||||
|
|
||||||
checkData() && draw()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async init () {
|
|
||||||
const { initCanvas, initColors, checkData, draw } = this
|
|
||||||
|
|
||||||
await initCanvas()
|
|
||||||
|
|
||||||
initColors()
|
|
||||||
|
|
||||||
checkData() && draw()
|
|
||||||
},
|
|
||||||
checkData () {
|
|
||||||
const { data } = this
|
|
||||||
|
|
||||||
this.status = false
|
|
||||||
|
|
||||||
if (!data || !data.series) return false
|
|
||||||
|
|
||||||
this.status = true
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
draw () {
|
|
||||||
const { clearCanvas, calcArcRadius, calcArcRadian, calcArcColor, drawArc, drawTitle } = this
|
|
||||||
|
|
||||||
clearCanvas()
|
|
||||||
|
|
||||||
calcArcRadius()
|
|
||||||
|
|
||||||
calcArcRadian()
|
|
||||||
|
|
||||||
calcArcColor()
|
|
||||||
|
|
||||||
drawArc()
|
|
||||||
|
|
||||||
drawTitle()
|
|
||||||
},
|
|
||||||
calcArcRadius () {
|
|
||||||
const { data: { series, arcArea, arcGap }, centerPos, defaultArcRadiusArea, defaultArcGap } = this
|
|
||||||
|
|
||||||
const arcNum = series.length
|
|
||||||
|
|
||||||
const fullRadius = (centerPos[0] > centerPos[1] ? centerPos[1] : centerPos[0])
|
|
||||||
|
|
||||||
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 () {
|
|
||||||
const { data: { series } } = this
|
|
||||||
|
|
||||||
const fullRadian = Math.PI / 2 * 3
|
|
||||||
|
|
||||||
const offsetRadian = Math.PI * 0.5
|
|
||||||
|
|
||||||
this.arcRadian = new Array(series.length).fill(0).map((t, i) => series[i].value * fullRadian - offsetRadian)
|
|
||||||
},
|
|
||||||
calcArcColor () {
|
|
||||||
const { ctx, arcLineWidth, defaultArcColor, canvas: { getLinearGradientColor } } = this
|
|
||||||
|
|
||||||
const { drawColors, arcRadius: [ radius ], centerPos: [x, y] } = this
|
|
||||||
|
|
||||||
const colors = drawColors || defaultArcColor
|
|
||||||
|
|
||||||
this.arcColor = getLinearGradientColor(ctx,
|
|
||||||
[x, y - radius - arcLineWidth],
|
|
||||||
[x, y + radius + arcLineWidth], colors)
|
|
||||||
},
|
|
||||||
drawArc () {
|
|
||||||
const { ctx, arcRadius, arcRadian, centerPos, arcLineWidth, arcColor } = this
|
|
||||||
|
|
||||||
const offsetRadian = Math.PI / -2
|
|
||||||
|
|
||||||
ctx.lineWidth = arcLineWidth
|
|
||||||
ctx.strokeStyle = arcColor
|
|
||||||
|
|
||||||
arcRadius.forEach((radius, i) => {
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.arc(...centerPos, radius, offsetRadian, arcRadian[i])
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
drawTitle () {
|
|
||||||
const { ctx, data: { series, fontSize }, arcRadius, centerPos: [ x, y ], arcLineWidth } = this
|
|
||||||
|
|
||||||
const textEndX = x - 10
|
|
||||||
|
|
||||||
ctx.font = `${fontSize || arcLineWidth}px Arial`
|
|
||||||
ctx.textAlign = 'right'
|
|
||||||
ctx.textBaseline = 'middle'
|
|
||||||
ctx.fillStyle = '#fff'
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
series.forEach(({ title }, i) => {
|
|
||||||
ctx.fillText(title, textEndX, y - arcRadius[i])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.concentric-arc-chart {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.canvas-container {
|
|
||||||
position: relative;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
Before Width: | Height: | Size: 48 KiB |
|
@ -1,20 +1,177 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="decoration-1">
|
<div class="dv-decoration-1" :ref="ref">
|
||||||
<img src="./img/decoration.gif" />
|
<svg :width="`${svgWH[0]}px`" :height="`${svgWH[1]}px`" :style="`transform:scale(${svgScale[0]},${svgScale[1]});`">
|
||||||
|
|
||||||
|
<template
|
||||||
|
v-for="(point, i) in points"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
v-if="Math.random() > 0.6"
|
||||||
|
:key="i"
|
||||||
|
fill="#fff"
|
||||||
|
:x="point[0] - halfPointSideLength"
|
||||||
|
:y="point[1] - halfPointSideLength"
|
||||||
|
:width="pointSideLength"
|
||||||
|
:height="pointSideLength"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
v-if="Math.random() > 0.6"
|
||||||
|
attributeName="fill"
|
||||||
|
values="#fff;transparent"
|
||||||
|
dur="1s"
|
||||||
|
:begin="Math.random() * 2"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</rect>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<rect
|
||||||
|
v-if="rects[0]"
|
||||||
|
fill="#0de7c2"
|
||||||
|
:x="rects[0][0] - pointSideLength"
|
||||||
|
:y="rects[0][1] - pointSideLength"
|
||||||
|
:width="pointSideLength * 2"
|
||||||
|
:height="pointSideLength * 2"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="width"
|
||||||
|
:values="`0;${pointSideLength * 2}`"
|
||||||
|
dur="2s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="height"
|
||||||
|
:values="`0;${pointSideLength * 2}`"
|
||||||
|
dur="2s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="x"
|
||||||
|
:values="`${rects[0][0]};${rects[0][0] - pointSideLength}`"
|
||||||
|
dur="2s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="y"
|
||||||
|
:values="`${rects[0][1]};${rects[0][1] - pointSideLength}`"
|
||||||
|
dur="2s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</rect>
|
||||||
|
|
||||||
|
<rect
|
||||||
|
v-if="rects[1]"
|
||||||
|
fill="#0de7c2"
|
||||||
|
:x="rects[1][0] - 40"
|
||||||
|
:y="rects[1][1] - pointSideLength"
|
||||||
|
:width="40"
|
||||||
|
:height="pointSideLength * 2"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="width"
|
||||||
|
values="0;40;0"
|
||||||
|
dur="2s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="x"
|
||||||
|
:values="`${rects[1][0]};${rects[1][0] - 40};${rects[1][0]}`"
|
||||||
|
dur="2s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</rect>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Decoration1'
|
name: 'Decoration1',
|
||||||
|
mixins: [autoResize],
|
||||||
|
data () {
|
||||||
|
const pointSideLength = 2.5
|
||||||
|
|
||||||
|
return {
|
||||||
|
ref: 'decoration-1',
|
||||||
|
|
||||||
|
svgWH: [200, 50],
|
||||||
|
|
||||||
|
svgScale: [1, 1],
|
||||||
|
|
||||||
|
rowNum: 4,
|
||||||
|
rowPoints: 20,
|
||||||
|
|
||||||
|
pointSideLength,
|
||||||
|
halfPointSideLength: pointSideLength / 2,
|
||||||
|
|
||||||
|
points: [],
|
||||||
|
|
||||||
|
rects: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
afterAutoResizeMixinInit () {
|
||||||
|
const { calcSVGData } = this
|
||||||
|
|
||||||
|
calcSVGData()
|
||||||
|
},
|
||||||
|
calcSVGData () {
|
||||||
|
const { calcPointsPosition, calcRectsPosition, calcScale } = this
|
||||||
|
|
||||||
|
calcPointsPosition()
|
||||||
|
|
||||||
|
calcRectsPosition()
|
||||||
|
|
||||||
|
calcScale()
|
||||||
|
},
|
||||||
|
calcPointsPosition () {
|
||||||
|
const { svgWH, rowNum, rowPoints } = this
|
||||||
|
|
||||||
|
const [w, h] = svgWH
|
||||||
|
|
||||||
|
const horizontalGap = w / (rowPoints + 1)
|
||||||
|
const verticalGap = h / (rowNum + 1)
|
||||||
|
|
||||||
|
let points = new Array(rowNum).fill(0).map((foo, i) =>
|
||||||
|
new Array(rowPoints).fill(0).map((foo, j) => [
|
||||||
|
horizontalGap * (j + 1), verticalGap * (i + 1)
|
||||||
|
]))
|
||||||
|
|
||||||
|
this.points = points.reduce((all, item) => [...all, ...item], [])
|
||||||
|
},
|
||||||
|
calcRectsPosition () {
|
||||||
|
const { points, rowPoints } = this
|
||||||
|
|
||||||
|
const rect1 = points[rowPoints * 2 - 1]
|
||||||
|
const rect2 = points[rowPoints * 2 - 3]
|
||||||
|
|
||||||
|
this.rects = [rect1, rect2]
|
||||||
|
},
|
||||||
|
calcScale () {
|
||||||
|
const { width, height, svgWH } = this
|
||||||
|
|
||||||
|
const [w, h] = svgWH
|
||||||
|
|
||||||
|
this.svgScale = [width / w, height / h]
|
||||||
|
},
|
||||||
|
onResize () {
|
||||||
|
const { calcSVGData } = this
|
||||||
|
|
||||||
|
calcSVGData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.decoration-1 {
|
.dv-decoration-1 {
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
transform-origin: left top;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,77 +1,101 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="decoration-2" :ref="ref">
|
<div class="dv-decoration-2" :ref="ref">
|
||||||
<div :class="reverse ? 'reverse' : 'normal'" />
|
<svg :width="`${width}px`" :height="`${height}px`">
|
||||||
|
<rect :x="x" :y="y" :width="w" :height="h" fill="#3faacb">
|
||||||
|
<animate
|
||||||
|
:attributeName="reverse ? 'height' : 'width'"
|
||||||
|
from="0"
|
||||||
|
:to="reverse ? height : width"
|
||||||
|
dur="6s"
|
||||||
|
calcMode="spline"
|
||||||
|
keyTimes="0;1"
|
||||||
|
keySplines=".42,0,.58,1"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</rect>
|
||||||
|
|
||||||
|
<rect :x="x" :y="y" width="1" height="1" fill="#fff">
|
||||||
|
<animate
|
||||||
|
:attributeName="reverse ? 'y' : 'x'"
|
||||||
|
from="0"
|
||||||
|
:to="reverse ? height : width"
|
||||||
|
dur="6s"
|
||||||
|
calcMode="spline"
|
||||||
|
keyTimes="0;1"
|
||||||
|
keySplines=".42,0,.58,1"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</rect>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Decoration2',
|
name: 'Decoration2',
|
||||||
|
mixins: [autoResize],
|
||||||
|
props: {
|
||||||
|
reverse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
ref: `decoration-2-${(new Date()).getTime()}`,
|
ref: 'decoration-2',
|
||||||
width: 0,
|
|
||||||
height: 0
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
|
||||||
|
w: 0,
|
||||||
|
h: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
reverse () {
|
||||||
|
const { calcSVGData } = this
|
||||||
|
|
||||||
|
calcSVGData()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: ['reverse'],
|
|
||||||
methods: {
|
methods: {
|
||||||
init () {
|
afterAutoResizeMixinInit () {
|
||||||
const { $nextTick, $refs, ref } = this
|
const { calcSVGData } = this
|
||||||
|
|
||||||
$nextTick(e => {
|
calcSVGData()
|
||||||
this.width = $refs[ref].clientWidth
|
},
|
||||||
this.height = $refs[ref].clientHeight
|
calcSVGData () {
|
||||||
})
|
const { reverse, width, height } = this
|
||||||
|
|
||||||
|
if (reverse) {
|
||||||
|
this.w = 1
|
||||||
|
this.h = height
|
||||||
|
this.x = width / 2
|
||||||
|
this.y = 0
|
||||||
|
} else {
|
||||||
|
this.w = width
|
||||||
|
this.h = 1
|
||||||
|
this.x = 0
|
||||||
|
this.y = height / 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
onResize () {
|
||||||
const { init } = this
|
const { calcSVGData } = this
|
||||||
|
|
||||||
init()
|
calcSVGData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less">
|
||||||
.decoration-2 {
|
.dv-decoration-2 {
|
||||||
|
display: flex;
|
||||||
.reverse, .normal {
|
|
||||||
background-color: #3faacb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.normal {
|
|
||||||
width: 0%;
|
|
||||||
height: 1px;
|
|
||||||
border-right: 1px solid #fff;
|
|
||||||
animation: normal-amt 6s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reverse {
|
|
||||||
width: 1px;
|
|
||||||
height: 0%;
|
|
||||||
border-bottom: 1px solid #fff;
|
|
||||||
animation: reverse-amt 6s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes reverse-amt {
|
|
||||||
70% {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes normal-amt {
|
|
||||||
70% {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
100% {
|
align-items: center;
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Before Width: | Height: | Size: 86 KiB |
|
@ -1,20 +1,108 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="decoration-3">
|
<div class="dv-decoration-3" :ref="ref">
|
||||||
<img src="./img/decoration.gif" />
|
<svg :width="`${svgWH[0]}px`" :height="`${svgWH[1]}px`" :style="`transform:scale(${svgScale[0]},${svgScale[1]});`">
|
||||||
|
|
||||||
|
<template
|
||||||
|
v-for="(point, i) in points"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
:key="i"
|
||||||
|
fill="#7acaec"
|
||||||
|
:x="point[0] - halfPointSideLength"
|
||||||
|
:y="point[1] - halfPointSideLength"
|
||||||
|
:width="pointSideLength"
|
||||||
|
:height="pointSideLength"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
v-if="Math.random() > 0.6"
|
||||||
|
attributeName="fill"
|
||||||
|
values="#7acaec;transparent"
|
||||||
|
:dur="Math.random() + 1 + 's'"
|
||||||
|
:begin="Math.random() * 2"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</rect>
|
||||||
|
</template>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Decoration3'
|
name: 'Decoration3',
|
||||||
|
mixins: [autoResize],
|
||||||
|
data () {
|
||||||
|
const pointSideLength = 7
|
||||||
|
|
||||||
|
return {
|
||||||
|
ref: 'decoration-3',
|
||||||
|
|
||||||
|
svgWH: [300, 35],
|
||||||
|
|
||||||
|
svgScale: [1, 1],
|
||||||
|
|
||||||
|
rowNum: 2,
|
||||||
|
rowPoints: 25,
|
||||||
|
|
||||||
|
pointSideLength,
|
||||||
|
halfPointSideLength: pointSideLength / 2,
|
||||||
|
|
||||||
|
points: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
afterAutoResizeMixinInit () {
|
||||||
|
const { calcSVGData } = this
|
||||||
|
|
||||||
|
calcSVGData()
|
||||||
|
},
|
||||||
|
calcSVGData () {
|
||||||
|
const { calcPointsPosition, calcScale } = this
|
||||||
|
|
||||||
|
calcPointsPosition()
|
||||||
|
|
||||||
|
calcScale()
|
||||||
|
},
|
||||||
|
calcPointsPosition () {
|
||||||
|
const { svgWH, rowNum, rowPoints } = this
|
||||||
|
|
||||||
|
const [w, h] = svgWH
|
||||||
|
|
||||||
|
const horizontalGap = w / (rowPoints + 1)
|
||||||
|
const verticalGap = h / (rowNum + 1)
|
||||||
|
|
||||||
|
let points = new Array(rowNum).fill(0).map((foo, i) =>
|
||||||
|
new Array(rowPoints).fill(0).map((foo, j) => [
|
||||||
|
horizontalGap * (j + 1), verticalGap * (i + 1)
|
||||||
|
]))
|
||||||
|
|
||||||
|
this.points = points.reduce((all, item) => [...all, ...item], [])
|
||||||
|
},
|
||||||
|
calcScale () {
|
||||||
|
const { width, height, svgWH } = this
|
||||||
|
|
||||||
|
const [w, h] = svgWH
|
||||||
|
|
||||||
|
this.svgScale = [width / w, height / h]
|
||||||
|
},
|
||||||
|
onResize () {
|
||||||
|
const { calcSVGData } = this
|
||||||
|
|
||||||
|
calcSVGData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.decoration-3 {
|
.dv-decoration-3 {
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
transform-origin: left top;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,92 +1,66 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="`decoration-4 ${reverse ? 'reverse' : 'normal'}`" :ref="ref">
|
<div class="dv-decoration-4" :ref="ref">
|
||||||
<svg :class="`svg-container ${reverse ? 'ani-width' : 'ani-height'}`">
|
<div
|
||||||
<template v-if="!reverse">
|
:class="`container ${reverse ? 'reverse' : 'normal'}`"
|
||||||
<polyline class="lighter-line" :points="`3, 5 3, ${height - 5}`" />
|
:style="reverse ? `width:${width}px;height:5px` : `width:5px;height:${height}px;`"
|
||||||
<polyline class="bolder-line" :points="`3, 5 3, ${height - 5}`" />
|
>
|
||||||
</template>
|
<svg :width="reverse ? width : 5" :height="reverse ? 5 : height">
|
||||||
|
<polyline
|
||||||
<template v-else>
|
stroke="rgba(255, 255, 255, 0.3)"
|
||||||
<polyline class="lighter-line" :points="`5, 3 ${width - 5},3`" />
|
:points="reverse ? `0, 2.5 ${width}, 2.5` : `2.5, 0 2.5, ${height}`"
|
||||||
<polyline class="bolder-line" :points="`5, 3 ${width - 5},3`" />
|
/>
|
||||||
|
<polyline
|
||||||
<!-- <polyline class="lighter-line" :points="`5, 3 ${width - 5},3`" />
|
class="bold-line"
|
||||||
<polyline class="bolder-line" :points="`5, 3 ${width - 5},3`" /> -->
|
stroke="rgba(255, 255, 255, 0.3)"
|
||||||
</template>
|
stroke-width="3"
|
||||||
|
stroke-dasharray="20, 80"
|
||||||
|
stroke-dashoffset="-30"
|
||||||
|
:points="reverse ? `0, 2.5 ${width}, 2.5` : `2.5, 0 2.5, ${height}`"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Decoration4',
|
name: 'Decoration4',
|
||||||
|
mixins: [autoResize],
|
||||||
|
props: ['reverse'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
ref: `decoration-4-${(new Date()).getTime()}`,
|
ref: 'decoration-4'
|
||||||
width: 0,
|
|
||||||
height: 0
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
props: ['reverse'],
|
|
||||||
methods: {
|
|
||||||
init () {
|
|
||||||
const { $nextTick, $refs, ref } = this
|
|
||||||
|
|
||||||
$nextTick(e => {
|
|
||||||
this.width = $refs[ref].clientWidth
|
|
||||||
this.height = $refs[ref].clientHeight
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.decoration-4 {
|
.dv-decoration-4 {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&.normal {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.reverse {
|
|
||||||
height: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.svg-container {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
&.ani-height {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 0%;
|
|
||||||
animation: ani-height 3s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.ani-width {
|
|
||||||
width: 0%;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.normal {
|
||||||
|
height: 0% !important;
|
||||||
|
animation: ani-height 3s ease-in-out infinite;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reverse {
|
||||||
|
width: 0% !important;
|
||||||
animation: ani-width 3s ease-in-out infinite;
|
animation: ani-width 3s ease-in-out infinite;
|
||||||
}
|
top: 50%;
|
||||||
|
margin-top: -2px;
|
||||||
polyline {
|
|
||||||
fill: none;
|
|
||||||
stroke: fade(gray, 25);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lighter-line {
|
|
||||||
stroke-width: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bolder-line {
|
|
||||||
stroke-width: 3px;
|
|
||||||
stroke-dasharray: 20, 80;
|
|
||||||
stroke-dashoffset: -30;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes ani-height {
|
@keyframes ani-height {
|
||||||
|
|
Before Width: | Height: | Size: 20 KiB |
|
@ -1,22 +1,110 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="dv-decoration-5">
|
<div class="dv-decoration-5" :ref="ref">
|
||||||
<img src="./img/decoration.gif" />
|
<svg :width="width" :height="height">
|
||||||
|
<polyline
|
||||||
|
fill="transparent"
|
||||||
|
stroke="#3f96a5"
|
||||||
|
stroke-width="3"
|
||||||
|
:points="line1Points"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="stroke-dasharray"
|
||||||
|
attributeType="XML"
|
||||||
|
:from="`0, ${line1Length / 2}, 0, ${line1Length / 2}`"
|
||||||
|
:to="`0, 0, ${line1Length}, 0`"
|
||||||
|
dur="1.2s"
|
||||||
|
begin="0s"
|
||||||
|
calcMode="spline"
|
||||||
|
keyTimes="0;1"
|
||||||
|
keySplines=".4,1,.49,.98"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</polyline>
|
||||||
|
<polyline
|
||||||
|
fill="transparent"
|
||||||
|
stroke="#3f96a5"
|
||||||
|
stroke-width="2"
|
||||||
|
:points="line2Points"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="stroke-dasharray"
|
||||||
|
attributeType="XML"
|
||||||
|
:from="`0, ${line2Length / 2}, 0, ${line2Length / 2}`"
|
||||||
|
:to="`0, 0, ${line2Length}, 0`"
|
||||||
|
dur="1.2s"
|
||||||
|
begin="0s"
|
||||||
|
calcMode="spline"
|
||||||
|
keyTimes="0;1"
|
||||||
|
keySplines=".4,1,.49,.98"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</polyline>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
|
import { getPolylineLength } from '@jiaminghi/charts/lib/util'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Decoration5'
|
name: 'Decoration5',
|
||||||
|
mixins: [autoResize],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
ref: 'decoration-5',
|
||||||
|
|
||||||
|
line1Points: '',
|
||||||
|
line2Points: '',
|
||||||
|
|
||||||
|
line1Length: 0,
|
||||||
|
line2Length: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
afterAutoResizeMixinInit () {
|
||||||
|
const { calcSVGData } = this
|
||||||
|
|
||||||
|
calcSVGData()
|
||||||
|
},
|
||||||
|
calcSVGData () {
|
||||||
|
const { width, height } = this
|
||||||
|
|
||||||
|
let line1Points = [
|
||||||
|
[0, height * 0.2], [width * 0.18, height * 0.2], [width * 0.2, height * 0.4], [width * 0.25, height * 0.4],
|
||||||
|
[width * 0.27, height * 0.6], [width * 0.72, height * 0.6], [width * 0.75, height * 0.4],
|
||||||
|
[width * 0.8, height * 0.4], [width * 0.82, height * 0.2], [width, height * 0.2]
|
||||||
|
]
|
||||||
|
|
||||||
|
let line2Points = [
|
||||||
|
[width * 0.3, height * 0.8], [width * 0.7, height * 0.8]
|
||||||
|
]
|
||||||
|
|
||||||
|
const line1Length = getPolylineLength(line1Points)
|
||||||
|
const line2Length = getPolylineLength(line2Points)
|
||||||
|
|
||||||
|
line1Points = line1Points.map(point => point.join(',')).join(' ')
|
||||||
|
line2Points = line2Points.map(point => point.join(',')).join(' ')
|
||||||
|
|
||||||
|
this.line1Points = line1Points
|
||||||
|
this.line2Points = line2Points
|
||||||
|
|
||||||
|
this.line1Length = line1Length
|
||||||
|
this.line2Length = line2Length
|
||||||
|
},
|
||||||
|
onResize () {
|
||||||
|
const { calcSVGData } = this
|
||||||
|
|
||||||
|
calcSVGData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.dv-decoration-5 {
|
.dv-decoration-5 {
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: -21%;
|
height: 100%;
|
||||||
margin-bottom: -25%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Before Width: | Height: | Size: 242 KiB |
|
@ -1,20 +1,133 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="decoration-6">
|
<div class="dv-decoration-6" :ref="ref">
|
||||||
<img src="./img/decoration.gif" />
|
<svg :width="`${svgWH[0]}px`" :height="`${svgWH[1]}px`" :style="`transform:scale(${svgScale[0]},${svgScale[1]});`">
|
||||||
|
<template
|
||||||
|
v-for="(point, i) in points"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
:key="i"
|
||||||
|
fill="#7acaec"
|
||||||
|
:x="point[0] - halfRectWidth"
|
||||||
|
:y="point[1] - heights[i] / 2"
|
||||||
|
:width="rectWidth"
|
||||||
|
:height="heights[i]"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="y"
|
||||||
|
:values="`${point[1] - minHeights[i] / 2};${point[1] - heights[i] / 2};${point[1] - minHeights[i] / 2}`"
|
||||||
|
:dur="`${randoms[i]}s`"
|
||||||
|
keyTimes="0;.5;1"
|
||||||
|
calcMode="spline"
|
||||||
|
keySplines=".42,0,.58,1;.42,0,.58,1"
|
||||||
|
begin="0s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="height"
|
||||||
|
:values="`${minHeights[i]};${heights[i]};${minHeights[i]}`"
|
||||||
|
:dur="`${randoms[i]}s`"
|
||||||
|
keyTimes="0;.5;1"
|
||||||
|
calcMode="spline"
|
||||||
|
keySplines=".42,0,.58,1;.42,0,.58,1"
|
||||||
|
begin="0s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</rect>
|
||||||
|
</template>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
|
import { randomExtend } from '../../util'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Decoration6'
|
name: 'Decoration6',
|
||||||
|
mixins: [autoResize],
|
||||||
|
data () {
|
||||||
|
const rectWidth = 7
|
||||||
|
|
||||||
|
return {
|
||||||
|
ref: 'decoration-6',
|
||||||
|
|
||||||
|
svgWH: [300, 35],
|
||||||
|
|
||||||
|
svgScale: [1, 1],
|
||||||
|
|
||||||
|
rowNum: 1,
|
||||||
|
rowPoints: 40,
|
||||||
|
|
||||||
|
rectWidth,
|
||||||
|
halfRectWidth: rectWidth / 2,
|
||||||
|
|
||||||
|
points: [],
|
||||||
|
heights: [],
|
||||||
|
minHeights: [],
|
||||||
|
randoms: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
afterAutoResizeMixinInit () {
|
||||||
|
const { calcSVGData } = this
|
||||||
|
|
||||||
|
calcSVGData()
|
||||||
|
},
|
||||||
|
calcSVGData () {
|
||||||
|
const { calcPointsPosition, calcScale } = this
|
||||||
|
|
||||||
|
calcPointsPosition()
|
||||||
|
|
||||||
|
calcScale()
|
||||||
|
},
|
||||||
|
calcPointsPosition () {
|
||||||
|
const { svgWH, rowNum, rowPoints } = this
|
||||||
|
|
||||||
|
const [w, h] = svgWH
|
||||||
|
|
||||||
|
const horizontalGap = w / (rowPoints + 1)
|
||||||
|
const verticalGap = h / (rowNum + 1)
|
||||||
|
|
||||||
|
let points = new Array(rowNum).fill(0).map((foo, i) =>
|
||||||
|
new Array(rowPoints).fill(0).map((foo, j) => [
|
||||||
|
horizontalGap * (j + 1), verticalGap * (i + 1)
|
||||||
|
]))
|
||||||
|
|
||||||
|
this.points = points.reduce((all, item) => [...all, ...item], [])
|
||||||
|
const heights = this.heights = new Array(rowNum * rowPoints)
|
||||||
|
.fill(0).map(foo =>
|
||||||
|
Math.random() > 0.8 ? randomExtend(0.7 * h, h) : randomExtend(0.2 * h, 0.5 * h))
|
||||||
|
|
||||||
|
this.minHeights = new Array(rowNum * rowPoints)
|
||||||
|
.fill(0).map((foo, i) => heights[i] * Math.random())
|
||||||
|
|
||||||
|
this.randoms = new Array(rowNum * rowPoints)
|
||||||
|
.fill(0).map(foo => Math.random() + 1.5)
|
||||||
|
},
|
||||||
|
calcScale () {
|
||||||
|
const { width, height, svgWH } = this
|
||||||
|
|
||||||
|
const [w, h] = svgWH
|
||||||
|
|
||||||
|
this.svgScale = [width / w, height / h]
|
||||||
|
},
|
||||||
|
onResize () {
|
||||||
|
const { calcSVGData } = this
|
||||||
|
|
||||||
|
calcSVGData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.decoration-6 {
|
.dv-decoration-6 {
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
transform-origin: left top;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Before Width: | Height: | Size: 496 B |
|
@ -1,8 +1,34 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="decoration-7">
|
<div class="dv-decoration-7">
|
||||||
<img src="./img/decoration.png" />
|
<svg width="21px" height="20px">
|
||||||
|
<polyline
|
||||||
|
stroke-width="4"
|
||||||
|
fill="transparent"
|
||||||
|
stroke="#1dc1f5"
|
||||||
|
points="10, 0 19, 10 10, 20"
|
||||||
|
/>
|
||||||
|
<polyline
|
||||||
|
stroke-width="2"
|
||||||
|
fill="transparent"
|
||||||
|
stroke="#1dc1f5"
|
||||||
|
points="2, 0 11, 10 2, 20"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<img class="reverse" src="./img/decoration.png" />
|
<svg width="21px" height="20px">
|
||||||
|
<polyline
|
||||||
|
stroke-width="4"
|
||||||
|
fill="transparent"
|
||||||
|
stroke="#1dc1f5"
|
||||||
|
points="11, 0 2, 10 11, 20"
|
||||||
|
/>
|
||||||
|
<polyline
|
||||||
|
stroke-width="2"
|
||||||
|
fill="transparent"
|
||||||
|
stroke="#1dc1f5"
|
||||||
|
points="19, 0 10, 10 19, 20"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -13,14 +39,11 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.decoration-7 {
|
.dv-decoration-7 {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.reverse {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<div class="dv-decoration-8" :ref="ref">
|
||||||
|
<svg>
|
||||||
|
<polyline
|
||||||
|
stroke="#3f96a5"
|
||||||
|
stroke-width="2"
|
||||||
|
fill="transparent"
|
||||||
|
:points="`${xPos(0)}, 0 ${xPos(30)}, ${height / 2}`"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<polyline
|
||||||
|
stroke="#3f96a5"
|
||||||
|
stroke-width="2"
|
||||||
|
fill="transparent"
|
||||||
|
:points="`${xPos(20)}, 0 ${xPos(50)}, ${height / 2} ${xPos(width)}, ${height / 2}`"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<polyline
|
||||||
|
stroke="#3f96a5"
|
||||||
|
fill="transparent"
|
||||||
|
stroke-width="3"
|
||||||
|
:points="`${xPos(0)}, ${height - 3}, ${xPos(200)}, ${height - 3}`"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Decoration8',
|
||||||
|
mixins: [autoResize],
|
||||||
|
props: {
|
||||||
|
reverse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
ref: 'decoration-8',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
xPos (pos) {
|
||||||
|
const { reverse, width } = this
|
||||||
|
|
||||||
|
if (!reverse) return pos
|
||||||
|
|
||||||
|
return width - pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.dv-decoration-8 {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,194 @@
|
||||||
|
<template>
|
||||||
|
<div class="dv-digital-flop">
|
||||||
|
<canvas ref="digital-flop" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CRender from '@jiaminghi/c-render'
|
||||||
|
|
||||||
|
import '@jiaminghi/charts/lib/extend/index'
|
||||||
|
|
||||||
|
import { deepMerge } from '@jiaminghi/charts/lib/util/index'
|
||||||
|
|
||||||
|
import { deepClone } from '@jiaminghi/c-render/lib/plugin/util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DigitalFlop',
|
||||||
|
props: {
|
||||||
|
config: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
render: null,
|
||||||
|
|
||||||
|
defaultConfig: {
|
||||||
|
/**
|
||||||
|
* @description Number for digital flop
|
||||||
|
* @type {Array<Number>}
|
||||||
|
* @default number = []
|
||||||
|
* @example number = [10]
|
||||||
|
*/
|
||||||
|
number: [],
|
||||||
|
/**
|
||||||
|
* @description Content formatter
|
||||||
|
* @type {String}
|
||||||
|
* @default content = ''
|
||||||
|
* @example content = '{nt}个'
|
||||||
|
*/
|
||||||
|
content: '',
|
||||||
|
/**
|
||||||
|
* @description Number toFixed
|
||||||
|
* @type {Number}
|
||||||
|
* @default toFixed = 0
|
||||||
|
*/
|
||||||
|
toFixed: 0,
|
||||||
|
/**
|
||||||
|
* @description Text align
|
||||||
|
* @type {String}
|
||||||
|
* @default textAlign = 'center'
|
||||||
|
* @example textAlign = 'center' | 'left' | 'right'
|
||||||
|
*/
|
||||||
|
textAlign: 'center',
|
||||||
|
/**
|
||||||
|
* @description Text style configuration
|
||||||
|
* @type {Object} {CRender Class Style}
|
||||||
|
*/
|
||||||
|
style: {
|
||||||
|
fontSize: 30,
|
||||||
|
fill: '#3de7c9'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description CRender animationCurve
|
||||||
|
* @type {String}
|
||||||
|
* @default animationCurve = 'easeOutCubic'
|
||||||
|
*/
|
||||||
|
animationCurve: 'easeOutCubic',
|
||||||
|
/**
|
||||||
|
* @description CRender animationFrame
|
||||||
|
* @type {String}
|
||||||
|
* @default animationFrame = 50
|
||||||
|
*/
|
||||||
|
animationFrame: 50
|
||||||
|
},
|
||||||
|
|
||||||
|
mergedConfig: null,
|
||||||
|
|
||||||
|
graph: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
config () {
|
||||||
|
const { update } = this
|
||||||
|
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
const { initRender, mergeConfig, initGraph } = this
|
||||||
|
|
||||||
|
initRender()
|
||||||
|
|
||||||
|
mergeConfig()
|
||||||
|
|
||||||
|
initGraph()
|
||||||
|
},
|
||||||
|
initRender () {
|
||||||
|
const { $refs } = this
|
||||||
|
|
||||||
|
this.render = new CRender($refs['digital-flop'])
|
||||||
|
},
|
||||||
|
mergeConfig () {
|
||||||
|
const { defaultConfig, config } = this
|
||||||
|
|
||||||
|
this.mergedConfig = deepMerge(deepClone(defaultConfig, true), config || {})
|
||||||
|
},
|
||||||
|
initGraph () {
|
||||||
|
const { getShape, getStyle, render, mergedConfig } = this
|
||||||
|
|
||||||
|
const { animationCurve, animationFrame } = mergedConfig
|
||||||
|
|
||||||
|
const shape = getShape()
|
||||||
|
const style = getStyle()
|
||||||
|
|
||||||
|
this.graph = render.add({
|
||||||
|
name: 'numberText',
|
||||||
|
animationCurve,
|
||||||
|
animationFrame,
|
||||||
|
shape,
|
||||||
|
style
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getShape () {
|
||||||
|
const { number, content, toFixed, textAlign } = this.mergedConfig
|
||||||
|
|
||||||
|
const [w, h] = this.render.area
|
||||||
|
|
||||||
|
const position = [w / 2, h / 2]
|
||||||
|
|
||||||
|
if (textAlign === 'left') position[0] = 0
|
||||||
|
if (textAlign === 'right') position[0] = w
|
||||||
|
|
||||||
|
return {
|
||||||
|
number,
|
||||||
|
content,
|
||||||
|
toFixed,
|
||||||
|
position
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStyle () {
|
||||||
|
const { style, textAlign } = this.mergedConfig
|
||||||
|
|
||||||
|
return deepMerge(style, {
|
||||||
|
textAlign,
|
||||||
|
textBaseline: 'middle'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
update () {
|
||||||
|
const { mergeConfig, mergeShape, getShape, getStyle, graph, mergedConfig } = this
|
||||||
|
|
||||||
|
mergeConfig()
|
||||||
|
|
||||||
|
if (!graph) return
|
||||||
|
|
||||||
|
const { animationCurve, animationFrame } = mergedConfig
|
||||||
|
|
||||||
|
const shape = getShape()
|
||||||
|
const style = getStyle()
|
||||||
|
|
||||||
|
mergeShape(graph, shape)
|
||||||
|
|
||||||
|
graph.animationCurve = animationCurve
|
||||||
|
graph.animationFrame = animationFrame
|
||||||
|
|
||||||
|
graph.animation('style', style, true)
|
||||||
|
graph.animation('shape', shape)
|
||||||
|
},
|
||||||
|
mergeShape (graph, shape) {
|
||||||
|
const cacheNum = graph.shape.number.length
|
||||||
|
const shapeNum = shape.number.length
|
||||||
|
|
||||||
|
if (cacheNum !== shapeNum) graph.shape.number = shape.number
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
const { init } = this
|
||||||
|
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.dv-digital-flop {
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,509 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="dv-flyline-chart"
|
||||||
|
ref="dv-flyline-chart"
|
||||||
|
:style="`background-image: url(${mergedConfig ? mergedConfig.bgImgUrl : ''})`"
|
||||||
|
@click="consoleClickPos"
|
||||||
|
>
|
||||||
|
<svg v-if="mergedConfig" :width="width" :height="height">
|
||||||
|
<defs>
|
||||||
|
<radialGradient
|
||||||
|
:id="gradientId"
|
||||||
|
cx="50%" cy="50%" r="50%"
|
||||||
|
>
|
||||||
|
<stop
|
||||||
|
offset="0%" stop-color="#fff"
|
||||||
|
stop-opacity="1"
|
||||||
|
/>
|
||||||
|
<stop
|
||||||
|
offset="100%" stop-color="#fff"
|
||||||
|
stop-opacity="0"
|
||||||
|
/>
|
||||||
|
</radialGradient>
|
||||||
|
|
||||||
|
<radialGradient
|
||||||
|
:id="gradient2Id"
|
||||||
|
cx="50%" cy="50%" r="50%"
|
||||||
|
>
|
||||||
|
<stop
|
||||||
|
offset="0%" stop-color="#fff"
|
||||||
|
stop-opacity="0"
|
||||||
|
/>
|
||||||
|
<stop
|
||||||
|
offset="100%" stop-color="#fff"
|
||||||
|
stop-opacity="1"
|
||||||
|
/>
|
||||||
|
</radialGradient>
|
||||||
|
|
||||||
|
<circle
|
||||||
|
v-if="paths[0]"
|
||||||
|
:id="`circle${paths[0].toString()}`"
|
||||||
|
:cx="paths[0][2][0]"
|
||||||
|
:cy="paths[0][2][1]"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="r"
|
||||||
|
:values="`1;${mergedConfig.halo.radius}`"
|
||||||
|
:dur="mergedConfig.halo.duration / 10 + 's'"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="opacity"
|
||||||
|
values="1;0"
|
||||||
|
:dur="mergedConfig.halo.duration / 10 + 's'"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<image
|
||||||
|
v-if="paths[0]"
|
||||||
|
:xlink:href="mergedConfig.centerPointImg.url"
|
||||||
|
:width="mergedConfig.centerPointImg.width"
|
||||||
|
:height="mergedConfig.centerPointImg.height"
|
||||||
|
:x="paths[0][2][0] - mergedConfig.centerPointImg.width / 2"
|
||||||
|
:y="paths[0][2][1] - mergedConfig.centerPointImg.height / 2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<mask :id="`maskhalo${paths[0].toString()}`">
|
||||||
|
<use
|
||||||
|
v-if="paths[0]"
|
||||||
|
:xlink:href="`#circle${paths[0].toString()}`"
|
||||||
|
:fill="`url(#${gradient2Id})`"
|
||||||
|
/>
|
||||||
|
</mask>
|
||||||
|
|
||||||
|
<use
|
||||||
|
v-if="paths[0] && mergedConfig.halo.show"
|
||||||
|
:xlink:href="`#circle${paths[0].toString()}`"
|
||||||
|
:fill="mergedConfig.halo.color"
|
||||||
|
:mask="`url(#maskhalo${paths[0].toString()})`"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<g
|
||||||
|
v-for="(path, i) in paths"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
|
<defs>
|
||||||
|
<path
|
||||||
|
:id="`path${path.toString()}`"
|
||||||
|
:ref="`path${i}`"
|
||||||
|
:d="`M${path[0].toString()} Q${path[1].toString()} ${path[2].toString()}`"
|
||||||
|
fill="transparent"
|
||||||
|
/>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<use
|
||||||
|
:xlink:href="`#path${path.toString()}`"
|
||||||
|
:stroke-width="mergedConfig.lineWidth"
|
||||||
|
:stroke="mergedConfig.orbitColor"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<use
|
||||||
|
v-if="lengths[i]"
|
||||||
|
:xlink:href="`#path${path.toString()}`"
|
||||||
|
:stroke-width="mergedConfig.lineWidth"
|
||||||
|
:stroke="mergedConfig.flylineColor"
|
||||||
|
:mask="`url(#mask${unique}${path.toString()})`"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="stroke-dasharray"
|
||||||
|
:from="`0, ${lengths[i]}`"
|
||||||
|
:to="`${lengths[i]}, 0`"
|
||||||
|
:dur="times[i] || 0"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</use>
|
||||||
|
|
||||||
|
<mask :id="`mask${unique}${path.toString()}`">
|
||||||
|
<circle cx="0" cy="0" :r="mergedConfig.flylineRadius" :fill="`url(#${gradientId})`">
|
||||||
|
<animateMotion
|
||||||
|
:dur="times[i] || 0"
|
||||||
|
:path="`M${path[0].toString()} Q${path[1].toString()} ${path[2].toString()}`"
|
||||||
|
rotate="auto"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
</mask>
|
||||||
|
|
||||||
|
<image
|
||||||
|
:xlink:href="mergedConfig.pointsImg.url"
|
||||||
|
:width="mergedConfig.pointsImg.width"
|
||||||
|
:height="mergedConfig.pointsImg.height"
|
||||||
|
:x="path[0][0] - mergedConfig.pointsImg.width / 2"
|
||||||
|
:y="path[0][1] - mergedConfig.pointsImg.height / 2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<text
|
||||||
|
:style="`fontSize:${mergedConfig.text.fontSize}px;`"
|
||||||
|
:fill="mergedConfig.text.color"
|
||||||
|
:x="path[0][0] + mergedConfig.text.offset[0]"
|
||||||
|
:y="path[0][1] + mergedConfig.text.offset[1]"
|
||||||
|
>
|
||||||
|
{{ texts[i] }}
|
||||||
|
</text>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { deepMerge } from '@jiaminghi/charts/lib/util/index'
|
||||||
|
|
||||||
|
import { deepClone } from '@jiaminghi/c-render/lib/plugin/util'
|
||||||
|
|
||||||
|
import { randomExtend } from '../../util/index'
|
||||||
|
|
||||||
|
import autoResize from '../../mixins/autoResize.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PercentPond',
|
||||||
|
mixins: [autoResize],
|
||||||
|
props: {
|
||||||
|
config: {
|
||||||
|
type: Object,
|
||||||
|
default: {}
|
||||||
|
},
|
||||||
|
dev: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
ref: 'dv-flyline-chart',
|
||||||
|
unique: Math.random(),
|
||||||
|
maskId: `flyline-mask-id-${(new Date()).getTime()}`,
|
||||||
|
maskCircleId: `mask-circle-id-${(new Date()).getTime()}`,
|
||||||
|
gradientId: `gradient-id-${(new Date()).getTime()}`,
|
||||||
|
gradient2Id: `gradient2-id-${(new Date()).getTime()}`,
|
||||||
|
|
||||||
|
defaultConfig: {
|
||||||
|
/**
|
||||||
|
* @description Flyline chart center point
|
||||||
|
* @type {Array<Number>}
|
||||||
|
* @default centerPoint = [0, 0]
|
||||||
|
*/
|
||||||
|
centerPoint: [0, 0],
|
||||||
|
/**
|
||||||
|
* @description Flyline start points
|
||||||
|
* @type {Array<Array<Number>>}
|
||||||
|
* @default points = []
|
||||||
|
* @example points = [[10, 10], [100, 100]]
|
||||||
|
*/
|
||||||
|
points: [],
|
||||||
|
/**
|
||||||
|
* @description Flyline width
|
||||||
|
* @type {Number}
|
||||||
|
* @default lineWidth = 1
|
||||||
|
*/
|
||||||
|
lineWidth: 1,
|
||||||
|
/**
|
||||||
|
* @description Orbit color
|
||||||
|
* @type {String}
|
||||||
|
* @default orbitColor = 'rgba(103, 224, 227, .2)'
|
||||||
|
*/
|
||||||
|
orbitColor: 'rgba(103, 224, 227, .2)',
|
||||||
|
/**
|
||||||
|
* @description Flyline color
|
||||||
|
* @type {String}
|
||||||
|
* @default orbitColor = '#ffde93'
|
||||||
|
*/
|
||||||
|
flylineColor: '#ffde93',
|
||||||
|
/**
|
||||||
|
* @description K value
|
||||||
|
* @type {Number}
|
||||||
|
* @default k = -0.5
|
||||||
|
* @example k = -1 ~ 1
|
||||||
|
*/
|
||||||
|
k: -0.5,
|
||||||
|
/**
|
||||||
|
* @description Flyline curvature
|
||||||
|
* @type {Number}
|
||||||
|
* @default curvature = 5
|
||||||
|
*/
|
||||||
|
curvature: 5,
|
||||||
|
/**
|
||||||
|
* @description Flyline radius
|
||||||
|
* @type {Number}
|
||||||
|
* @default flylineRadius = 100
|
||||||
|
*/
|
||||||
|
flylineRadius: 100,
|
||||||
|
/**
|
||||||
|
* @description Flyline animation duration
|
||||||
|
* @type {Array<Number>}
|
||||||
|
* @default duration = [20, 30]
|
||||||
|
*/
|
||||||
|
duration: [20, 30],
|
||||||
|
/**
|
||||||
|
* @description Relative points position
|
||||||
|
* @type {Boolean}
|
||||||
|
* @default relative = true
|
||||||
|
*/
|
||||||
|
relative: true,
|
||||||
|
/**
|
||||||
|
* @description Back ground image url
|
||||||
|
* @type {String}
|
||||||
|
* @default bgImgUrl = ''
|
||||||
|
* @example bgImgUrl = './img/bg.jpg'
|
||||||
|
*/
|
||||||
|
bgImgUrl: '',
|
||||||
|
/**
|
||||||
|
* @description Text configuration
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
text: {
|
||||||
|
/**
|
||||||
|
* @description Text offset
|
||||||
|
* @type {Array<Number>}
|
||||||
|
* @default offset = [0, 15]
|
||||||
|
*/
|
||||||
|
offset: [0, 15],
|
||||||
|
/**
|
||||||
|
* @description Text color
|
||||||
|
* @type {String}
|
||||||
|
* @default color = '#ffdb5c'
|
||||||
|
*/
|
||||||
|
color: '#ffdb5c',
|
||||||
|
/**
|
||||||
|
* @description Text font size
|
||||||
|
* @type {Number}
|
||||||
|
* @default fontSize = 12
|
||||||
|
*/
|
||||||
|
fontSize: 12
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description Halo configuration
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
halo: {
|
||||||
|
/**
|
||||||
|
* @description Weather to show halo
|
||||||
|
* @type {Boolean}
|
||||||
|
* @default show = true
|
||||||
|
* @example show = true | false
|
||||||
|
*/
|
||||||
|
show: true,
|
||||||
|
/**
|
||||||
|
* @description Halo animation duration (10 = 1s)
|
||||||
|
* @type {Number}
|
||||||
|
* @default duration = 30
|
||||||
|
*/
|
||||||
|
duration: 30,
|
||||||
|
/**
|
||||||
|
* @description Halo color
|
||||||
|
* @type {String}
|
||||||
|
* @default color = '#fb7293'
|
||||||
|
*/
|
||||||
|
color: '#fb7293',
|
||||||
|
/**
|
||||||
|
* @description Halo max radius
|
||||||
|
* @type {Number}
|
||||||
|
* @default radius = 120
|
||||||
|
*/
|
||||||
|
radius: 120
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description Center point img configuration
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
centerPointImg: {
|
||||||
|
/**
|
||||||
|
* @description Center point img width
|
||||||
|
* @type {Number}
|
||||||
|
* @default width = 40
|
||||||
|
*/
|
||||||
|
width: 40,
|
||||||
|
/**
|
||||||
|
* @description Center point img height
|
||||||
|
* @type {Number}
|
||||||
|
* @default height = 40
|
||||||
|
*/
|
||||||
|
height: 40,
|
||||||
|
/**
|
||||||
|
* @description Center point img url
|
||||||
|
* @type {String}
|
||||||
|
* @default url = ''
|
||||||
|
*/
|
||||||
|
url: ''
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description Points img configuration
|
||||||
|
* @type {Object}
|
||||||
|
* @default radius = 120
|
||||||
|
*/
|
||||||
|
pointsImg: {
|
||||||
|
/**
|
||||||
|
* @description Points img width
|
||||||
|
* @type {Number}
|
||||||
|
* @default width = 15
|
||||||
|
*/
|
||||||
|
width: 15,
|
||||||
|
/**
|
||||||
|
* @description Points img height
|
||||||
|
* @type {Number}
|
||||||
|
* @default height = 15
|
||||||
|
*/
|
||||||
|
height: 15,
|
||||||
|
/**
|
||||||
|
* @description Points img url
|
||||||
|
* @type {String}
|
||||||
|
* @default url = ''
|
||||||
|
*/
|
||||||
|
url: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mergedConfig: null,
|
||||||
|
|
||||||
|
paths: [],
|
||||||
|
lengths: [],
|
||||||
|
times: [],
|
||||||
|
texts: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
config () {
|
||||||
|
const { calcData } = this
|
||||||
|
|
||||||
|
calcData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
afterAutoResizeMixinInit () {
|
||||||
|
const { calcData } = this
|
||||||
|
|
||||||
|
calcData()
|
||||||
|
},
|
||||||
|
onResize () {
|
||||||
|
const { calcData } = this
|
||||||
|
|
||||||
|
calcData()
|
||||||
|
},
|
||||||
|
async calcData () {
|
||||||
|
const { mergeConfig, createFlylinePaths, calcLineLengths } = this
|
||||||
|
|
||||||
|
mergeConfig()
|
||||||
|
|
||||||
|
createFlylinePaths()
|
||||||
|
|
||||||
|
await calcLineLengths()
|
||||||
|
|
||||||
|
const { calcTimes, calcTexts } = this
|
||||||
|
|
||||||
|
calcTimes()
|
||||||
|
|
||||||
|
calcTexts()
|
||||||
|
},
|
||||||
|
mergeConfig () {
|
||||||
|
let { config, defaultConfig } = this
|
||||||
|
|
||||||
|
const mergedConfig = deepMerge(deepClone(defaultConfig, true), config || {})
|
||||||
|
|
||||||
|
const { points } = mergedConfig
|
||||||
|
|
||||||
|
mergedConfig.points = points.map(item => {
|
||||||
|
if (item instanceof Array) {
|
||||||
|
return { position: item, text: '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
|
||||||
|
this.mergedConfig = mergedConfig
|
||||||
|
},
|
||||||
|
createFlylinePaths () {
|
||||||
|
const { getPath, mergedConfig, width, height } = this
|
||||||
|
|
||||||
|
let { centerPoint, points, relative } = mergedConfig
|
||||||
|
|
||||||
|
points = points.map(({ position }) => position)
|
||||||
|
|
||||||
|
if (relative) {
|
||||||
|
centerPoint = [width * centerPoint[0], height * centerPoint[1]]
|
||||||
|
points = points.map(([x, y]) => [width * x, height * y])
|
||||||
|
}
|
||||||
|
|
||||||
|
this.paths = points.map(point => getPath(centerPoint, point))
|
||||||
|
},
|
||||||
|
getPath (center, point) {
|
||||||
|
const { getControlPoint } = this
|
||||||
|
|
||||||
|
const controlPoint = getControlPoint(center, point)
|
||||||
|
|
||||||
|
return [point, controlPoint, center]
|
||||||
|
},
|
||||||
|
getControlPoint ([sx, sy], [ex, ey]) {
|
||||||
|
const { getPointDistance, getKLinePointByx, mergedConfig } = this
|
||||||
|
|
||||||
|
const { curvature, k } = mergedConfig
|
||||||
|
|
||||||
|
const [mx, my] = [(sx + ex) / 2, (sy + ey) / 2]
|
||||||
|
|
||||||
|
const distance = getPointDistance([sx, sy], [ex, ey])
|
||||||
|
|
||||||
|
const targetLength = distance / curvature
|
||||||
|
const disDived = targetLength / 2
|
||||||
|
|
||||||
|
let [dx, dy] = [mx, my]
|
||||||
|
|
||||||
|
do {
|
||||||
|
dx += disDived
|
||||||
|
dy = getKLinePointByx(k, [mx, my], dx)[1]
|
||||||
|
} while (getPointDistance([mx, my], [dx, dy]) < targetLength)
|
||||||
|
|
||||||
|
return [dx, dy]
|
||||||
|
},
|
||||||
|
getKLinePointByx (k, [lx, ly], x) {
|
||||||
|
const y = ly - k * lx + k * x
|
||||||
|
|
||||||
|
return [x, y]
|
||||||
|
},
|
||||||
|
async calcLineLengths () {
|
||||||
|
const { $nextTick, paths, $refs } = this
|
||||||
|
|
||||||
|
await $nextTick()
|
||||||
|
|
||||||
|
this.lengths = paths.map((foo, i) => $refs[`path${i}`][0].getTotalLength())
|
||||||
|
},
|
||||||
|
calcTimes () {
|
||||||
|
const { duration, points } = this.mergedConfig
|
||||||
|
|
||||||
|
this.times = points.map(foo => randomExtend(...duration) / 10)
|
||||||
|
},
|
||||||
|
calcTexts () {
|
||||||
|
const { points } = this.mergedConfig
|
||||||
|
|
||||||
|
this.texts = points.map(({ text }) => text)
|
||||||
|
},
|
||||||
|
consoleClickPos ({ offsetX, offsetY }) {
|
||||||
|
const { width, height, dev } = this
|
||||||
|
|
||||||
|
if (!dev) return
|
||||||
|
|
||||||
|
const relativeX = (offsetX / width).toFixed(2)
|
||||||
|
const relativeY = (offsetY / height).toFixed(2)
|
||||||
|
|
||||||
|
console.warn(`dv-flyline-chart DEV: \n Click Position is [${offsetX}, ${offsetY}] \n Relative Position is [${relativeX}, ${relativeY}]`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.dv-flyline-chart {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
|
||||||
|
polyline {
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
text {
|
||||||
|
text-anchor: middle;
|
||||||
|
dominant-baseline: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,21 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="for-slot">
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'ForSlot'
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.for-slot {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
import fullScreenContainer from './fullScreenContainer'
|
||||||
|
import loading from './loading/index.vue'
|
||||||
|
|
||||||
|
// border box
|
||||||
import borderBox1 from './borderBox1/index'
|
import borderBox1 from './borderBox1/index'
|
||||||
import borderBox2 from './borderBox2/index'
|
import borderBox2 from './borderBox2/index'
|
||||||
import borderBox3 from './borderBox3/index'
|
import borderBox3 from './borderBox3/index'
|
||||||
|
@ -5,7 +9,9 @@ import borderBox4 from './borderBox4/index'
|
||||||
import borderBox5 from './borderBox5/index'
|
import borderBox5 from './borderBox5/index'
|
||||||
import borderBox6 from './borderBox6/index'
|
import borderBox6 from './borderBox6/index'
|
||||||
import borderBox7 from './borderBox7/index'
|
import borderBox7 from './borderBox7/index'
|
||||||
|
import borderBox8 from './borderBox8/index'
|
||||||
|
|
||||||
|
// decoration
|
||||||
import decoration1 from './decoration1/index'
|
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'
|
||||||
|
@ -13,61 +19,51 @@ import decoration4 from './decoration4/index'
|
||||||
import decoration5 from './decoration5/index'
|
import decoration5 from './decoration5/index'
|
||||||
import decoration6 from './decoration6/index'
|
import decoration6 from './decoration6/index'
|
||||||
import decoration7 from './decoration7/index'
|
import decoration7 from './decoration7/index'
|
||||||
import loading from './loading/index.vue'
|
import decoration8 from './decoration8/index'
|
||||||
|
|
||||||
import capsuleChart from './capsuleChart/index.vue'
|
// charts
|
||||||
import ringChart from './ringChart/index.vue'
|
import charts from './charts/index.vue'
|
||||||
import polylineChart from './polylineChart/index.vue'
|
|
||||||
import concentricArcChart from './concentricArcChart/index.vue'
|
|
||||||
import arcRingChart from './arcRingChart/index.vue'
|
|
||||||
import radarChart from './radarChart/index.vue'
|
|
||||||
import columnChart from './columnChart/index.vue'
|
|
||||||
import pointChart from './pointChart/index.vue'
|
|
||||||
|
|
||||||
import numberShow from './numberShow/index.vue'
|
import activeRingChart from './activeRingChart'
|
||||||
import percentPond from './percentPond/index.vue'
|
|
||||||
import percentArc from './percentArc/index.vue'
|
|
||||||
import waterLevelPond from './waterLevelPond/index.vue'
|
import waterLevelPond from './waterLevelPond/index.vue'
|
||||||
|
import percentPond from './percentPond/index.vue'
|
||||||
|
import flylineChart from './flylineChart'
|
||||||
|
import digitalFlop from './digitalFlop'
|
||||||
import scrollBoard from './scrollBoard/index.vue'
|
import scrollBoard from './scrollBoard/index.vue'
|
||||||
|
|
||||||
import fullScreenContainer from './fullScreenContainer'
|
|
||||||
import labelLine from './labelLine'
|
|
||||||
import forSlot from './forSlot'
|
|
||||||
|
|
||||||
export default function (Vue) {
|
export default function (Vue) {
|
||||||
Vue.component('borderBox1', borderBox1)
|
Vue.component('dvFullScreenContainer', fullScreenContainer)
|
||||||
Vue.component('borderBox2', borderBox2)
|
|
||||||
Vue.component('borderBox3', borderBox3)
|
|
||||||
Vue.component('borderBox4', borderBox4)
|
|
||||||
Vue.component('borderBox5', borderBox5)
|
|
||||||
Vue.component('borderBox6', borderBox6)
|
|
||||||
Vue.component('borderBox7', borderBox7)
|
|
||||||
|
|
||||||
Vue.component('decoration1', decoration1)
|
Vue.component('dvLoading', loading)
|
||||||
Vue.component('decoration2', decoration2)
|
|
||||||
Vue.component('decoration3', decoration3)
|
|
||||||
Vue.component('decoration4', decoration4)
|
|
||||||
Vue.component('decoration5', decoration5)
|
|
||||||
Vue.component('decoration6', decoration6)
|
|
||||||
Vue.component('decoration7', decoration7)
|
|
||||||
Vue.component('loading', loading)
|
|
||||||
|
|
||||||
Vue.component('capsuleChart', capsuleChart)
|
// border box
|
||||||
Vue.component('polylineChart', polylineChart)
|
Vue.component('dvBorderBox1', borderBox1)
|
||||||
Vue.component('ringChart', ringChart)
|
Vue.component('dvBorderBox2', borderBox2)
|
||||||
Vue.component('concentricArcChart', concentricArcChart)
|
Vue.component('dvBorderBox3', borderBox3)
|
||||||
Vue.component('arcRingChart', arcRingChart)
|
Vue.component('dvBorderBox4', borderBox4)
|
||||||
Vue.component('radarChart', radarChart)
|
Vue.component('dvBorderBox5', borderBox5)
|
||||||
Vue.component('columnChart', columnChart)
|
Vue.component('dvBorderBox6', borderBox6)
|
||||||
Vue.component('pointChart', pointChart)
|
Vue.component('dvBorderBox7', borderBox7)
|
||||||
|
Vue.component('dvBorderBox8', borderBox8)
|
||||||
|
|
||||||
Vue.component('numberShow', numberShow)
|
// decoration
|
||||||
Vue.component('percentPond', percentPond)
|
Vue.component('dvDecoration1', decoration1)
|
||||||
Vue.component('percentArc', percentArc)
|
Vue.component('dvDecoration2', decoration2)
|
||||||
Vue.component('waterLevelPond', waterLevelPond)
|
Vue.component('dvDecoration3', decoration3)
|
||||||
Vue.component('scrollBoard', scrollBoard)
|
Vue.component('dvDecoration4', decoration4)
|
||||||
|
Vue.component('dvDecoration5', decoration5)
|
||||||
|
Vue.component('dvDecoration6', decoration6)
|
||||||
|
Vue.component('dvDecoration7', decoration7)
|
||||||
|
Vue.component('dvDecoration8', decoration8)
|
||||||
|
|
||||||
Vue.component('fullScreenContainer', fullScreenContainer)
|
// charts
|
||||||
Vue.component('labelLine', labelLine)
|
Vue.component('dvCharts', charts)
|
||||||
Vue.component('forSlot', forSlot)
|
|
||||||
|
Vue.component('dvActiveRingChart', activeRingChart)
|
||||||
|
Vue.component('dvWaterLevelPond', waterLevelPond)
|
||||||
|
Vue.component('dvPercentPond', percentPond)
|
||||||
|
Vue.component('dvFlylineChart', flylineChart)
|
||||||
|
Vue.component('dvDigitalFlop', digitalFlop)
|
||||||
|
|
||||||
|
// Vue.component('dvScrollBoard', scrollBoard)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="label-line">
|
|
||||||
<div class="label-item"
|
|
||||||
v-for="(labelItem, i) in labelData"
|
|
||||||
:key="labelItem">
|
|
||||||
<div :class="type" :style="`background-color: ${labelColor[i % labelColorNum]};`"></div>
|
|
||||||
<div>{{ labelItem }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'LabelLine',
|
|
||||||
props: ['label', 'colors'],
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
labelData: [],
|
|
||||||
labelColor: [],
|
|
||||||
labelColorNum: 0,
|
|
||||||
type: 'rect'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
label () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
},
|
|
||||||
colors () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
init () {
|
|
||||||
const { label, colors } = this
|
|
||||||
|
|
||||||
if (!label) return
|
|
||||||
|
|
||||||
const { labels, color, type } = label
|
|
||||||
|
|
||||||
if (!labels) return
|
|
||||||
|
|
||||||
this.labelData = labels
|
|
||||||
|
|
||||||
let trueColor = color || colors
|
|
||||||
|
|
||||||
typeof trueColor === 'string' && (trueColor = [trueColor])
|
|
||||||
|
|
||||||
this.labelColor = trueColor
|
|
||||||
|
|
||||||
this.labelColorNum = trueColor.length
|
|
||||||
|
|
||||||
this.type = type || 'rect'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.label-line {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 10px;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.label-item {
|
|
||||||
height: 20px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
margin: 0px 5px 5px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rect {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rectangle {
|
|
||||||
width: 30px;
|
|
||||||
height: 10px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
Before Width: | Height: | Size: 6.1 KiB |
|
@ -1,6 +1,59 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="loading">
|
<div class="dv-loading">
|
||||||
<img class="loading-img" src="./img/loading.png">
|
<svg width="50px" height="50px">
|
||||||
|
<circle
|
||||||
|
cx="25"
|
||||||
|
cy="25"
|
||||||
|
r="20"
|
||||||
|
fill="transparent"
|
||||||
|
stroke-width="3"
|
||||||
|
stroke-dasharray="31.415, 31.415"
|
||||||
|
stroke="#02bcfe"
|
||||||
|
stroke-linecap="round"
|
||||||
|
>
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
type="rotate"
|
||||||
|
values="0, 25 25;360, 25 25"
|
||||||
|
dur="1.5s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="stroke"
|
||||||
|
values="#02bcfe;#3be6cb;#02bcfe"
|
||||||
|
dur="3s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
|
||||||
|
<circle
|
||||||
|
cx="25"
|
||||||
|
cy="25"
|
||||||
|
r="10"
|
||||||
|
fill="transparent"
|
||||||
|
stroke-width="3"
|
||||||
|
stroke-dasharray="15.7, 15.7"
|
||||||
|
stroke="#3be6cb"
|
||||||
|
stroke-linecap="round"
|
||||||
|
>
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
type="rotate"
|
||||||
|
values="360, 25 25;0, 25 25"
|
||||||
|
dur="1.5s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="stroke"
|
||||||
|
values="#3be6cb;#02bcfe;#3be6cb"
|
||||||
|
dur="3s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
</svg>
|
||||||
|
<div class="loading-tip">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -11,29 +64,17 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.loading {
|
.dv-loading {
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.loading-img {
|
.loading-tip {
|
||||||
position: absolute;
|
font-size: 15px;
|
||||||
top: 50%;
|
color: #fff;
|
||||||
left: 50%;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
margin-left: -15px;
|
|
||||||
margin-top: -15px;
|
|
||||||
transform: rotate(0deg);
|
|
||||||
animation: round 1s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes round {
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="number-show">
|
|
||||||
<div class="number-itme" v-for="(n, i) in number.toString()" :key="`${n}-${i}`">
|
|
||||||
{{ n }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'NumberShow',
|
|
||||||
props: ['number']
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.number-show {
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
.number-itme {
|
|
||||||
display: inline-block;
|
|
||||||
height: 70px;
|
|
||||||
padding: 0 20px;
|
|
||||||
line-height: 70px;
|
|
||||||
text-align: center;
|
|
||||||
background-color: rgba(4, 49, 128, 0.6);
|
|
||||||
margin-right: 20px;
|
|
||||||
color: rgb(8, 229, 255);
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 45px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,222 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="percent-arc">
|
|
||||||
<loading v-if="!percent || percent === 0" />
|
|
||||||
|
|
||||||
<div class="canvas-container">
|
|
||||||
<canvas :ref="ref" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="for-slot">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import canvasMixin from '../../mixins/canvasMixin.js'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'PercentArc',
|
|
||||||
props: ['percent', 'ringColor', 'arcColor', 'ringLineWidth', 'arcLineWidth', 'radius', 'arcType'],
|
|
||||||
mixins: [canvasMixin],
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
ref: `percent-arc-${(new Date()).getTime()}`,
|
|
||||||
|
|
||||||
defaultRadius: 0.9,
|
|
||||||
defaultRingLineWidth: 4,
|
|
||||||
defaultArcLineWidth: 10,
|
|
||||||
defaultArcType: 'butt',
|
|
||||||
|
|
||||||
defaultRingColor: '#00BAFF',
|
|
||||||
defaultArcColor: ['#00BAFF', '#3DE7C9'],
|
|
||||||
|
|
||||||
trueArcType: '',
|
|
||||||
trueRadius: 0,
|
|
||||||
ringRadius: 0,
|
|
||||||
arcRadius: 0,
|
|
||||||
trueRingColor: '',
|
|
||||||
trueArcColor: '',
|
|
||||||
trueRingLineWidth: 0,
|
|
||||||
trueArcLineWidth: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
percent () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
},
|
|
||||||
ringColor () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
},
|
|
||||||
arcColor () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
},
|
|
||||||
ringLineWidth () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
},
|
|
||||||
arcLineWidth () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
},
|
|
||||||
radius () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
},
|
|
||||||
arcType () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async init () {
|
|
||||||
const { initCanvas, draw } = this
|
|
||||||
|
|
||||||
await initCanvas()
|
|
||||||
|
|
||||||
draw()
|
|
||||||
},
|
|
||||||
draw () {
|
|
||||||
const { percent } = this
|
|
||||||
|
|
||||||
if (!percent && percent !== 0) return
|
|
||||||
|
|
||||||
const { clearCanvas } = this
|
|
||||||
|
|
||||||
clearCanvas()
|
|
||||||
|
|
||||||
const { calcLineWidth, calcRaidus, calcColors } = this
|
|
||||||
|
|
||||||
calcLineWidth()
|
|
||||||
|
|
||||||
calcRaidus()
|
|
||||||
|
|
||||||
calcColors()
|
|
||||||
|
|
||||||
const { calcArcType, drawRing, drawArc } = this
|
|
||||||
|
|
||||||
calcArcType()
|
|
||||||
|
|
||||||
drawRing()
|
|
||||||
|
|
||||||
drawArc()
|
|
||||||
},
|
|
||||||
calcLineWidth () {
|
|
||||||
const { defaultRingLineWidth, defaultArcLineWidth, ringLineWidth, arcLineWidth } = this
|
|
||||||
|
|
||||||
this.trueRingLineWidth = ringLineWidth || defaultRingLineWidth
|
|
||||||
|
|
||||||
this.trueArcLineWidth = arcLineWidth || defaultArcLineWidth
|
|
||||||
},
|
|
||||||
calcRaidus () {
|
|
||||||
const { radius, defaultRadius, canvasWH } = this
|
|
||||||
|
|
||||||
const trueRadius = this.trueRadius = radius || defaultRadius
|
|
||||||
|
|
||||||
const ringRadius = this.ringRadius = Math.min(...canvasWH) * trueRadius / 2
|
|
||||||
|
|
||||||
const { trueRingLineWidth, trueArcLineWidth } = this
|
|
||||||
|
|
||||||
const halfRingLineWidth = trueRingLineWidth / 2
|
|
||||||
|
|
||||||
const halfArcLineWidth = trueArcLineWidth / 2
|
|
||||||
|
|
||||||
this.arcRadius = ringRadius - halfRingLineWidth - halfArcLineWidth
|
|
||||||
},
|
|
||||||
calcColors () {
|
|
||||||
const { ringColor, defaultRingColor } = this
|
|
||||||
|
|
||||||
this.trueRingColor = ringColor || defaultRingColor
|
|
||||||
|
|
||||||
const { arcColor, defaultArcColor } = this
|
|
||||||
|
|
||||||
this.trueArcColor = arcColor || defaultArcColor
|
|
||||||
},
|
|
||||||
calcArcType () {
|
|
||||||
const { arcType, defaultArcType } = this
|
|
||||||
|
|
||||||
this.trueArcType = arcType || defaultArcType
|
|
||||||
},
|
|
||||||
drawRing () {
|
|
||||||
const { ringRadius, ctx, trueRingLineWidth, centerPos, trueRingColor } = this
|
|
||||||
|
|
||||||
ctx.lineWidth = trueRingLineWidth
|
|
||||||
|
|
||||||
ctx.strokeStyle = trueRingColor
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.arc(...centerPos, ringRadius, 0, Math.PI * 2)
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
},
|
|
||||||
drawArc () {
|
|
||||||
const { ctx, centerPos, percent, trueArcType, canvasWH } = this
|
|
||||||
|
|
||||||
const { trueArcLineWidth, arcRadius, trueArcColor } = this
|
|
||||||
|
|
||||||
const { canvas: { getLinearGradientColor } } = this
|
|
||||||
|
|
||||||
const fullArc = Math.PI * 2
|
|
||||||
|
|
||||||
const offsetArc = Math.PI / 2
|
|
||||||
|
|
||||||
const arcBegin = offsetArc * -1
|
|
||||||
|
|
||||||
const arcEnd = fullArc * percent / 100 - offsetArc
|
|
||||||
|
|
||||||
ctx.lineCap = trueArcType
|
|
||||||
|
|
||||||
ctx.lineWidth = trueArcLineWidth
|
|
||||||
|
|
||||||
ctx.strokeStyle = getLinearGradientColor(ctx, [0, 0], [0, canvasWH[1]], trueArcColor)
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.arc(...centerPos, arcRadius, arcBegin, arcEnd)
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.percent-arc {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.canvas-container {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.for-slot {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,76 +1,246 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="percent-pond">
|
<div class="dv-percent-pond" ref="percent-pond">
|
||||||
<div class="percent-text">
|
<svg>
|
||||||
<span :style="`margin-left: ${percent * (width - 2) / 100 + 6}px`">{{ percent || 0 }}%</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="percent-container">
|
|
||||||
<div class="p-decoration-box" />
|
|
||||||
<div class="p-svg-container" :ref="ref">
|
|
||||||
<svg :width="width" :height="height">
|
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="linear">
|
<linearGradient :id="gradientId1" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||||
|
<stop v-for="lc in linearGradient" :key="lc[0]"
|
||||||
|
:offset="lc[0]"
|
||||||
|
:stop-color="lc[1]" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient :id="gradientId2" x1="0%" y1="0%" :x2="gradient2XPos" y2="0%">
|
||||||
<stop v-for="lc in linearGradient" :key="lc[0]"
|
<stop v-for="lc in linearGradient" :key="lc[0]"
|
||||||
:offset="lc[0]"
|
:offset="lc[0]"
|
||||||
:stop-color="lc[1]" />
|
:stop-color="lc[1]" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
|
<rect
|
||||||
<polyline :stroke-width="height - 1"
|
:x="mergedConfig ? mergedConfig.borderWidth / 2 : '0'"
|
||||||
stroke="url(#linear)"
|
:y="mergedConfig ? mergedConfig.borderWidth / 2 : '0'"
|
||||||
:points="`1, ${height * 0.5} ${percent * (width - 2) / 100}, ${height * 0.5 + 0.0001}`" />
|
:rx="mergedConfig ? mergedConfig.borderRadius : '0'"
|
||||||
|
:ry="mergedConfig ? mergedConfig.borderRadius : '0'"
|
||||||
|
fill="transparent"
|
||||||
|
:stroke-width="mergedConfig ? mergedConfig.borderWidth : '0'"
|
||||||
|
:stroke="`url(#${gradientId1})`"
|
||||||
|
:width="rectWidth"
|
||||||
|
:height="rectHeight"
|
||||||
|
/>
|
||||||
|
<polyline
|
||||||
|
:stroke-width="polylineWidth"
|
||||||
|
:stroke-dasharray="mergedConfig ? mergedConfig.lineDash.join(',') : '0'"
|
||||||
|
:stroke="`url(#${polylineGradient})`"
|
||||||
|
:points="points"
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
:stroke="mergedConfig ? mergedConfig.textColor : '#fff'"
|
||||||
|
:fill="mergedConfig ? mergedConfig.textColor : '#fff'"
|
||||||
|
:x="width / 2"
|
||||||
|
:y="height / 2"
|
||||||
|
>
|
||||||
|
{{ details }}
|
||||||
|
</text>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-decoration-box" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { deepMerge } from '@jiaminghi/charts/lib/util/index'
|
||||||
|
|
||||||
|
import { deepClone } from '@jiaminghi/c-render/lib/plugin/util'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PercentPond',
|
name: 'PercentPond',
|
||||||
props: ['percent', 'colors'],
|
props: {
|
||||||
|
config: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
ref: `percent-pond-${(new Date()).getTime()}`,
|
gradientId1: `percent-pond-gradientId1-${(new Date()).getTime()}`,
|
||||||
|
gradientId2: `percent-pond-gradientId2-${(new Date()).getTime()}`,
|
||||||
|
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
|
|
||||||
defaultColor: ['#00BAFF', '#3DE7C9'],
|
defaultConfig: {
|
||||||
|
/**
|
||||||
|
* @description Value
|
||||||
|
* @type {Number}
|
||||||
|
* @default value = 0
|
||||||
|
*/
|
||||||
|
value: 0,
|
||||||
|
/**
|
||||||
|
* @description Colors (Hex|rgb|rgba)
|
||||||
|
* @type {Array<String>}
|
||||||
|
* @default colors = ['#00BAFF', '#3DE7C9']
|
||||||
|
*/
|
||||||
|
colors: ['#3DE7C9', '#00BAFF'],
|
||||||
|
/**
|
||||||
|
* @description Border width
|
||||||
|
* @type {Number}
|
||||||
|
* @default borderWidth = 3
|
||||||
|
*/
|
||||||
|
borderWidth: 3,
|
||||||
|
/**
|
||||||
|
* @description Gap between border and pond
|
||||||
|
* @type {Number}
|
||||||
|
* @default borderGap = 3
|
||||||
|
*/
|
||||||
|
borderGap: 3,
|
||||||
|
/**
|
||||||
|
* @description Line dash
|
||||||
|
* @type {Array<Number>}
|
||||||
|
* @default lineDash = [5, 1]
|
||||||
|
*/
|
||||||
|
lineDash: [5, 1],
|
||||||
|
/**
|
||||||
|
* @description Text color
|
||||||
|
* @type {String}
|
||||||
|
* @default textColor = '#fff'
|
||||||
|
*/
|
||||||
|
textColor: '#fff',
|
||||||
|
/**
|
||||||
|
* @description Border radius
|
||||||
|
* @type {Number}
|
||||||
|
* @default borderRadius = 5
|
||||||
|
*/
|
||||||
|
borderRadius: 5,
|
||||||
|
/**
|
||||||
|
* @description Local Gradient
|
||||||
|
* @type {Boolean}
|
||||||
|
* @default localGradient = false
|
||||||
|
* @example localGradient = false | true
|
||||||
|
*/
|
||||||
|
localGradient: false,
|
||||||
|
/**
|
||||||
|
* @description Formatter
|
||||||
|
* @type {String}
|
||||||
|
* @default formatter = '{value}%'
|
||||||
|
*/
|
||||||
|
formatter: '{value}%'
|
||||||
|
},
|
||||||
|
|
||||||
linearGradient: []
|
mergedConfig: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
computed: {
|
||||||
colors () {
|
rectWidth () {
|
||||||
const { calcLinearColor } = this
|
const { mergedConfig, width } = this
|
||||||
|
|
||||||
calcLinearColor()
|
if (!mergedConfig) return 0
|
||||||
}
|
|
||||||
|
const { borderWidth } = mergedConfig
|
||||||
|
|
||||||
|
return width - borderWidth
|
||||||
},
|
},
|
||||||
methods: {
|
rectHeight () {
|
||||||
init () {
|
const { mergedConfig, height } = this
|
||||||
const { $nextTick, $refs, ref, calcLinearColor } = this
|
|
||||||
|
|
||||||
$nextTick(e => {
|
if (!mergedConfig) return 0
|
||||||
this.width = $refs[ref].clientWidth
|
|
||||||
this.height = $refs[ref].clientHeight
|
|
||||||
})
|
|
||||||
|
|
||||||
calcLinearColor()
|
const { borderWidth } = mergedConfig
|
||||||
|
|
||||||
|
return height - borderWidth
|
||||||
},
|
},
|
||||||
calcLinearColor () {
|
points () {
|
||||||
const { colors, defaultColor } = this
|
const { mergedConfig, width, height } = this
|
||||||
|
|
||||||
let trueColor = colors || defaultColor
|
const halfHeight = height / 2
|
||||||
|
|
||||||
typeof trueColor === 'string' && (trueColor = [trueColor, trueColor])
|
if (!mergedConfig) return `0, ${halfHeight} 0, ${halfHeight}`
|
||||||
|
|
||||||
const colorNum = trueColor.length
|
const { borderWidth, borderGap, value } = mergedConfig
|
||||||
|
|
||||||
|
const polylineLength = (width - (borderWidth + borderGap) * 2) / 100 * value
|
||||||
|
|
||||||
|
return `
|
||||||
|
${borderWidth + borderGap}, ${halfHeight}
|
||||||
|
${borderWidth + borderGap + polylineLength}, ${halfHeight + 0.001}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
polylineWidth () {
|
||||||
|
const { mergedConfig, height } = this
|
||||||
|
|
||||||
|
if (!mergedConfig) return 0
|
||||||
|
|
||||||
|
const { borderWidth, borderGap } = mergedConfig
|
||||||
|
|
||||||
|
return height - (borderWidth + borderGap) * 2
|
||||||
|
},
|
||||||
|
linearGradient () {
|
||||||
|
const { mergedConfig } = this
|
||||||
|
|
||||||
|
if (!mergedConfig) return []
|
||||||
|
|
||||||
|
const { colors } = mergedConfig
|
||||||
|
|
||||||
|
const colorNum = colors.length
|
||||||
|
|
||||||
const colorOffsetGap = 100 / (colorNum - 1)
|
const colorOffsetGap = 100 / (colorNum - 1)
|
||||||
|
|
||||||
this.linearGradient = trueColor.map((c, i) => [colorOffsetGap * i, c])
|
return colors.map((c, i) => [colorOffsetGap * i, c])
|
||||||
|
},
|
||||||
|
polylineGradient () {
|
||||||
|
const { gradientId1, gradientId2, mergedConfig } = this
|
||||||
|
|
||||||
|
if (!mergedConfig) return gradientId2
|
||||||
|
|
||||||
|
if (mergedConfig.localGradient) return gradientId1
|
||||||
|
|
||||||
|
return gradientId2
|
||||||
|
},
|
||||||
|
gradient2XPos () {
|
||||||
|
const { mergedConfig } = this
|
||||||
|
|
||||||
|
if (!mergedConfig) return '100%'
|
||||||
|
|
||||||
|
const { value } = mergedConfig
|
||||||
|
|
||||||
|
return `${200 - value}%`
|
||||||
|
},
|
||||||
|
details () {
|
||||||
|
const { mergedConfig } = this
|
||||||
|
|
||||||
|
if (!mergedConfig) return ''
|
||||||
|
|
||||||
|
const { value, formatter } = mergedConfig
|
||||||
|
|
||||||
|
return formatter.replace('{value}', value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
config () {
|
||||||
|
const { mergeConfig } = this
|
||||||
|
|
||||||
|
mergeConfig()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async init () {
|
||||||
|
const { initWH, config, mergeConfig } = this
|
||||||
|
|
||||||
|
await initWH()
|
||||||
|
|
||||||
|
if (!config) return
|
||||||
|
|
||||||
|
mergeConfig()
|
||||||
|
},
|
||||||
|
async initWH () {
|
||||||
|
const { $nextTick, $refs } = this
|
||||||
|
|
||||||
|
await $nextTick()
|
||||||
|
|
||||||
|
const dom = $refs['percent-pond']
|
||||||
|
|
||||||
|
this.width = dom.clientWidth
|
||||||
|
this.height = dom.clientHeight
|
||||||
|
},
|
||||||
|
mergeConfig () {
|
||||||
|
let { config, defaultConfig } = this
|
||||||
|
|
||||||
|
this.mergedConfig = deepMerge(deepClone(defaultConfig, true), config || {})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
@ -82,58 +252,19 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.percent-pond {
|
.dv-percent-pond {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.percent-text {
|
|
||||||
height: 30px;
|
|
||||||
font-size: 15px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
span {
|
|
||||||
position: relative;
|
|
||||||
box-shadow: 0 0 3px gray;
|
|
||||||
padding: 0px 5px;
|
|
||||||
display: inline-block;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
display: block;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
content: '';
|
|
||||||
width: 0px;
|
|
||||||
height: 0px;
|
|
||||||
border-width: 8px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: fade(gray, 50) transparent transparent transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.percent-container {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
.p-decoration-box {
|
|
||||||
width: 3px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
box-shadow: 0 0 3px gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-svg-container {
|
|
||||||
flex: 1;
|
|
||||||
margin: 0px 3px;
|
|
||||||
box-shadow: 0 0 3px gray;
|
|
||||||
|
|
||||||
polyline {
|
polyline {
|
||||||
fill: none;
|
transition: all 0.3s;
|
||||||
stroke-dasharray: 5, 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-anchor: middle;
|
||||||
|
dominant-baseline: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,175 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="point-chart">
|
|
||||||
<loading v-if="!data" />
|
|
||||||
|
|
||||||
<div class="canvas-container">
|
|
||||||
<canvas :ref="ref" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label-line :label="labelLine" :colors="drawColors" />
|
|
||||||
|
|
||||||
<for-slot><slot></slot></for-slot>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import canvasMixin from '../../mixins/canvasMixin.js'
|
|
||||||
|
|
||||||
import colorsMixin from '../../mixins/colorsMixin.js'
|
|
||||||
|
|
||||||
import axisMixin from '../../mixins/axisMixin.js'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'PointChart',
|
|
||||||
mixins: [canvasMixin, colorsMixin, axisMixin],
|
|
||||||
props: ['data', 'labelLine', 'colors'],
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
ref: `point-chart-${(new Date()).getTime()}`,
|
|
||||||
|
|
||||||
// axis base config
|
|
||||||
boundaryGap: true,
|
|
||||||
horizon: false,
|
|
||||||
mulValueAdd: false,
|
|
||||||
|
|
||||||
defaultPointRadius: 2,
|
|
||||||
|
|
||||||
valuePointPos: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
data () {
|
|
||||||
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 { initAxis, drawAxis, calcValuePointPos } = this
|
|
||||||
|
|
||||||
initAxis()
|
|
||||||
|
|
||||||
drawAxis()
|
|
||||||
|
|
||||||
calcValuePointPos()
|
|
||||||
|
|
||||||
const { drawPoints } = this
|
|
||||||
|
|
||||||
drawPoints()
|
|
||||||
},
|
|
||||||
calcValuePointPos () {
|
|
||||||
const { data: { series }, valueAxisMaxMin, getAxisPointsPos } = this
|
|
||||||
|
|
||||||
const { axisOriginPos, axisWH, labelAxisTagPos } = this
|
|
||||||
|
|
||||||
this.valuePointPos = series.map(({ value }, i) =>
|
|
||||||
getAxisPointsPos(
|
|
||||||
valueAxisMaxMin,
|
|
||||||
value,
|
|
||||||
axisOriginPos,
|
|
||||||
axisWH,
|
|
||||||
labelAxisTagPos,
|
|
||||||
false
|
|
||||||
))
|
|
||||||
},
|
|
||||||
drawPoints () {
|
|
||||||
const { data: { series }, drawSeriesPoint, ctx } = this
|
|
||||||
|
|
||||||
ctx.setLineDash([10, 0])
|
|
||||||
|
|
||||||
series.forEach((seriesItem, i) => drawSeriesPoint(seriesItem, i))
|
|
||||||
},
|
|
||||||
drawSeriesPoint ({ color: cr, edgeColor, fillColor, radius, opacity }, i) {
|
|
||||||
const { drawColors, defaultPointRadius, valuePointPos, drawPoint } = this
|
|
||||||
|
|
||||||
const { color: { hexToRgb }, data: { radius: outerRadius } } = this
|
|
||||||
|
|
||||||
const drawColorsNum = drawColors.length
|
|
||||||
|
|
||||||
const baseColor = drawColors[i % drawColorsNum]
|
|
||||||
|
|
||||||
const trueEdgeColor = edgeColor || cr || baseColor
|
|
||||||
|
|
||||||
let trueFillColor = fillColor || cr || baseColor
|
|
||||||
|
|
||||||
opacity && (trueFillColor = hexToRgb(trueFillColor, opacity))
|
|
||||||
|
|
||||||
const trueRadius = radius || outerRadius || defaultPointRadius
|
|
||||||
|
|
||||||
valuePointPos[i].forEach(cp => {
|
|
||||||
if (!cp && cp !== 0) return
|
|
||||||
|
|
||||||
const isSeries = cp[0] instanceof Array
|
|
||||||
|
|
||||||
isSeries && cp.forEach(p => (p || p === 0) && drawPoint(p, trueEdgeColor, trueFillColor, trueRadius))
|
|
||||||
|
|
||||||
!isSeries && drawPoint(cp, trueEdgeColor, trueFillColor, trueRadius)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
drawPoint (pos, edgeColor, fillColor, radius) {
|
|
||||||
const { ctx } = this
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.arc(...pos, radius, 0, Math.PI * 2)
|
|
||||||
|
|
||||||
ctx.closePath()
|
|
||||||
|
|
||||||
ctx.strokeStyle = edgeColor
|
|
||||||
ctx.fillStyle = fillColor
|
|
||||||
|
|
||||||
ctx.fill()
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.point-chart {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.canvas-container {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,281 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,558 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="radar-chart">
|
|
||||||
|
|
||||||
<loading v-if="!data" />
|
|
||||||
|
|
||||||
<div class="canvas-container">
|
|
||||||
<canvas :ref="ref" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label-line :label="labelLine" :colors="drawColors" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import colorsMixin from '../../mixins/colorsMixin.js'
|
|
||||||
|
|
||||||
import canvasMixin from '../../mixins/canvasMixin.js'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'RadarChart',
|
|
||||||
mixins: [canvasMixin, colorsMixin],
|
|
||||||
props: ['data', 'labelLine', 'colors'],
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
ref: `radar-chart-${(new Date()).getTime()}`,
|
|
||||||
|
|
||||||
defaultRadius: 0.8,
|
|
||||||
defaultRingNum: 4,
|
|
||||||
defaultRingType: 'circle',
|
|
||||||
defaultRingLineType: 'dashed',
|
|
||||||
defaultRingLineColor: '#666',
|
|
||||||
defaultRingFillType: 'none',
|
|
||||||
defaultRayLineType: 'line',
|
|
||||||
defaultRayLineColor: '#666',
|
|
||||||
|
|
||||||
defaultRayLineOffset: Math.PI * -0.5,
|
|
||||||
defaultLabelColor: '#fff',
|
|
||||||
defaultLabelFS: 10,
|
|
||||||
|
|
||||||
defaultValueFontSize: 10,
|
|
||||||
defaultValueColor: '#999',
|
|
||||||
|
|
||||||
drawColors: '',
|
|
||||||
radius: '',
|
|
||||||
ringType: '',
|
|
||||||
rayLineRadianData: [],
|
|
||||||
ringRadiusData: [],
|
|
||||||
ringPolylineData: [],
|
|
||||||
ringLineDash: [],
|
|
||||||
ringlineMultipleColor: false,
|
|
||||||
ringLineColor: '',
|
|
||||||
ringFillType: '',
|
|
||||||
ringFillMultipleColor: false,
|
|
||||||
ringFillColor: '',
|
|
||||||
rayLineColor: '',
|
|
||||||
rayLineDash: '',
|
|
||||||
rayLineMultipleColor: false,
|
|
||||||
labelPosData: [],
|
|
||||||
labelColor: '',
|
|
||||||
labelFontSize: '',
|
|
||||||
labelMultipleColor: false,
|
|
||||||
|
|
||||||
valuePointData: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
data (d) {
|
|
||||||
const { checkData, reDraw } = this
|
|
||||||
|
|
||||||
checkData && reDraw(d)
|
|
||||||
},
|
|
||||||
color (d) {
|
|
||||||
const { checkData, reDraw } = this
|
|
||||||
|
|
||||||
checkData && reDraw(d)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async init () {
|
|
||||||
const { initCanvas, checkData, draw } = this
|
|
||||||
|
|
||||||
await initCanvas()
|
|
||||||
|
|
||||||
checkData() && draw()
|
|
||||||
},
|
|
||||||
checkData () {
|
|
||||||
const { data } = this
|
|
||||||
|
|
||||||
this.status = false
|
|
||||||
|
|
||||||
if (!data || !data.series) return false
|
|
||||||
|
|
||||||
this.status = true
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
draw () {
|
|
||||||
const { ctx, canvasWH } = this
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, ...canvasWH)
|
|
||||||
|
|
||||||
const { initColors, calcRadarRadius, calcRingType } = this
|
|
||||||
|
|
||||||
initColors()
|
|
||||||
|
|
||||||
calcRadarRadius()
|
|
||||||
|
|
||||||
calcRingType()
|
|
||||||
|
|
||||||
const { calcRayLineRadianData, calcRingRadiusData, calcRingPolylineData } = this
|
|
||||||
|
|
||||||
calcRayLineRadianData()
|
|
||||||
|
|
||||||
calcRingRadiusData()
|
|
||||||
|
|
||||||
calcRingPolylineData()
|
|
||||||
|
|
||||||
const { calcRingDrawConfig, calcRingFillConfig, fillRing } = this
|
|
||||||
|
|
||||||
calcRingDrawConfig()
|
|
||||||
|
|
||||||
calcRingFillConfig()
|
|
||||||
|
|
||||||
fillRing()
|
|
||||||
|
|
||||||
const { drawCircleRing, drawPolylineRing, calcRayLineConfig } = this
|
|
||||||
|
|
||||||
drawCircleRing()
|
|
||||||
|
|
||||||
drawPolylineRing()
|
|
||||||
|
|
||||||
calcRayLineConfig()
|
|
||||||
|
|
||||||
const { drawRayLine, calcLabelPosData, calcLabelConfig } = this
|
|
||||||
|
|
||||||
drawRayLine()
|
|
||||||
|
|
||||||
calcLabelPosData()
|
|
||||||
|
|
||||||
calcLabelConfig()
|
|
||||||
|
|
||||||
const { drawLable, caclValuePointData, fillRadar } = this
|
|
||||||
|
|
||||||
drawLable()
|
|
||||||
|
|
||||||
caclValuePointData()
|
|
||||||
|
|
||||||
fillRadar()
|
|
||||||
|
|
||||||
const { fillValueText } = this
|
|
||||||
|
|
||||||
fillValueText()
|
|
||||||
},
|
|
||||||
calcRadarRadius () {
|
|
||||||
const { canvasWH, data: { radius }, defaultRadius } = this
|
|
||||||
|
|
||||||
this.radius = Math.min(...canvasWH) * (radius || defaultRadius) * 0.5
|
|
||||||
},
|
|
||||||
calcRingType () {
|
|
||||||
const { data: { ringType }, defaultRingType } = this
|
|
||||||
|
|
||||||
this.ringType = ringType || defaultRingType
|
|
||||||
},
|
|
||||||
calcRayLineRadianData () {
|
|
||||||
const { data: { label, rayLineOffset }, defaultRayLineOffset } = this
|
|
||||||
|
|
||||||
const { tags } = label
|
|
||||||
|
|
||||||
const fullRadian = Math.PI * 2
|
|
||||||
|
|
||||||
const radianGap = fullRadian / tags.length
|
|
||||||
|
|
||||||
const radianOffset = rayLineOffset || defaultRayLineOffset
|
|
||||||
|
|
||||||
this.rayLineRadianData = tags.map((t, i) => radianGap * i + radianOffset)
|
|
||||||
},
|
|
||||||
calcRingRadiusData () {
|
|
||||||
const { data: { ringNum }, defaultRingNum, radius } = this
|
|
||||||
|
|
||||||
const num = ringNum || defaultRingNum
|
|
||||||
|
|
||||||
const radiusGap = radius / num
|
|
||||||
|
|
||||||
this.ringRadiusData = new Array(num).fill(0).map((t, i) =>
|
|
||||||
radiusGap * (i + 1))
|
|
||||||
},
|
|
||||||
calcRingPolylineData () {
|
|
||||||
const { ringRadiusData, rayLineRadianData, centerPos } = this
|
|
||||||
|
|
||||||
const { canvas: { getCircleRadianPoint } } = this
|
|
||||||
|
|
||||||
this.ringPolylineData = ringRadiusData.map((r, i) =>
|
|
||||||
rayLineRadianData.map(radian =>
|
|
||||||
getCircleRadianPoint(...centerPos, r, radian)))
|
|
||||||
},
|
|
||||||
calcRingDrawConfig () {
|
|
||||||
const { defaultRingLineType, defaultRingLineColor } = this
|
|
||||||
|
|
||||||
const { data: { ringLineType, ringLineColor }, drawColors } = this
|
|
||||||
|
|
||||||
this.ringLineDash = (ringLineType || defaultRingLineType) === 'dashed' ? [5, 5] : [10, 0]
|
|
||||||
|
|
||||||
const trueRingLineColor = ringLineColor === 'colors' ? drawColors : ringLineColor
|
|
||||||
|
|
||||||
this.ringlineMultipleColor = typeof trueRingLineColor === 'object'
|
|
||||||
|
|
||||||
this.ringLineColor = trueRingLineColor || defaultRingLineColor
|
|
||||||
},
|
|
||||||
calcRingFillConfig () {
|
|
||||||
const { data: { ringFillType, ringFillColor }, defaultRingFillType, drawColors } = this
|
|
||||||
|
|
||||||
this.ringFillType = ringFillType || defaultRingFillType
|
|
||||||
|
|
||||||
const trueRingFillColor = this.ringFillColor = (!ringFillColor || ringFillColor === 'colors') ? drawColors : ringFillColor
|
|
||||||
|
|
||||||
this.ringFillMultipleColor = typeof trueRingFillColor === 'object'
|
|
||||||
},
|
|
||||||
fillRing () {
|
|
||||||
const { ringFillType, fillCoverRing, fillMulCoverRing, fillRingRing } = this
|
|
||||||
|
|
||||||
switch (ringFillType) {
|
|
||||||
case 'cover': fillCoverRing()
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'mulCover': fillMulCoverRing()
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'ring': fillRingRing()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fillCoverRing () {
|
|
||||||
const { ctx, centerPos, ringFillColor, ringType, radius, ringPolylineData } = this
|
|
||||||
|
|
||||||
const { canvas: { getRadialGradientColor, drawPolylinePath } } = this
|
|
||||||
|
|
||||||
const color = getRadialGradientColor(ctx, centerPos, 0, radius, ringFillColor)
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ringType === 'circle' && ctx.arc(...centerPos, radius, 0, Math.PI * 2)
|
|
||||||
|
|
||||||
ringType === 'polyline' && drawPolylinePath(ctx, ringPolylineData[ringPolylineData.length - 1])
|
|
||||||
|
|
||||||
ctx.closePath()
|
|
||||||
|
|
||||||
ctx.fillStyle = color
|
|
||||||
|
|
||||||
ctx.fill()
|
|
||||||
},
|
|
||||||
fillMulCoverRing () {
|
|
||||||
const { ctx, ringType, ringFillColor, centerPos } = this
|
|
||||||
|
|
||||||
const { ringFillMultipleColor, ringPolylineData, ringRadiusData, deepClone } = this
|
|
||||||
|
|
||||||
const { canvas: { drawPolylinePath } } = this
|
|
||||||
|
|
||||||
!ringFillMultipleColor && (ctx.fillStyle = ringFillColor)
|
|
||||||
|
|
||||||
const colorNum = ringFillColor.length
|
|
||||||
|
|
||||||
const LastRingIndex = ringRadiusData.length - 1
|
|
||||||
|
|
||||||
ringType === 'circle' &&
|
|
||||||
deepClone(ringRadiusData).reverse().forEach((radius, i) => {
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.arc(...centerPos, radius, 0, Math.PI * 2)
|
|
||||||
|
|
||||||
ringFillMultipleColor && (ctx.fillStyle = ringFillColor[(LastRingIndex - i) % colorNum])
|
|
||||||
|
|
||||||
ctx.fill()
|
|
||||||
})
|
|
||||||
|
|
||||||
ringType === 'polyline' &&
|
|
||||||
deepClone(ringPolylineData).reverse().forEach((line, i) => {
|
|
||||||
drawPolylinePath(ctx, line, true, true)
|
|
||||||
|
|
||||||
ringFillMultipleColor && (ctx.fillStyle = ringFillColor[(LastRingIndex - i) % colorNum])
|
|
||||||
|
|
||||||
ctx.fill()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fillRingRing () {
|
|
||||||
const { ctx, ringType, ringRadiusData, rayLineRadianData, getPointToLineDistance } = this
|
|
||||||
|
|
||||||
const { ringFillMultipleColor, centerPos, ringFillColor, ringPolylineData } = this
|
|
||||||
|
|
||||||
const { canvas: { drawPolylinePath, getCircleRadianPoint } } = this
|
|
||||||
|
|
||||||
let lineWidth = ctx.lineWidth = ringRadiusData[0]
|
|
||||||
|
|
||||||
const halfLineWidth = lineWidth / 2
|
|
||||||
|
|
||||||
const colorNum = ringFillColor.length
|
|
||||||
|
|
||||||
!ringFillMultipleColor && (ctx.strokeStyle = ringFillColor)
|
|
||||||
|
|
||||||
ringType === 'circle' &&
|
|
||||||
ringRadiusData.forEach((r, i) => {
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.arc(...centerPos, r - halfLineWidth, 0, Math.PI * 2)
|
|
||||||
|
|
||||||
ringFillMultipleColor && (ctx.strokeStyle = ringFillColor[i % colorNum])
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.lineCap = 'round'
|
|
||||||
|
|
||||||
ctx.lineWidth = getPointToLineDistance(centerPos, ringPolylineData[0][0], ringPolylineData[0][1])
|
|
||||||
|
|
||||||
ringType === 'polyline' &&
|
|
||||||
ringRadiusData.map(r => r - halfLineWidth).map(r =>
|
|
||||||
rayLineRadianData.map(radian =>
|
|
||||||
getCircleRadianPoint(...centerPos, r, radian))).forEach((line, i) => {
|
|
||||||
drawPolylinePath(ctx, line, true, true)
|
|
||||||
|
|
||||||
ringFillMultipleColor && (ctx.strokeStyle = ringFillColor[i % colorNum])
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
drawCircleRing () {
|
|
||||||
const { data: { ringType }, defaultRingType } = this
|
|
||||||
|
|
||||||
if ((ringType && ringType !== 'circle') || (!ringType && defaultRingType !== 'circle')) return
|
|
||||||
|
|
||||||
const { ctx, ringRadiusData, centerPos, ringLineDash, ringlineMultipleColor, ringLineColor } = this
|
|
||||||
|
|
||||||
ctx.setLineDash(ringLineDash)
|
|
||||||
|
|
||||||
ctx.lineWidth = 1
|
|
||||||
|
|
||||||
!ringlineMultipleColor && (ctx.strokeStyle = ringLineColor)
|
|
||||||
|
|
||||||
const colorNum = ringLineColor.length
|
|
||||||
|
|
||||||
ringRadiusData.forEach((r, i) => {
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.arc(...centerPos, r, 0, Math.PI * 2)
|
|
||||||
|
|
||||||
ringlineMultipleColor && (ctx.strokeStyle = ringLineColor[i % colorNum])
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
drawPolylineRing () {
|
|
||||||
const { data: { ringType }, defaultRingType } = this
|
|
||||||
|
|
||||||
if ((ringType && ringType !== 'polyline') || (!ringType && defaultRingType !== 'polyline')) return
|
|
||||||
|
|
||||||
const { ctx, ringPolylineData, ringLineDash, ringlineMultipleColor, ringLineColor } = this
|
|
||||||
|
|
||||||
const { canvas: { drawPolyline } } = this
|
|
||||||
|
|
||||||
const colorNum = ringLineColor.length
|
|
||||||
|
|
||||||
ringPolylineData.forEach((line, i) =>
|
|
||||||
drawPolyline(ctx, line, 1,
|
|
||||||
(ringlineMultipleColor ? ringLineColor[i % colorNum] : ringLineColor),
|
|
||||||
true, ringLineDash, true))
|
|
||||||
},
|
|
||||||
calcRayLineConfig () {
|
|
||||||
const { data: { rayLineType, rayLineColor }, defaultRayLineType, defaultRayLineColor, drawColors } = this
|
|
||||||
|
|
||||||
this.rayLineDash = (rayLineType || defaultRayLineType) === 'line' ? [10, 0] : [5, 5]
|
|
||||||
|
|
||||||
const trueRayLineColor = rayLineColor === 'colors' ? drawColors : (rayLineColor || defaultRayLineColor)
|
|
||||||
|
|
||||||
this.rayLineColor = trueRayLineColor
|
|
||||||
|
|
||||||
this.rayLineMultipleColor = typeof trueRayLineColor === 'object'
|
|
||||||
},
|
|
||||||
drawRayLine () {
|
|
||||||
const { ctx, rayLineColor, rayLineDash, ringPolylineData, centerPos, rayLineMultipleColor } = this
|
|
||||||
|
|
||||||
const lastRingLineIndex = ringPolylineData.length - 1
|
|
||||||
|
|
||||||
ctx.setLineDash(rayLineDash)
|
|
||||||
|
|
||||||
!rayLineMultipleColor && (ctx.strokeStyle = rayLineColor)
|
|
||||||
|
|
||||||
ctx.lineWidth = 1
|
|
||||||
|
|
||||||
const colorNum = rayLineColor.length
|
|
||||||
|
|
||||||
ringPolylineData[lastRingLineIndex].forEach((point, i) => {
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.moveTo(...centerPos)
|
|
||||||
|
|
||||||
ctx.lineTo(...point)
|
|
||||||
|
|
||||||
rayLineMultipleColor && (ctx.strokeStyle = rayLineColor[i % colorNum])
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
calcLabelPosData () {
|
|
||||||
const { rayLineRadianData, radius, centerPos } = this
|
|
||||||
|
|
||||||
const { canvas: { getCircleRadianPoint } } = this
|
|
||||||
|
|
||||||
const labelRadius = radius + 10
|
|
||||||
|
|
||||||
this.labelPosData = rayLineRadianData.map(radian =>
|
|
||||||
getCircleRadianPoint(...centerPos, labelRadius, radian))
|
|
||||||
},
|
|
||||||
calcLabelConfig () {
|
|
||||||
const { defaultLabelColor, defaultLabelFS, drawColors } = this
|
|
||||||
|
|
||||||
const { data: { label: { color, fontSize } } } = this
|
|
||||||
|
|
||||||
const trueLabelColor = color === 'colors' ? drawColors : (color || defaultLabelColor)
|
|
||||||
|
|
||||||
this.labelMultipleColor = typeof trueLabelColor === 'object'
|
|
||||||
|
|
||||||
this.labelFontSize = fontSize || defaultLabelFS
|
|
||||||
|
|
||||||
this.labelColor = trueLabelColor
|
|
||||||
},
|
|
||||||
drawLable () {
|
|
||||||
const { ctx, centerPos: [x], labelPosData, labelColor, labelFontSize, labelMultipleColor } = this
|
|
||||||
|
|
||||||
const { data: { label: { tags } } } = this
|
|
||||||
|
|
||||||
ctx.font = `${labelFontSize}px Arial`
|
|
||||||
|
|
||||||
!labelMultipleColor && (ctx.fillStyle = labelColor)
|
|
||||||
|
|
||||||
ctx.textBaseline = 'middle'
|
|
||||||
|
|
||||||
const colorNum = labelColor.length
|
|
||||||
|
|
||||||
labelPosData.forEach((pos, i) => {
|
|
||||||
ctx.textAlign = 'start'
|
|
||||||
|
|
||||||
pos[0] < x && (ctx.textAlign = 'end')
|
|
||||||
|
|
||||||
labelMultipleColor && (ctx.fillStyle = labelColor[i % colorNum])
|
|
||||||
|
|
||||||
ctx.fillText(tags[i], ...pos)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
caclValuePointData () {
|
|
||||||
const { data: { series, max }, centerPos, radius, rayLineRadianData } = this
|
|
||||||
|
|
||||||
const { canvas: { getCircleRadianPoint } } = this
|
|
||||||
|
|
||||||
const maxValue = max || Math.max(...series.map(({ value }) => Math.max(...value)))
|
|
||||||
|
|
||||||
const valueRadius = series.map(({ value }) =>
|
|
||||||
value.map(v =>
|
|
||||||
Number.isFinite(v)
|
|
||||||
? v / maxValue * radius : false))
|
|
||||||
|
|
||||||
this.valuePointData = valueRadius.map(td =>
|
|
||||||
td.map((r, i) =>
|
|
||||||
(r || r === 0) ? getCircleRadianPoint(...centerPos, r, rayLineRadianData[i]) : false))
|
|
||||||
},
|
|
||||||
fillRadar () {
|
|
||||||
const { ctx, data: { series }, valuePointData, drawColors, filterNull } = this
|
|
||||||
|
|
||||||
const { canvas: { drawPolylinePath } } = this
|
|
||||||
|
|
||||||
const { color: { hexToRgb } } = this
|
|
||||||
|
|
||||||
const colorNum = drawColors.length
|
|
||||||
|
|
||||||
valuePointData.forEach((line, i) => {
|
|
||||||
const currentColor = drawColors[i % colorNum]
|
|
||||||
|
|
||||||
const lineColor = series[i].lineColor
|
|
||||||
const fillColor = series[i].fillColor
|
|
||||||
|
|
||||||
series[i].dashed ? ctx.setLineDash([5, 5]) : ctx.setLineDash([10, 0])
|
|
||||||
|
|
||||||
drawPolylinePath(ctx, filterNull(line), 1, true, true)
|
|
||||||
|
|
||||||
ctx.strokeStyle = lineColor || currentColor
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
|
|
||||||
ctx.fillStyle = fillColor || hexToRgb(currentColor, 0.5)
|
|
||||||
|
|
||||||
ctx.fill()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fillValueText () {
|
|
||||||
const { data: { series, showValueText, valueTextFontSize } } = this
|
|
||||||
|
|
||||||
if (!showValueText) return
|
|
||||||
|
|
||||||
const { ctx, defaultValueFontSize } = this
|
|
||||||
|
|
||||||
ctx.font = `${valueTextFontSize || defaultValueFontSize}px Arial`
|
|
||||||
|
|
||||||
const { fillSeriesText } = this
|
|
||||||
|
|
||||||
series.forEach((item, i) => fillSeriesText(item, i))
|
|
||||||
},
|
|
||||||
fillSeriesText ({ valueTextColor, lineColor, fillColor, value }, i) {
|
|
||||||
const { ctx, drawColors, valuePointData, drawTexts } = this
|
|
||||||
|
|
||||||
const { data: { valueTextOffset, valueTextColor: outerValueTC }, defaultValueColor } = this
|
|
||||||
|
|
||||||
const trueOffset = valueTextOffset || [5, -5]
|
|
||||||
|
|
||||||
const drawColorsNum = drawColors.length
|
|
||||||
|
|
||||||
let currentColor = valueTextColor
|
|
||||||
currentColor === 'inherit' && (currentColor = lineColor || fillColor || drawColors[i % drawColorsNum])
|
|
||||||
currentColor instanceof Array && (currentColor = currentColor[0])
|
|
||||||
|
|
||||||
ctx.fillStyle = currentColor || outerValueTC || defaultValueColor
|
|
||||||
|
|
||||||
drawTexts(ctx, value, valuePointData[i], trueOffset)
|
|
||||||
},
|
|
||||||
drawTexts (ctx, values, points, [x, y] = [0, 0]) {
|
|
||||||
values.forEach((v, i) => {
|
|
||||||
if (!v && v !== 0) return
|
|
||||||
|
|
||||||
ctx.fillText(v, points[i][0] + x, points[i][1] + y)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
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%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,434 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="ring-chart">
|
|
||||||
<loading v-if="!status" />
|
|
||||||
|
|
||||||
<div class="canvas-container">
|
|
||||||
<canvas :ref="ref" />
|
|
||||||
|
|
||||||
<div class="center-info" v-if="data.active">
|
|
||||||
<div class="percent-show">{{percent}}</div>
|
|
||||||
<div class="current-label" :ref="labelRef">{{data.series[activeIndex].title}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label-line :label="dealAfterLabelLine" :colors="drawColors" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import colorsMixin from '../../mixins/colorsMixin.js'
|
|
||||||
import canvasMixin from '../../mixins/canvasMixin.js'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'RingChart',
|
|
||||||
props: ['data', 'labelLine', 'colors'],
|
|
||||||
mixins: [colorsMixin, canvasMixin],
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
ref: `ring-chart-${(new Date()).getTime()}`,
|
|
||||||
|
|
||||||
status: false,
|
|
||||||
|
|
||||||
labelRef: `label-ref-${(new Date()).getTime()}`,
|
|
||||||
labelDom: '',
|
|
||||||
|
|
||||||
ringRadius: '',
|
|
||||||
ringLineWidth: '',
|
|
||||||
maxRingWidthP: 1.15,
|
|
||||||
|
|
||||||
activeIndex: 1,
|
|
||||||
activePercent: 1,
|
|
||||||
activeAddStatus: true,
|
|
||||||
|
|
||||||
arcData: [],
|
|
||||||
radiusData: [],
|
|
||||||
aroundLineData: [],
|
|
||||||
aroundTextData: [],
|
|
||||||
aroundTextFont: '13px Arial',
|
|
||||||
|
|
||||||
activeIncrease: 0.005,
|
|
||||||
activeTime: 4500,
|
|
||||||
|
|
||||||
offsetAngle: Math.PI * 0.5 * -1,
|
|
||||||
|
|
||||||
dealAfterLabelLine: [],
|
|
||||||
|
|
||||||
percent: 0,
|
|
||||||
totalValue: 0,
|
|
||||||
|
|
||||||
activeAnimationHandler: '',
|
|
||||||
awaitActiveHandler: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
data (d) {
|
|
||||||
const { checkData, reDraw } = this
|
|
||||||
|
|
||||||
checkData() && reDraw()
|
|
||||||
},
|
|
||||||
activeIndex () {
|
|
||||||
const { doPercentAnimation, doLabelTextAnimation } = this
|
|
||||||
|
|
||||||
doPercentAnimation()
|
|
||||||
|
|
||||||
doLabelTextAnimation()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async init () {
|
|
||||||
const { initCanvas, initColors, calcRingConfig, checkData, draw } = this
|
|
||||||
|
|
||||||
await initCanvas()
|
|
||||||
|
|
||||||
initColors()
|
|
||||||
|
|
||||||
calcRingConfig()
|
|
||||||
|
|
||||||
checkData() && draw()
|
|
||||||
},
|
|
||||||
calcRingConfig () {
|
|
||||||
const { canvasWH } = this
|
|
||||||
|
|
||||||
const ringRadius = this.ringRadius = Math.min(...canvasWH) * 0.6 / 2
|
|
||||||
|
|
||||||
this.ringLineWidth = ringRadius * 0.3
|
|
||||||
},
|
|
||||||
checkData () {
|
|
||||||
const { data } = this
|
|
||||||
|
|
||||||
this.status = false
|
|
||||||
|
|
||||||
if (!data || !data.series) return false
|
|
||||||
|
|
||||||
this.status = true
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
draw () {
|
|
||||||
const { clearCanvas, calcLabelLineData } = this
|
|
||||||
|
|
||||||
clearCanvas()
|
|
||||||
|
|
||||||
calcLabelLineData()
|
|
||||||
|
|
||||||
const { caclArcData, data: { active }, drawActive, drwaStatic } = this
|
|
||||||
|
|
||||||
caclArcData()
|
|
||||||
|
|
||||||
active ? drawActive() : drwaStatic()
|
|
||||||
},
|
|
||||||
calcLabelLineData () {
|
|
||||||
const { labelLine, deepClone, data: { series } } = this
|
|
||||||
|
|
||||||
if (!labelLine) return
|
|
||||||
|
|
||||||
const dealAfterLabelLine = this.dealAfterLabelLine = deepClone(labelLine)
|
|
||||||
|
|
||||||
if (labelLine.labels === 'inherit') dealAfterLabelLine.labels = series.map(({ title }) => title)
|
|
||||||
},
|
|
||||||
caclArcData () {
|
|
||||||
const { data: { series } } = this
|
|
||||||
|
|
||||||
const { getTotalValue, offsetAngle } = this
|
|
||||||
|
|
||||||
const totalValue = getTotalValue()
|
|
||||||
|
|
||||||
const full = 2 * Math.PI
|
|
||||||
|
|
||||||
const aveAngle = full / series.length
|
|
||||||
|
|
||||||
let currentPercent = offsetAngle
|
|
||||||
|
|
||||||
this.arcData = []
|
|
||||||
|
|
||||||
series.forEach(({ value }) => {
|
|
||||||
const valueAngle = totalValue === 0 ? aveAngle : value / totalValue * full
|
|
||||||
|
|
||||||
this.arcData.push([
|
|
||||||
currentPercent,
|
|
||||||
currentPercent += valueAngle
|
|
||||||
])
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getTotalValue () {
|
|
||||||
const { data: { series } } = this
|
|
||||||
|
|
||||||
let totalValue = 0
|
|
||||||
|
|
||||||
series.forEach(({ value }) => (totalValue += value))
|
|
||||||
|
|
||||||
this.totalValue = totalValue
|
|
||||||
|
|
||||||
return totalValue
|
|
||||||
},
|
|
||||||
drawActive () {
|
|
||||||
const { ctx, canvasWH } = this
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, ...canvasWH)
|
|
||||||
|
|
||||||
const { calcRadiusData, drawRing, drawActive } = this
|
|
||||||
|
|
||||||
calcRadiusData()
|
|
||||||
|
|
||||||
drawRing()
|
|
||||||
|
|
||||||
this.activeAnimationHandler = requestAnimationFrame(drawActive)
|
|
||||||
},
|
|
||||||
calcRadiusData () {
|
|
||||||
const { arcData, activeAddStatus, activePercent, activeIncrease, activeIndex } = this
|
|
||||||
|
|
||||||
const radiusData = new Array(arcData.length).fill(1)
|
|
||||||
|
|
||||||
const activeRadius = (activeAddStatus ? this.activePercent += activeIncrease : activePercent)
|
|
||||||
|
|
||||||
radiusData[activeIndex] = activeRadius
|
|
||||||
|
|
||||||
const { maxRingWidthP, ringRadius, awaitActive } = this
|
|
||||||
|
|
||||||
const prevRadius = maxRingWidthP - activeRadius + 1
|
|
||||||
|
|
||||||
const prevIndex = activeIndex - 1
|
|
||||||
|
|
||||||
radiusData[prevIndex < 0 ? arcData.length - 1 : prevIndex] = prevRadius
|
|
||||||
|
|
||||||
this.radiusData = radiusData.map(v => (v * ringRadius))
|
|
||||||
|
|
||||||
if (activeRadius >= maxRingWidthP && activeAddStatus) awaitActive()
|
|
||||||
},
|
|
||||||
awaitActive () {
|
|
||||||
const { activeTime, turnToNextActive } = this
|
|
||||||
|
|
||||||
this.activeAddStatus = false
|
|
||||||
|
|
||||||
this.awaitActiveHandler = setTimeout(turnToNextActive, activeTime)
|
|
||||||
},
|
|
||||||
turnToNextActive () {
|
|
||||||
const { arcData, activeIndex } = this
|
|
||||||
|
|
||||||
this.activePercent = 1
|
|
||||||
|
|
||||||
this.activeIndex = (activeIndex + 1 === arcData.length ? 0 : activeIndex + 1)
|
|
||||||
|
|
||||||
this.activeAddStatus = true
|
|
||||||
},
|
|
||||||
drawRing () {
|
|
||||||
const { arcData, ctx, centerPos, radiusData } = this
|
|
||||||
|
|
||||||
const { ringLineWidth, drawColors } = this
|
|
||||||
|
|
||||||
const arcNum = arcData.length
|
|
||||||
|
|
||||||
arcData.forEach((arc, i) => {
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.arc(...centerPos, radiusData[i], ...arc)
|
|
||||||
|
|
||||||
ctx.lineWidth = ringLineWidth
|
|
||||||
|
|
||||||
ctx.strokeStyle = drawColors[i % arcNum]
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
doPercentAnimation () {
|
|
||||||
const { totalValue, percent, activeIndex, data: { series }, doPercentAnimation } = this
|
|
||||||
|
|
||||||
if (!totalValue) return
|
|
||||||
|
|
||||||
const currentValue = series[activeIndex].value
|
|
||||||
|
|
||||||
let currentPercent = Math.trunc(currentValue / totalValue * 100)
|
|
||||||
|
|
||||||
currentPercent === 0 && (currentPercent = 1)
|
|
||||||
currentValue === 0 && (currentPercent = 0)
|
|
||||||
|
|
||||||
if (currentPercent === percent) return
|
|
||||||
|
|
||||||
currentPercent > percent ? this.percent++ : this.percent--
|
|
||||||
|
|
||||||
setTimeout(doPercentAnimation, 10)
|
|
||||||
},
|
|
||||||
doLabelTextAnimation () {
|
|
||||||
let { labelDom, $refs, labelRef } = this
|
|
||||||
|
|
||||||
if (!labelDom) labelDom = this.labelDom = $refs[labelRef]
|
|
||||||
|
|
||||||
labelDom.setAttribute('class', 'current-label transform-text')
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
labelDom.setAttribute('class', 'current-label')
|
|
||||||
}, 2000)
|
|
||||||
},
|
|
||||||
drwaStatic () {
|
|
||||||
const { drawStaticRing, calcAroundLineData, drawAroundLine, calcAroundTextData, drawAroundText } = this
|
|
||||||
|
|
||||||
drawStaticRing()
|
|
||||||
|
|
||||||
calcAroundLineData()
|
|
||||||
|
|
||||||
drawAroundLine()
|
|
||||||
|
|
||||||
calcAroundTextData()
|
|
||||||
|
|
||||||
drawAroundText()
|
|
||||||
},
|
|
||||||
drawStaticRing () {
|
|
||||||
const { arcData, ringRadius, drawRing } = this
|
|
||||||
|
|
||||||
this.radiusData = new Array(arcData.length).fill(1).map(v => v * ringRadius)
|
|
||||||
|
|
||||||
drawRing()
|
|
||||||
},
|
|
||||||
calcAroundLineData () {
|
|
||||||
const { arcData, ringRadius, ringLineWidth, centerPos: [x, y], data: { series }, canvas, totalValue } = this
|
|
||||||
|
|
||||||
const { getCircleRadianPoint } = canvas
|
|
||||||
|
|
||||||
const radian = arcData.map(([a, b]) => (a + (b - a) / 2))
|
|
||||||
|
|
||||||
const radius = ringRadius + ringLineWidth / 2
|
|
||||||
|
|
||||||
const aroundLineData = radian.map(r => getCircleRadianPoint(x, y, radius, r))
|
|
||||||
|
|
||||||
const lineLength = 35
|
|
||||||
|
|
||||||
this.aroundLineData = aroundLineData.map(([bx, by], i) => {
|
|
||||||
if (!series[i].value && totalValue) return [false, false]
|
|
||||||
|
|
||||||
const lineEndXPos = (bx > x ? bx + lineLength : bx - lineLength)
|
|
||||||
|
|
||||||
return [
|
|
||||||
[bx, by],
|
|
||||||
[lineEndXPos, by]
|
|
||||||
]
|
|
||||||
})
|
|
||||||
},
|
|
||||||
drawAroundLine () {
|
|
||||||
const { aroundLineData, drawColors, ctx, canvas: { drawLine } } = this
|
|
||||||
|
|
||||||
const colorNum = drawColors.length
|
|
||||||
|
|
||||||
aroundLineData.forEach(([lineBegin, lineEnd], i) =>
|
|
||||||
lineBegin !== false &&
|
|
||||||
drawLine(ctx, lineBegin, lineEnd, 1, drawColors[i % colorNum]))
|
|
||||||
},
|
|
||||||
calcAroundTextData () {
|
|
||||||
const { data: { series, fixed }, totalValue } = this
|
|
||||||
|
|
||||||
const aroundTextData = this.aroundTextData = []
|
|
||||||
|
|
||||||
if (!totalValue) return data.forEach(({ v, title }, i) => aroundTextData.push([0, title]))
|
|
||||||
|
|
||||||
const dataLast = series.length - 1
|
|
||||||
|
|
||||||
let totalPercent = 0
|
|
||||||
|
|
||||||
series.forEach(({ value, title }, i) => {
|
|
||||||
if (!value) return aroundTextData.push([false, false])
|
|
||||||
|
|
||||||
let percent = Number((value / totalValue * 100).toFixed(fixed || 1))
|
|
||||||
|
|
||||||
percent < 0.1 && (percent = 0.1)
|
|
||||||
|
|
||||||
const currentPercent = (i === dataLast ? 100 - totalPercent : percent).toFixed(fixed || 1)
|
|
||||||
|
|
||||||
aroundTextData.push([currentPercent, title])
|
|
||||||
|
|
||||||
totalPercent += percent
|
|
||||||
})
|
|
||||||
},
|
|
||||||
drawAroundText () {
|
|
||||||
const { ctx, aroundTextData, aroundTextFont, aroundLineData, centerPos: [x] } = this
|
|
||||||
|
|
||||||
ctx.font = aroundTextFont
|
|
||||||
ctx.fillStyle = '#fff'
|
|
||||||
|
|
||||||
aroundTextData.forEach(([percent, title], i) => {
|
|
||||||
if (percent === false) return
|
|
||||||
|
|
||||||
const currentPos = aroundLineData[i][1]
|
|
||||||
|
|
||||||
ctx.textAlign = 'start'
|
|
||||||
|
|
||||||
currentPos[0] < x && (ctx.textAlign = 'end')
|
|
||||||
|
|
||||||
ctx.textBaseline = 'bottom'
|
|
||||||
ctx.fillText(`${percent}%`, ...currentPos)
|
|
||||||
|
|
||||||
ctx.textBaseline = 'top'
|
|
||||||
ctx.fillText(title, ...currentPos)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
reDraw () {
|
|
||||||
const { activeAnimationHandler, draw } = this
|
|
||||||
|
|
||||||
cancelAnimationFrame(activeAnimationHandler)
|
|
||||||
|
|
||||||
draw()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
const { init } = this
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.ring-chart {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.canvas-container {
|
|
||||||
position: relative;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-info {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
text-align: center;
|
|
||||||
font-family: "Microsoft Yahei", Arial, sans-serif;
|
|
||||||
max-width: 25%;
|
|
||||||
|
|
||||||
.percent-show {
|
|
||||||
font-size: 28px;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '%';
|
|
||||||
font-size: 15px;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.current-label {
|
|
||||||
font-size: 16px;
|
|
||||||
margin-top: 5%;
|
|
||||||
transform: rotateY(0deg);
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.transform-text {
|
|
||||||
animation: transform-text 2s linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes transform-text {
|
|
||||||
to {
|
|
||||||
transform: rotateY(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,272 +1,306 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="water-level-pond">
|
<div class="dv-water-pond-level">
|
||||||
<loading v-if="!status" />
|
<svg v-if="render">
|
||||||
|
|
||||||
<svg class="svg-container">
|
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient :id="id" x1="0%" y1="100%" x2="0%" y2="0%">
|
<linearGradient :id="gradientId" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||||
<stop v-for="lc in linearGradient" :key="lc[0]"
|
<stop v-for="lc in svgBorderGradient" :key="lc[0]"
|
||||||
:offset="lc[0]"
|
:offset="lc[0]"
|
||||||
:stop-color="lc[1]" />
|
:stop-color="lc[1]" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
|
|
||||||
<text :stroke="`url(#${id})`"
|
<text
|
||||||
:fill="`url(#${id})`"
|
v-if="render"
|
||||||
:x="centerPos[0] + 8"
|
:stroke="`url(#${gradientId})`"
|
||||||
:y="centerPos[1] + 8">
|
:fill="`url(#${gradientId})`"
|
||||||
{{ (level && Math.max(...level)) || 0 }}%
|
:x="render.area[0] / 2 + 8"
|
||||||
|
:y="render.area[1] / 2 + 8"
|
||||||
|
>
|
||||||
|
{{ details }}
|
||||||
</text>
|
</text>
|
||||||
|
|
||||||
<ellipse v-if="!type || type === 'circle'"
|
<ellipse v-if="!shape || shape === 'round'"
|
||||||
:cx="centerPos[0] + 8"
|
:cx="render.area[0] / 2 + 8"
|
||||||
:cy="centerPos[1] + 8"
|
:cy="render.area[1] / 2 + 8"
|
||||||
:rx="canvasWH[0] / 2 + 5"
|
:rx="render.area[0] / 2 + 5"
|
||||||
:ry="canvasWH[1] / 2 + 5"
|
:ry="render.area[1] / 2 + 5"
|
||||||
:stroke="`url(#${id})`" />
|
:stroke="`url(#${gradientId})`" />
|
||||||
|
|
||||||
<rect v-else
|
<rect v-else
|
||||||
x="2" y="2"
|
x="2" y="2"
|
||||||
:rx="type === 'roundRect' && 10" :ry="type === 'roundRect' && 10"
|
:rx="shape === 'roundRect' ? 10 : 0"
|
||||||
:width="canvasWH[0] + 12"
|
:ry="shape === 'roundRect' ? 10 : 0"
|
||||||
:height="canvasWH[1] + 12"
|
:width="render.area[0] + 12"
|
||||||
:stroke="`url(#${id})`" />
|
:height="render.area[1] + 12"
|
||||||
|
:stroke="`url(#${gradientId})`" />
|
||||||
</svg>
|
</svg>
|
||||||
<canvas :ref="ref" :style="`border-radius: ${radius};`" />
|
|
||||||
|
<canvas ref="water-pond-level" :style="`border-radius: ${radius};`" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import canvasMixin from '../../mixins/canvasMixin.js'
|
import { deepMerge } from '@jiaminghi/charts/lib/util/index'
|
||||||
|
|
||||||
|
import { deepClone } from '@jiaminghi/c-render/lib/plugin/util'
|
||||||
|
|
||||||
|
import CRender from '@jiaminghi/c-render'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WaterLevelPond',
|
name: 'waterLevelPond',
|
||||||
mixins: [canvasMixin],
|
props: {
|
||||||
|
config: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
ref: `water-level-pond-${(new Date()).getTime()}`,
|
gradientId: `water-level-pond-${(new Date()).getTime()}`,
|
||||||
|
|
||||||
status: false,
|
defaultConfig: {
|
||||||
|
/**
|
||||||
id: `water-level-pond-${(new Date()).getTime()}`,
|
* @description Data
|
||||||
|
* @type {Array<Number>}
|
||||||
defaultColor: ['#00BAFF', '#3DE7C9'],
|
* @default data = []
|
||||||
|
* @example data = [60, 40]
|
||||||
defaultWaveNum: 3,
|
*/
|
||||||
defaultWaveHeight: 0.2,
|
data: [],
|
||||||
defaultWaveOffset: -0.5,
|
/**
|
||||||
|
* @description Shape of wanter level pond
|
||||||
waveAdded: 0.7,
|
* @type {String}
|
||||||
|
* @default shape = 'rect'
|
||||||
drawColor: '',
|
* @example shape = 'rect' | 'roundRect' | 'round'
|
||||||
linearGradient: [],
|
*/
|
||||||
waveTrueNum: '',
|
shape: 'rect',
|
||||||
waveTrueHeight: '',
|
/**
|
||||||
waveTrueWidth: '',
|
* @description Water wave number
|
||||||
wavePoints: [],
|
* @type {Number}
|
||||||
bottomPoints: [],
|
* @default waveNum = 3
|
||||||
overXPos: 0,
|
*/
|
||||||
currentPoints: [],
|
waveNum: 3,
|
||||||
animationHandler: ''
|
/**
|
||||||
}
|
* @description Water wave height (px)
|
||||||
|
* @type {Number}
|
||||||
|
* @default waveHeight = 40
|
||||||
|
*/
|
||||||
|
waveHeight: 40,
|
||||||
|
/**
|
||||||
|
* @description Wave opacity
|
||||||
|
* @type {Number}
|
||||||
|
* @default waveOpacity = 0.4
|
||||||
|
*/
|
||||||
|
waveOpacity: 0.4,
|
||||||
|
/**
|
||||||
|
* @description Colors (Hex|rgb|rgba)
|
||||||
|
* @type {Array<String>}
|
||||||
|
* @default colors = ['#00BAFF', '#3DE7C9']
|
||||||
|
*/
|
||||||
|
colors: ['#3DE7C9', '#00BAFF'],
|
||||||
|
/**
|
||||||
|
* @description Formatter
|
||||||
|
* @type {String}
|
||||||
|
* @default formatter = '{value}%'
|
||||||
|
*/
|
||||||
|
formatter: '{value}%'
|
||||||
},
|
},
|
||||||
props: ['level', 'type', 'colors', 'waveNum', 'waveHeight', 'borderColor', 'noGradient'],
|
|
||||||
watch: {
|
|
||||||
level () {
|
|
||||||
const { checkData, draw } = this
|
|
||||||
|
|
||||||
checkData() && draw()
|
mergedConfig: {},
|
||||||
|
|
||||||
|
render: null,
|
||||||
|
|
||||||
|
svgBorderGradient: [],
|
||||||
|
|
||||||
|
details: '',
|
||||||
|
|
||||||
|
waves: [],
|
||||||
|
|
||||||
|
animation: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
radius () {
|
radius () {
|
||||||
const { type } = this
|
const { shape } = this.mergedConfig
|
||||||
|
|
||||||
if (type === 'circle') return '50%'
|
if (shape === 'round') return '50%'
|
||||||
|
|
||||||
if (type === 'rect') return '0'
|
if (shape === 'rect') return '0'
|
||||||
|
|
||||||
if (type === 'roundRect') return '10px'
|
if (shape === 'roundRect') return '10px'
|
||||||
|
|
||||||
return '50%'
|
return '0'
|
||||||
|
},
|
||||||
|
shape () {
|
||||||
|
const { shape } = this.mergedConfig
|
||||||
|
|
||||||
|
if (!shape) return 'rect'
|
||||||
|
|
||||||
|
return shape
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
config () {
|
||||||
|
const { calcData, render } = this
|
||||||
|
|
||||||
|
render.delAllGraph()
|
||||||
|
|
||||||
|
this.waves = []
|
||||||
|
|
||||||
|
setTimeout(calcData, 0)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async init () {
|
init () {
|
||||||
const { initCanvas, checkData, draw } = this
|
const { initRender, config, calcData } = this
|
||||||
|
|
||||||
await initCanvas()
|
initRender()
|
||||||
|
|
||||||
checkData() && draw()
|
if (!config) return
|
||||||
|
|
||||||
|
calcData()
|
||||||
},
|
},
|
||||||
checkData () {
|
initRender () {
|
||||||
const { level } = this
|
const { $refs } = this
|
||||||
|
|
||||||
this.status = false
|
this.render = new CRender($refs['water-pond-level'])
|
||||||
|
|
||||||
if (!level || !level.length) return false
|
|
||||||
|
|
||||||
this.status = true
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
},
|
||||||
draw () {
|
calcData () {
|
||||||
const { stopAnimation, clearCanvas } = this
|
const { mergeConfig, calcSvgBorderGradient, calcDetails } = this
|
||||||
|
|
||||||
stopAnimation()
|
mergeConfig()
|
||||||
|
|
||||||
clearCanvas()
|
calcSvgBorderGradient()
|
||||||
|
|
||||||
const { initColor, calcBorderLinearColor, calcWaveData } = this
|
calcDetails()
|
||||||
|
|
||||||
initColor()
|
const { addWave, animationWave } = this
|
||||||
|
|
||||||
calcBorderLinearColor()
|
addWave()
|
||||||
|
|
||||||
calcWaveData()
|
animationWave()
|
||||||
|
|
||||||
const { calcBottomPoints, calcOverXPos, drawWaveAnimation } = this
|
|
||||||
|
|
||||||
calcBottomPoints()
|
|
||||||
|
|
||||||
calcOverXPos()
|
|
||||||
|
|
||||||
drawWaveAnimation()
|
|
||||||
},
|
},
|
||||||
initColor () {
|
mergeConfig () {
|
||||||
const { colors, defaultColor } = this
|
const { config, defaultConfig } = this
|
||||||
|
|
||||||
this.drawColor = colors || defaultColor
|
this.mergedConfig = deepMerge(deepClone(defaultConfig, true), config)
|
||||||
},
|
},
|
||||||
calcBorderLinearColor () {
|
calcSvgBorderGradient () {
|
||||||
const { colors, defaultColor, borderColor } = this
|
const { colors } = this.mergedConfig
|
||||||
|
|
||||||
let trueColor = borderColor || colors || defaultColor
|
const colorNum = colors.length
|
||||||
|
|
||||||
typeof trueColor === 'string' && (trueColor = [trueColor, trueColor])
|
|
||||||
|
|
||||||
const colorNum = trueColor.length
|
|
||||||
|
|
||||||
const colorOffsetGap = 100 / (colorNum - 1)
|
const colorOffsetGap = 100 / (colorNum - 1)
|
||||||
|
|
||||||
this.linearGradient = trueColor.map((c, i) => [colorOffsetGap * i, c])
|
this.svgBorderGradient = colors.map((c, i) => [colorOffsetGap * i, c])
|
||||||
},
|
},
|
||||||
calcWaveData () {
|
calcDetails () {
|
||||||
const { waveNum, waveHeight, defaultWaveNum, defaultWaveHeight, canvasWH } = this
|
const { data, formatter } = this.mergedConfig
|
||||||
|
|
||||||
const waveTrueNum = this.waveTrueNum = waveNum || defaultWaveNum
|
if (!data.length) {
|
||||||
|
this.details = ''
|
||||||
|
|
||||||
const waveTrueHeight = this.waveTrueHeight = (waveHeight || defaultWaveHeight) * canvasWH[1]
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const waveWidth = this.waveTrueWidth = canvasWH[0] / waveTrueNum
|
const maxValue = Math.max(...data)
|
||||||
|
|
||||||
const { waveOffset, defaultWaveOffset, addWavePoint } = this
|
this.details = formatter.replace('{value}', maxValue)
|
||||||
|
|
||||||
const waveOffsetLength = waveTrueHeight * (waveOffset || defaultWaveOffset)
|
|
||||||
|
|
||||||
const waveTop = waveTrueHeight + waveOffsetLength + canvasWH[1]
|
|
||||||
|
|
||||||
const waveBottom = waveOffsetLength + canvasWH[1]
|
|
||||||
|
|
||||||
const halfWidth = waveWidth / 2
|
|
||||||
|
|
||||||
this.wavePoints = new Array(waveTrueNum * 2 + 1).fill(0).map((t, i) =>
|
|
||||||
[i * halfWidth, i % 2 === 0 ? waveBottom : waveTop])
|
|
||||||
|
|
||||||
addWavePoint() && addWavePoint() && addWavePoint()
|
|
||||||
},
|
},
|
||||||
addWavePoint () {
|
addWave () {
|
||||||
const { wavePoints, waveTrueWidth } = this
|
const { render, getWaveShapes, getWaveStyle, drawed } = this
|
||||||
|
|
||||||
const addPoint = [wavePoints[1][0] - waveTrueWidth, wavePoints[1][1]]
|
const shapes = getWaveShapes()
|
||||||
|
const style = getWaveStyle()
|
||||||
|
|
||||||
return wavePoints.unshift(addPoint)
|
this.waves = shapes.map(shape => render.add({
|
||||||
|
name: 'smoothline',
|
||||||
|
animationFrame: 300,
|
||||||
|
shape,
|
||||||
|
style,
|
||||||
|
drawed
|
||||||
|
}))
|
||||||
},
|
},
|
||||||
calcBottomPoints () {
|
getWaveShapes () {
|
||||||
const { canvasWH } = this
|
const { mergedConfig, render, mergeOffset } = this
|
||||||
|
|
||||||
this.bottomPoints = [
|
const { waveNum, waveHeight, data } = mergedConfig
|
||||||
[...canvasWH],
|
|
||||||
[0, canvasWH[1]]
|
const [w, h] = render.area
|
||||||
]
|
|
||||||
|
const pointsNum = waveNum * 4 + 4
|
||||||
|
|
||||||
|
const pointXGap = w / waveNum / 2
|
||||||
|
|
||||||
|
return data.map(v => {
|
||||||
|
let points = new Array(pointsNum).fill(0).map((foo, j) => {
|
||||||
|
const x = w - pointXGap * j
|
||||||
|
|
||||||
|
const startY = (1 - v / 100) * h
|
||||||
|
|
||||||
|
const y = j % 2 === 0 ? startY : startY - waveHeight
|
||||||
|
|
||||||
|
return [x, y]
|
||||||
|
})
|
||||||
|
|
||||||
|
points = points.map(p => mergeOffset(p, [pointXGap * 2, 0]))
|
||||||
|
|
||||||
|
return { points }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
calcOverXPos () {
|
mergeOffset ([x, y], [ox, oy]) {
|
||||||
const { canvasWH: [width], waveTrueWidth } = this
|
return [x + ox, y + oy]
|
||||||
|
|
||||||
this.overXPos = width + waveTrueWidth
|
|
||||||
},
|
},
|
||||||
drawWaveAnimation () {
|
getWaveStyle () {
|
||||||
const { clearCanvas, drawWaveAnimation } = this
|
const { render, mergedConfig } = this
|
||||||
|
|
||||||
clearCanvas()
|
const h = render.area[1]
|
||||||
|
|
||||||
const { getCurrentPoints, drawCurrentWave, calcNextFramePoints } = this
|
return {
|
||||||
|
gradientColor: mergedConfig.colors,
|
||||||
getCurrentPoints()
|
gradientType: 'linear',
|
||||||
|
gradientParams: [0, 0, 0, h],
|
||||||
drawCurrentWave()
|
gradientWith: 'fill',
|
||||||
|
opacity: mergedConfig.waveOpacity,
|
||||||
calcNextFramePoints()
|
translate: [0, 0]
|
||||||
|
}
|
||||||
this.animationHandler = requestAnimationFrame(drawWaveAnimation)
|
|
||||||
},
|
},
|
||||||
getCurrentPoints () {
|
drawed ({ shape: { points } }, { ctx, area }) {
|
||||||
const { level, wavePoints, canvasWH: [, height] } = this
|
const firstPoint = points[0]
|
||||||
|
const lastPoint = points.slice(-1)[0]
|
||||||
|
|
||||||
this.currentPoints = level.map(l =>
|
const [w, h] = area
|
||||||
wavePoints.map(([x, y]) =>
|
|
||||||
[x, y - (l / 100 * height)]))
|
|
||||||
},
|
|
||||||
drawCurrentWave () {
|
|
||||||
const { currentPoints, ctx, bottomPoints, drawColor, canvasWH: [, y], noGradient } = this
|
|
||||||
|
|
||||||
const { canvas: { drawSmoothlinePath, getLinearGradientColor } } = this
|
ctx.lineTo(lastPoint[0], h)
|
||||||
|
ctx.lineTo(firstPoint[0], h)
|
||||||
const { color: { hexToRgb } } = this
|
|
||||||
|
|
||||||
const multipleColor = typeof drawColor === 'object'
|
|
||||||
|
|
||||||
!multipleColor && (ctx.fillStyle = drawColor)
|
|
||||||
|
|
||||||
multipleColor &&
|
|
||||||
!noGradient &&
|
|
||||||
(ctx.fillStyle = getLinearGradientColor(ctx, [0, y], [0, 0], drawColor.map(c => hexToRgb(c, 0.5))))
|
|
||||||
|
|
||||||
const colorNum = drawColor.length
|
|
||||||
|
|
||||||
currentPoints.forEach((line, i) => {
|
|
||||||
drawSmoothlinePath(ctx, line, false, true, true)
|
|
||||||
|
|
||||||
ctx.lineTo(...bottomPoints[0])
|
|
||||||
ctx.lineTo(...bottomPoints[1])
|
|
||||||
|
|
||||||
ctx.closePath()
|
ctx.closePath()
|
||||||
|
|
||||||
multipleColor && noGradient && (ctx.fillStyle = drawColor[i % colorNum])
|
|
||||||
|
|
||||||
ctx.fill()
|
ctx.fill()
|
||||||
|
},
|
||||||
|
async animationWave (repeat = 1) {
|
||||||
|
const { waves, render, animation } = this
|
||||||
|
|
||||||
|
if (animation) return
|
||||||
|
|
||||||
|
this.animation = true
|
||||||
|
|
||||||
|
const w = render.area[0]
|
||||||
|
|
||||||
|
waves.forEach(graph => {
|
||||||
|
const reset = repeat % 2 === 0
|
||||||
|
|
||||||
|
graph.attr('style', { translate: [0, 0] })
|
||||||
|
|
||||||
|
graph.animation('style', {
|
||||||
|
translate: [w, 0]
|
||||||
|
}, true)
|
||||||
})
|
})
|
||||||
},
|
|
||||||
calcNextFramePoints () {
|
|
||||||
const { wavePoints, waveAdded, addWavePoint, overXPos } = this
|
|
||||||
|
|
||||||
const addedWavePoints = wavePoints.map(([x, y]) => [x + waveAdded, y])
|
await render.launchAnimation()
|
||||||
|
|
||||||
const lastPointIndex = addedWavePoints.length - 1
|
this.animation = false
|
||||||
|
|
||||||
let addStatus = false
|
if (!render.graphs.length) return
|
||||||
|
|
||||||
addedWavePoints[lastPointIndex][0] > overXPos &&
|
this.animationWave(repeat + 1)
|
||||||
addedWavePoints.pop() && (addStatus = true)
|
|
||||||
|
|
||||||
this.wavePoints = addedWavePoints
|
|
||||||
|
|
||||||
addStatus && addWavePoint()
|
|
||||||
},
|
|
||||||
stopAnimation () {
|
|
||||||
const { animationHandler } = this
|
|
||||||
|
|
||||||
animationHandler && cancelAnimationFrame(animationHandler)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
@ -274,27 +308,21 @@ export default {
|
||||||
|
|
||||||
init()
|
init()
|
||||||
},
|
},
|
||||||
destroyed () {
|
beforeDestroy () {
|
||||||
const { stopAnimation } = this
|
const { calcData, render } = this
|
||||||
|
|
||||||
stopAnimation()
|
render.delAllGraph()
|
||||||
|
|
||||||
|
this.waves = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.water-level-pond {
|
.dv-water-pond-level {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.percent-text {
|
svg {
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
font-weight: bold;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.svg-container {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -320,7 +348,6 @@ export default {
|
||||||
width: calc(~"100% - 16px");
|
width: calc(~"100% - 16px");
|
||||||
height: calc(~"100% - 16px");
|
height: calc(~"100% - 16px");
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 50%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
export default {}
|
|
3
index.js
|
@ -1,8 +1,5 @@
|
||||||
import components from './components/index'
|
import components from './components/index'
|
||||||
|
|
||||||
import plugins from './plugins'
|
|
||||||
|
|
||||||
export default function (Vue) {
|
export default function (Vue) {
|
||||||
components(Vue)
|
components(Vue)
|
||||||
plugins(Vue)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,17 +12,19 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async init () {
|
async autoResizeMixinInit () {
|
||||||
const { initWH, getDebounceInitWHFun, bindDomResizeCallback } = this
|
const { initWH, getDebounceInitWHFun, bindDomResizeCallback, afterAutoResizeMixinInit } = this
|
||||||
|
|
||||||
await initWH()
|
await initWH()
|
||||||
|
|
||||||
getDebounceInitWHFun()
|
getDebounceInitWHFun()
|
||||||
|
|
||||||
bindDomResizeCallback()
|
bindDomResizeCallback()
|
||||||
|
|
||||||
|
if (typeof afterAutoResizeMixinInit === 'function') afterAutoResizeMixinInit()
|
||||||
},
|
},
|
||||||
initWH () {
|
initWH () {
|
||||||
const { $nextTick, $refs, ref } = this
|
const { $nextTick, $refs, ref, onResize } = this
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
$nextTick(e => {
|
$nextTick(e => {
|
||||||
|
@ -31,6 +33,8 @@ export default {
|
||||||
this.width = dom.clientWidth
|
this.width = dom.clientWidth
|
||||||
this.height = dom.clientHeight
|
this.height = dom.clientHeight
|
||||||
|
|
||||||
|
if (typeof onResize === 'function') onResize()
|
||||||
|
|
||||||
resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -58,9 +62,9 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
const { init } = this
|
const { autoResizeMixinInit } = this
|
||||||
|
|
||||||
init()
|
autoResizeMixinInit()
|
||||||
},
|
},
|
||||||
beforeDestroyed () {
|
beforeDestroyed () {
|
||||||
const { unbindDomResizeCallback } = this
|
const { unbindDomResizeCallback } = this
|
|
@ -1,672 +0,0 @@
|
||||||
export default {
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
// config able
|
|
||||||
defaultAxisFontSize: 10,
|
|
||||||
defaultAxisFontFamily: 'Arial',
|
|
||||||
|
|
||||||
defaultAxisLineColor: '#666',
|
|
||||||
defaultGridLineColor: '#666',
|
|
||||||
defaultAxisTagColor: '#666',
|
|
||||||
defaultAxisUnitColor: '#666',
|
|
||||||
|
|
||||||
defaultGridLineDash: [2, 2],
|
|
||||||
|
|
||||||
defaultNoAxisLine: false,
|
|
||||||
defaultNoAxisTag: false,
|
|
||||||
|
|
||||||
defaultXAxisOffset: 20,
|
|
||||||
defaultAxisLineTagGap: 5,
|
|
||||||
|
|
||||||
// after merge
|
|
||||||
axisFontSize: 0,
|
|
||||||
axisFontFamily: '',
|
|
||||||
|
|
||||||
axisLineColor: '',
|
|
||||||
gridLineColor: '',
|
|
||||||
axisTagColor: '',
|
|
||||||
axisUnitColor: '',
|
|
||||||
|
|
||||||
gridLineDash: [],
|
|
||||||
|
|
||||||
noAxisLine: '',
|
|
||||||
noAxisTag: '',
|
|
||||||
|
|
||||||
// calc data
|
|
||||||
valueMaxMin: [],
|
|
||||||
agValueMaxMin: [],
|
|
||||||
|
|
||||||
valueAxisMaxMin: [],
|
|
||||||
agValueAxisMaxMin: [],
|
|
||||||
|
|
||||||
valueAxisTag: [],
|
|
||||||
agValueAxisTag: [],
|
|
||||||
|
|
||||||
labelAxisTag: [],
|
|
||||||
agLabelAxisTag: [],
|
|
||||||
|
|
||||||
xAxisTagBA: ['', ''],
|
|
||||||
agXAxisTagBA: ['', ''],
|
|
||||||
yAxisTagBA: ['', ''],
|
|
||||||
agYAxisTagBA: ['', ''],
|
|
||||||
|
|
||||||
addBAValueAxisTag: [],
|
|
||||||
addBAAGValueAxisTag: [],
|
|
||||||
|
|
||||||
addBALabelAxisTag: [],
|
|
||||||
addBAAGLabelAxisTag: [],
|
|
||||||
|
|
||||||
axisUnit: [],
|
|
||||||
|
|
||||||
axisOffset: [],
|
|
||||||
|
|
||||||
axisOriginPos: [],
|
|
||||||
axisWH: [],
|
|
||||||
|
|
||||||
axisAnglePos: {},
|
|
||||||
|
|
||||||
valueAxisTagPos: [],
|
|
||||||
agValueAxisTagPos: [],
|
|
||||||
|
|
||||||
labelAxisTagPos: [],
|
|
||||||
agLabelAxisTagPos: [],
|
|
||||||
|
|
||||||
tagAlign: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
initAxis () {
|
|
||||||
const { calcValuesMaxMin, calcValueAxisData, calcLabelAxisData } = this
|
|
||||||
|
|
||||||
calcValuesMaxMin()
|
|
||||||
|
|
||||||
calcValueAxisData()
|
|
||||||
|
|
||||||
calcLabelAxisData()
|
|
||||||
|
|
||||||
const { calcTagBA, calcAddBATag, calcAxisUnit } = this
|
|
||||||
|
|
||||||
calcTagBA()
|
|
||||||
|
|
||||||
calcAddBATag()
|
|
||||||
|
|
||||||
calcAxisUnit()
|
|
||||||
|
|
||||||
const { calcAxisFontData, calcAxisColor, calcGridLineDash } = this
|
|
||||||
|
|
||||||
calcAxisFontData()
|
|
||||||
|
|
||||||
calcAxisColor()
|
|
||||||
|
|
||||||
calcGridLineDash()
|
|
||||||
|
|
||||||
const { calcAxisLineTagStatus, calcAxisOffset, calcAxisAreaData } = this
|
|
||||||
|
|
||||||
calcAxisLineTagStatus()
|
|
||||||
|
|
||||||
calcAxisOffset()
|
|
||||||
|
|
||||||
calcAxisAreaData()
|
|
||||||
|
|
||||||
const { calcAxisAnglePos, calcValueAxisTagPos, calcLabelAxisTagPos } = this
|
|
||||||
|
|
||||||
calcAxisAnglePos()
|
|
||||||
|
|
||||||
calcValueAxisTagPos()
|
|
||||||
|
|
||||||
calcLabelAxisTagPos()
|
|
||||||
|
|
||||||
const { calcTagAlign } = this
|
|
||||||
|
|
||||||
calcTagAlign()
|
|
||||||
},
|
|
||||||
calcValuesMaxMin () {
|
|
||||||
const { data: { series }, calcValueMaxMin } = this
|
|
||||||
|
|
||||||
const valueSeries = series.filter(({ againstAxis }) => !againstAxis)
|
|
||||||
|
|
||||||
if (valueSeries.length) this.valueMaxMin = calcValueMaxMin(valueSeries)
|
|
||||||
|
|
||||||
const agValueSeries = series.filter(({ againstAxis }) => againstAxis)
|
|
||||||
|
|
||||||
if (agValueSeries.length) this.agValueMaxMin = calcValueMaxMin(agValueSeries)
|
|
||||||
},
|
|
||||||
calcValueMaxMin (series) {
|
|
||||||
const { mulValueAdd, calcMulValueAdd, getArrayMax, getArrayMin } = this
|
|
||||||
|
|
||||||
let valueSeries = series.map(({ value }) => value)
|
|
||||||
|
|
||||||
const min = getArrayMin(valueSeries)
|
|
||||||
|
|
||||||
mulValueAdd && (valueSeries = calcMulValueAdd(valueSeries))
|
|
||||||
|
|
||||||
const max = getArrayMax(valueSeries)
|
|
||||||
|
|
||||||
return [max, min]
|
|
||||||
},
|
|
||||||
calcMulValueAdd (values) {
|
|
||||||
const { multipleSum, filterNull } = this
|
|
||||||
|
|
||||||
return values.map(series =>
|
|
||||||
filterNull(series).map(n =>
|
|
||||||
n instanceof Array ? multipleSum(...filterNull(n)) : n))
|
|
||||||
},
|
|
||||||
calcValueAxisData () {
|
|
||||||
const { horizon, data: { x, ax, y, ay }, calcValueAxisTag } = this
|
|
||||||
|
|
||||||
const { valueMaxMin, agValueMaxMin, getValueAxisMaxMin } = this
|
|
||||||
|
|
||||||
const valueAxis = horizon ? [x, ax] : [y, ay]
|
|
||||||
|
|
||||||
if (valueMaxMin.length) {
|
|
||||||
const valueAxisTag = this.valueAxisTag = calcValueAxisTag(valueMaxMin, valueAxis[0])
|
|
||||||
|
|
||||||
this.valueAxisMaxMin = getValueAxisMaxMin(valueAxisTag)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (agValueMaxMin.length) {
|
|
||||||
const agValueAxisTag = this.agValueAxisTag = calcValueAxisTag(agValueMaxMin, valueAxis[1])
|
|
||||||
|
|
||||||
this.agValueAxisMaxMin = getValueAxisMaxMin(agValueAxisTag)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
calcValueAxisTag ([vmax, vmin], { max, min, num, fixed, tags } = {}) {
|
|
||||||
if (tags) return tags
|
|
||||||
|
|
||||||
let [trueMax, trueMin] = [max, min]
|
|
||||||
|
|
||||||
if (vmax === 0 && vmin === 0) vmax = 8
|
|
||||||
|
|
||||||
const thirdValueMinus = parseInt((vmax - vmin) / 3)
|
|
||||||
|
|
||||||
!max && (max !== 0) && (trueMax = vmax + thirdValueMinus)
|
|
||||||
!min && (min !== 0) && (trueMin = vmin - thirdValueMinus)
|
|
||||||
|
|
||||||
const trueMinus = trueMax - trueMin
|
|
||||||
|
|
||||||
!num && trueMinus < 9 && (num = Math.ceil(trueMinus) + 1)
|
|
||||||
!num && (num = 10)
|
|
||||||
|
|
||||||
const valueGap = trueMinus / (num - 1)
|
|
||||||
|
|
||||||
return Array(num).fill(0).map((t, i) =>
|
|
||||||
(trueMin + i * valueGap).toFixed(fixed))
|
|
||||||
},
|
|
||||||
getValueAxisMaxMin (valueTag) {
|
|
||||||
const lastIndex = valueTag.length - 1
|
|
||||||
|
|
||||||
return [
|
|
||||||
parseFloat(valueTag[lastIndex]),
|
|
||||||
parseFloat(valueTag[0])
|
|
||||||
]
|
|
||||||
},
|
|
||||||
calcLabelAxisData () {
|
|
||||||
const { horizon, data: { x, ax, y, ay } } = this
|
|
||||||
|
|
||||||
const labelAxis = horizon ? [y, ay] : [x, ax]
|
|
||||||
|
|
||||||
if (labelAxis[0] && labelAxis[0].tags) this.labelAxisTag = labelAxis[0].tags
|
|
||||||
|
|
||||||
if (labelAxis[1] && labelAxis[1].tags) this.agLabelAxisTag = labelAxis[1].tags
|
|
||||||
},
|
|
||||||
calcTagBA () {
|
|
||||||
const { data: { x, ax, y, ay } } = this
|
|
||||||
|
|
||||||
if (x && x.tagBefore) this.xAxisTagBA[0] = x.tagBefore
|
|
||||||
if (ax && ax.tagBefore) this.agXAxisTagBA[0] = ax.tagBefore
|
|
||||||
if (y && y.tagBefore) this.yAxisTagBA[0] = y.tagBefore
|
|
||||||
if (ay && ay.tagBefore) this.agYAxisTagBA[0] = ay.tagBefore
|
|
||||||
|
|
||||||
if (x && x.tagAfter) this.xAxisTagBA[1] = x.tagAfter
|
|
||||||
if (ax && ax.tagAfter) this.agXAxisTagBA[1] = ax.tagAfter
|
|
||||||
if (y && y.tagAfter) this.yAxisTagBA[1] = y.tagAfter
|
|
||||||
if (ay && ay.tagAfter) this.agYAxisTagBA[1] = ay.tagAfter
|
|
||||||
},
|
|
||||||
calcAddBATag () {
|
|
||||||
const { xAxisTagBA, agXAxisTagBA, yAxisTagBA, agYAxisTagBA } = this
|
|
||||||
|
|
||||||
const { valueAxisTag, agValueAxisTag, labelAxisTag, agLabelAxisTag } = this
|
|
||||||
|
|
||||||
const { horizon, addBATag } = this
|
|
||||||
|
|
||||||
const valueTagBA = horizon ? [xAxisTagBA, agXAxisTagBA] : [yAxisTagBA, agYAxisTagBA]
|
|
||||||
|
|
||||||
const labelTagBA = horizon ? [yAxisTagBA, agYAxisTagBA] : [xAxisTagBA, agXAxisTagBA]
|
|
||||||
|
|
||||||
if (valueAxisTag.length) this.addBAValueAxisTag = addBATag(valueAxisTag, valueTagBA[0])
|
|
||||||
if (agValueAxisTag.length) this.addBAAGValueAxisTag = addBATag(agValueAxisTag, valueTagBA[1])
|
|
||||||
if (labelAxisTag.length) this.addBALabelAxisTag = addBATag(labelAxisTag, labelTagBA[0])
|
|
||||||
if (agLabelAxisTag.length) this.addBAAGLabelAxisTag = addBATag(agLabelAxisTag, labelTagBA[1])
|
|
||||||
},
|
|
||||||
addBATag (tags, ba) {
|
|
||||||
return tags.map(tag => tag ? `${ba[0]}${tag}${ba[1]}` : tag)
|
|
||||||
},
|
|
||||||
calcAxisUnit () {
|
|
||||||
const { data: { x, ax, y, ay } } = this
|
|
||||||
|
|
||||||
if (x && x.unit) this.axisUnit[0] = x.unit
|
|
||||||
if (ax && ax.unit) this.axisUnit[1] = ax.unit
|
|
||||||
if (y && y.unit) this.axisUnit[2] = y.unit
|
|
||||||
if (ay && ay.unit) this.axisUnit[3] = ay.unit
|
|
||||||
},
|
|
||||||
calcAxisFontData () {
|
|
||||||
const { defaultAxisFontSize, defaultAxisFontFamily, data } = this
|
|
||||||
|
|
||||||
const { fontSize, fontFamily, axisFontSize, axisFontFamily }= data
|
|
||||||
|
|
||||||
this.axisFontSize = axisFontSize || fontSize || defaultAxisFontSize
|
|
||||||
|
|
||||||
this.axisFontFamily = axisFontFamily || fontFamily || defaultAxisFontFamily
|
|
||||||
},
|
|
||||||
calcAxisColor () {
|
|
||||||
const { data, defaultAxisTagColor, defaultAxisLineColor, defaultGridLineColor } = this
|
|
||||||
|
|
||||||
const { axisTagColor, axisLineColor, gridLineColor } = data
|
|
||||||
|
|
||||||
this.axisTagColor = axisTagColor || defaultAxisTagColor
|
|
||||||
|
|
||||||
this.axisLineColor = axisLineColor || defaultAxisLineColor
|
|
||||||
|
|
||||||
this.gridLineColor = gridLineColor || defaultGridLineColor
|
|
||||||
},
|
|
||||||
calcGridLineDash () {
|
|
||||||
const { data, defaultGridLineDash } = this
|
|
||||||
|
|
||||||
const { gridLineDash } = data
|
|
||||||
|
|
||||||
if (gridLineDash instanceof Array) {
|
|
||||||
this.gridLineDash = gridLineDash
|
|
||||||
} else if (gridLineDash) {
|
|
||||||
this.gridLineDash = defaultGridLineDash
|
|
||||||
} else {
|
|
||||||
this.gridLineDash = [10, 0]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
calcAxisLineTagStatus () {
|
|
||||||
const { defaultNoAxisLine, defaultNoAxisTag, data } = this
|
|
||||||
|
|
||||||
const { noAxisLine, noAxisTag } = data
|
|
||||||
|
|
||||||
this.noAxisLine = noAxisLine || defaultNoAxisLine
|
|
||||||
|
|
||||||
this.noAxisTag = noAxisTag || defaultNoAxisTag
|
|
||||||
},
|
|
||||||
calcAxisOffset () {
|
|
||||||
const { horizon, axisUnit, defaultXAxisOffset } = this
|
|
||||||
|
|
||||||
const { addBAValueAxisTag, addBAAGValueAxisTag, addBALabelAxisTag, addBAAGLabelAxisTag } = this
|
|
||||||
|
|
||||||
const { axisFontSize, axisFontFamily, defaultAxisLineTagGap } = this
|
|
||||||
|
|
||||||
const { data: { x, ax, y, ay } } = this
|
|
||||||
|
|
||||||
const { ctx, canvas: { getTextsWidth }, boundaryGap } = this
|
|
||||||
|
|
||||||
ctx.font = `${axisFontSize}px ${axisFontFamily}`
|
|
||||||
|
|
||||||
this.axisOffset[0] = (ax && ax.offset) || defaultXAxisOffset
|
|
||||||
this.axisOffset[2] = (x && x.offset) || defaultXAxisOffset
|
|
||||||
|
|
||||||
const horizonAxisTags = horizon
|
|
||||||
? [addBALabelAxisTag, addBAAGLabelAxisTag]
|
|
||||||
: [addBAValueAxisTag, addBAAGValueAxisTag]
|
|
||||||
|
|
||||||
this.axisOffset[3] = (y && y.offset) ||
|
|
||||||
Math.max(...getTextsWidth(ctx, [axisUnit[2] || '']),
|
|
||||||
...getTextsWidth(ctx, horizonAxisTags[0].length ? horizonAxisTags[0] : 0)) + defaultAxisLineTagGap
|
|
||||||
|
|
||||||
// axis offset 1
|
|
||||||
const xAxisTags = horizon ? addBAValueAxisTag : addBALabelAxisTag
|
|
||||||
|
|
||||||
let xAxisTagsHalfWidth = 0
|
|
||||||
|
|
||||||
xAxisTags.length && (xAxisTagsHalfWidth = ctx.measureText(xAxisTags.length - 1).width / 2)
|
|
||||||
|
|
||||||
let rightOffset = Math.max(...getTextsWidth(ctx, [axisUnit[3] || '']),
|
|
||||||
...getTextsWidth(ctx, [axisUnit[0] || '']),
|
|
||||||
...getTextsWidth(ctx, horizonAxisTags[1].length ? horizonAxisTags[1] : [''])) + defaultAxisLineTagGap
|
|
||||||
|
|
||||||
;(!boundaryGap || horizon) && (rightOffset += (xAxisTagsHalfWidth + 5))
|
|
||||||
|
|
||||||
this.axisOffset[1] = (ay && ay.offset) || rightOffset
|
|
||||||
|
|
||||||
if (y && y.noTag) this.axisOffset[3] = 1
|
|
||||||
if (ay && ay.noTag) this.axisOffset[1] = 1
|
|
||||||
},
|
|
||||||
calcAxisAreaData () {
|
|
||||||
const { canvasWH, axisOffset, axisWH, axisOriginPos } = this
|
|
||||||
|
|
||||||
axisWH[0] = canvasWH[0] - axisOffset[1] - axisOffset[3]
|
|
||||||
axisWH[1] = canvasWH[1] - axisOffset[0] - axisOffset[2]
|
|
||||||
|
|
||||||
axisOriginPos[0] = axisOffset[3]
|
|
||||||
axisOriginPos[1] = axisWH[1] + axisOffset[0]
|
|
||||||
},
|
|
||||||
calcAxisAnglePos () {
|
|
||||||
const { axisWH, axisOriginPos, axisAnglePos } = this
|
|
||||||
|
|
||||||
axisAnglePos.leftTop = [axisOriginPos[0], axisOriginPos[1] - axisWH[1]]
|
|
||||||
axisAnglePos.rightTop = [axisOriginPos[0] + axisWH[0], axisOriginPos[1] - axisWH[1]]
|
|
||||||
|
|
||||||
axisAnglePos.leftBottom = axisOriginPos
|
|
||||||
axisAnglePos.rightBottom = [axisOriginPos[0] + axisWH[0], axisOriginPos[1]]
|
|
||||||
},
|
|
||||||
calcValueAxisTagPos () {
|
|
||||||
const { horizon, axisOriginPos, axisAnglePos } = this
|
|
||||||
|
|
||||||
const { valueAxisTag, agValueAxisTag, getValueAxisTagPos } = this
|
|
||||||
|
|
||||||
if (valueAxisTag) this.valueAxisTagPos = getValueAxisTagPos(valueAxisTag, axisOriginPos)
|
|
||||||
|
|
||||||
const basePoint = horizon ? axisAnglePos.leftTop : axisAnglePos.rightBottom
|
|
||||||
|
|
||||||
if (agValueAxisTag) this.agValueAxisTagPos = getValueAxisTagPos(agValueAxisTag, basePoint)
|
|
||||||
},
|
|
||||||
getValueAxisTagPos (tags, [x, y]) {
|
|
||||||
const { horizon, axisWH } = this
|
|
||||||
|
|
||||||
const tagsNum = tags.length
|
|
||||||
|
|
||||||
const areaLength = horizon ? axisWH[0] : axisWH[1]
|
|
||||||
|
|
||||||
const tagGap = areaLength / (tagsNum - 1)
|
|
||||||
|
|
||||||
return new Array(tagsNum).fill(0).map((t, i) =>
|
|
||||||
horizon ? [x + tagGap * i, y] : [x, y - tagGap * i])
|
|
||||||
},
|
|
||||||
calcLabelAxisTagPos () {
|
|
||||||
const { horizon, getLabelAxisTagPos, axisAnglePos } = this
|
|
||||||
|
|
||||||
const { labelAxisTag, agLabelAxisTag, axisOriginPos } = this
|
|
||||||
|
|
||||||
if (labelAxisTag.length) this.labelAxisTagPos = getLabelAxisTagPos(labelAxisTag, axisOriginPos)
|
|
||||||
|
|
||||||
const basePoint = horizon ? axisAnglePos.rightBottom : axisAnglePos.leftTop
|
|
||||||
|
|
||||||
if (agLabelAxisTag.length) this.agLabelAxisTagPos = getLabelAxisTagPos(agLabelAxisTag, basePoint)
|
|
||||||
},
|
|
||||||
getLabelAxisTagPos (tags, [x, y]) {
|
|
||||||
const { horizon, axisWH, boundaryGap } = this
|
|
||||||
|
|
||||||
const tagsNum = tags.length
|
|
||||||
|
|
||||||
const areaLength = horizon ? axisWH[1] : axisWH[0]
|
|
||||||
|
|
||||||
let gapColumnNum = boundaryGap ? tagsNum : tagsNum - 1
|
|
||||||
|
|
||||||
gapColumnNum === 0 && (gapColumnNum = 1)
|
|
||||||
|
|
||||||
const tagGap = areaLength / gapColumnNum
|
|
||||||
|
|
||||||
const halfGap = tagGap / 2
|
|
||||||
|
|
||||||
const tempPos = new Array(tagsNum).fill(0)
|
|
||||||
|
|
||||||
if (boundaryGap) {
|
|
||||||
return tempPos.map((t, i) =>
|
|
||||||
horizon ? [x, y - (tagGap * i) - halfGap] : [x + (tagGap * i) + halfGap, y])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!boundaryGap) {
|
|
||||||
return tempPos.map((t, i) =>
|
|
||||||
horizon ? [x, y + tagGap * i] : [x + tagGap * i, y])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
calcTagAlign () {
|
|
||||||
const { tagAlign } = this
|
|
||||||
|
|
||||||
tagAlign.x = ['center', 'top']
|
|
||||||
tagAlign.y = ['right', 'middle']
|
|
||||||
tagAlign.ax = ['center', 'bottom']
|
|
||||||
tagAlign.ay = ['left', 'middle']
|
|
||||||
},
|
|
||||||
drawAxis () {
|
|
||||||
const { drawAxisLine, drawAxisTag, drawAxisUnit } = this
|
|
||||||
|
|
||||||
drawAxisLine()
|
|
||||||
|
|
||||||
drawAxisTag()
|
|
||||||
|
|
||||||
drawAxisUnit()
|
|
||||||
|
|
||||||
const { drawAxisGrid } = this
|
|
||||||
|
|
||||||
drawAxisGrid()
|
|
||||||
},
|
|
||||||
drawAxisLine () {
|
|
||||||
const { noAxisLine } = this
|
|
||||||
|
|
||||||
if (noAxisLine) return
|
|
||||||
|
|
||||||
const { ctx, horizon, axisOriginPos, axisAnglePos } = this
|
|
||||||
|
|
||||||
const { axisLineColor, agValueAxisTag, agLabelAxisTag } = this
|
|
||||||
|
|
||||||
const { data: { x, ax, y, ay } } = this
|
|
||||||
|
|
||||||
ctx.lineWidth = 1
|
|
||||||
|
|
||||||
if (!x || !x.noAxisLine) {
|
|
||||||
ctx.strokeStyle = (x && x.axisLineColor) || axisLineColor
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.moveTo(...axisOriginPos)
|
|
||||||
ctx.lineTo(...axisAnglePos.rightBottom)
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!y || !y.noAxisLine) {
|
|
||||||
ctx.strokeStyle = (y && y.axisLineColor) || axisLineColor
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.moveTo(...axisOriginPos)
|
|
||||||
ctx.lineTo(...axisAnglePos.leftTop)
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
|
|
||||||
const agValueAxis = horizon ? ay : ax
|
|
||||||
|
|
||||||
if (agValueAxisTag.length && (!agValueAxis || !agValueAxis.noAxisLine)) {
|
|
||||||
ctx.strokeStyle = (agValueAxis && agValueAxis.axisLineColor) || axisLineColor
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.moveTo(...(horizon ? axisAnglePos.leftTop : axisAnglePos.rightTop))
|
|
||||||
ctx.lineTo(...(horizon ? axisAnglePos.rightTop : axisAnglePos.rightBottom))
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
|
|
||||||
const agLebalAxis = horizon ? ax : ay
|
|
||||||
|
|
||||||
if (agLabelAxisTag.length && (!agLebalAxis || !agLebalAxis.noAxisLine)) {
|
|
||||||
ctx.strokeStyle = (agLebalAxis && agLebalAxis.axisLineColor) || axisLineColor
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.moveTo(...(horizon ? axisAnglePos.rightTop : axisAnglePos.leftTop))
|
|
||||||
ctx.lineTo(...(horizon ? axisAnglePos.rightBottom : axisAnglePos.rightTop))
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
drawAxisTag () {
|
|
||||||
const { noAxisTag } = this
|
|
||||||
|
|
||||||
if (noAxisTag) return
|
|
||||||
|
|
||||||
const { horizon, tagAlign, defaultAxisLineTagGap: offset } = this
|
|
||||||
|
|
||||||
const { data: { x, ax, y, ay }, drawAxisSeriesTag } = this
|
|
||||||
|
|
||||||
const xAxis = horizon ? ['addBAValueAxisTag', 'valueAxisTagPos'] : ['addBALabelAxisTag', 'labelAxisTagPos']
|
|
||||||
const yAxis = horizon ? ['addBALabelAxisTag', 'labelAxisTagPos'] : ['addBAValueAxisTag', 'valueAxisTagPos']
|
|
||||||
const agXAxis = horizon ? ['addBAAGValueAxisTag', 'agValueAxisTagPos'] : ['addBAAGLabelAxisTag', 'agLabelAxisTagPos']
|
|
||||||
const agYAxis = horizon ? ['addBAAGLabelAxisTag', 'agLabelAxisTagPos'] : ['addBAAGValueAxisTag', 'agValueAxisTagPos']
|
|
||||||
|
|
||||||
if (!x || !x.noAxisTag) drawAxisSeriesTag(...xAxis.map(td => this[td]), x, tagAlign.x, [0, offset], x && x.rotate)
|
|
||||||
if (!y || !y.noAxisTag) drawAxisSeriesTag(...yAxis.map(td => this[td]), y, tagAlign.y, [-offset, 0])
|
|
||||||
if (!ax || !ax.noAxisTag) drawAxisSeriesTag(...agXAxis.map(td => this[td]), ax, tagAlign.ax, [0, -offset])
|
|
||||||
if (!ay || !ay.noAxisTag) drawAxisSeriesTag(...agYAxis.map(td => this[td]), ay, tagAlign.ay, [offset, 0])
|
|
||||||
},
|
|
||||||
drawAxisSeriesTag (tags, tagPos, { fontSize, fontFamily, tagColor } = {}, align, offset, rotate = false) {
|
|
||||||
const { ctx, axisFontSize, axisFontFamily, axisTagColor, drawColors } = this
|
|
||||||
|
|
||||||
let color = tagColor || axisTagColor
|
|
||||||
|
|
||||||
color === 'colors' && (color = drawColors)
|
|
||||||
|
|
||||||
const colorNum = color.length
|
|
||||||
|
|
||||||
const mulColor = color instanceof Array
|
|
||||||
|
|
||||||
ctx.font = `${fontSize || axisFontSize}px ${fontFamily || axisFontFamily}`
|
|
||||||
|
|
||||||
!mulColor && (ctx.fillStyle = color)
|
|
||||||
|
|
||||||
ctx.textAlign = align[0]
|
|
||||||
ctx.textBaseline = align[1]
|
|
||||||
|
|
||||||
tags.forEach((tag, i) => {
|
|
||||||
if (!tag && tag !== 0) return
|
|
||||||
|
|
||||||
const currentPos = [tagPos[i][0] + offset[0], tagPos[i][1] + offset[1]]
|
|
||||||
|
|
||||||
mulColor && (ctx.fillStyle = color[i % colorNum])
|
|
||||||
|
|
||||||
if (rotate) {
|
|
||||||
ctx.textAlign = 'left'
|
|
||||||
ctx.textBaseline = 'top'
|
|
||||||
|
|
||||||
ctx.save()
|
|
||||||
ctx.translate(...currentPos)
|
|
||||||
ctx.rotate(rotate * Math.PI / 180)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.fillText(tag, ...(rotate ? [0, 0] : currentPos))
|
|
||||||
|
|
||||||
if (rotate) ctx.restore()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
drawAxisUnit () {
|
|
||||||
const { noAxisTag } = this
|
|
||||||
|
|
||||||
if (noAxisTag) return
|
|
||||||
|
|
||||||
const { axisOriginPos, canvasWH, drawUnit, defaultAxisLineTagGap } = this
|
|
||||||
|
|
||||||
const { data: { x, ax, y, ay }, axisAnglePos } = this
|
|
||||||
|
|
||||||
if (x) {
|
|
||||||
const pos = [canvasWH[0], axisOriginPos[1] + defaultAxisLineTagGap]
|
|
||||||
drawUnit(x, pos, ['right', 'top'])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ax) {
|
|
||||||
const pos = [canvasWH[0], axisAnglePos.rightTop[1] - defaultAxisLineTagGap]
|
|
||||||
drawUnit(ax, pos, ['right', 'bottom'])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y) {
|
|
||||||
const pos = [axisOriginPos[0] - defaultAxisLineTagGap, 0]
|
|
||||||
drawUnit(y, pos, ['right', 'top'])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ay) {
|
|
||||||
const pos = [axisAnglePos.rightTop[0] + defaultAxisLineTagGap, 0]
|
|
||||||
drawUnit(ay, pos, ['left', 'top'])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
drawUnit ({ unit, unitColor, fontSize, fontFamily }, pos, align) {
|
|
||||||
const { axisTagColor, axisFontSize, axisFontFamily } = this
|
|
||||||
|
|
||||||
const { ctx } = this
|
|
||||||
|
|
||||||
if (!unit) return
|
|
||||||
|
|
||||||
ctx.font = `${fontSize || axisFontSize}px ${fontFamily || axisFontFamily}`
|
|
||||||
|
|
||||||
ctx.fillStyle = unitColor || axisTagColor
|
|
||||||
|
|
||||||
ctx.textAlign = align[0]
|
|
||||||
ctx.textBaseline = align[1]
|
|
||||||
|
|
||||||
ctx.fillText(unit, ...pos)
|
|
||||||
},
|
|
||||||
drawAxisGrid () {
|
|
||||||
const { valueAxisTagPos, agValueAxisTagPos, labelAxisTagPos, agLabelAxisTagPos } = this
|
|
||||||
|
|
||||||
const { valueAxisTag, agValueAxisTag, labelAxisTag, agLabelAxisTag } = this
|
|
||||||
|
|
||||||
const { data: { x, ax, y, ay }, horizon, drawGrid, boundaryGap } = this
|
|
||||||
|
|
||||||
const xAxis = horizon ? [valueAxisTag, valueAxisTagPos] : [labelAxisTag, labelAxisTagPos]
|
|
||||||
|
|
||||||
let xBELineStatus = [false, false]
|
|
||||||
|
|
||||||
if (horizon) {
|
|
||||||
xBELineStatus = [true, false]
|
|
||||||
} else {
|
|
||||||
xBELineStatus = boundaryGap ? [false, false] : [true, true]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xAxis[0].length) drawGrid(x, ...xAxis, false, false, true, ...xBELineStatus)
|
|
||||||
|
|
||||||
const yAxis = horizon ? [labelAxisTag, labelAxisTagPos] : [valueAxisTag, valueAxisTagPos]
|
|
||||||
|
|
||||||
if (yAxis[0].length) drawGrid(y, ...yAxis, true, true, false, !horizon)
|
|
||||||
|
|
||||||
const agXAxis = horizon ? [agValueAxisTag, agValueAxisTagPos] : [agLabelAxisTag, agLabelAxisTagPos]
|
|
||||||
|
|
||||||
if (agXAxis[0].length) drawGrid(ax, ...agXAxis, false, false, false, ...(boundaryGap ? [false, false] : [true, true]))
|
|
||||||
|
|
||||||
const agYAxis = horizon ? [agLabelAxisTag, agLabelAxisTagPos] : [agValueAxisTag, agValueAxisTagPos]
|
|
||||||
|
|
||||||
if (agYAxis[0].length) drawGrid(ay, ...agYAxis, true, false, false, true)
|
|
||||||
},
|
|
||||||
drawGrid (axis = {}, gridTag, gridPos, horizon = true, right = true, top = true, noFirst = false, noLast = false) {
|
|
||||||
const { grid, gridLineColor: cGLC, gridLineDash: cGLD } = axis
|
|
||||||
|
|
||||||
if (!grid) return
|
|
||||||
|
|
||||||
const { gridLineColor, gridLineDash, defaultGridLineDash } = this
|
|
||||||
|
|
||||||
const { ctx, drawColors, axisWH } = this
|
|
||||||
|
|
||||||
let trueGridLineDash = gridLineDash
|
|
||||||
|
|
||||||
if (cGLD instanceof Array) {
|
|
||||||
trueGridLineDash = cGLD
|
|
||||||
} else if (cGLD === true) {
|
|
||||||
trueGridLineDash = defaultGridLineDash
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.setLineDash(trueGridLineDash)
|
|
||||||
|
|
||||||
let color = cGLC || gridLineColor
|
|
||||||
|
|
||||||
color === 'colors' && (color = drawColors)
|
|
||||||
|
|
||||||
const mulColor = color instanceof Array
|
|
||||||
|
|
||||||
const colorNum = color.length
|
|
||||||
|
|
||||||
!mulColor && (ctx.strokeStyle = color)
|
|
||||||
|
|
||||||
ctx.lineWidth = 1
|
|
||||||
|
|
||||||
const gridLastIndex = gridPos.length - 1
|
|
||||||
|
|
||||||
gridPos.forEach((pos, i) => {
|
|
||||||
if (!gridTag[i] && gridTag[i] !== 0) return
|
|
||||||
|
|
||||||
if (i === 0 && noFirst) return
|
|
||||||
|
|
||||||
if (i === gridLastIndex && noLast) return
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
mulColor && (ctx.strokeStyle = color[i % colorNum])
|
|
||||||
|
|
||||||
ctx.moveTo(...pos)
|
|
||||||
ctx.lineTo(...(horizon
|
|
||||||
? [right ? pos[0] + axisWH[0] : pos[0] - axisWH[0], pos[1]]
|
|
||||||
: [pos[0], top ? pos[1] - axisWH[1] : pos[1] + axisWH[1]]))
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
export default {
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
canvasDom: '',
|
|
||||||
canvasWH: [0, 0],
|
|
||||||
ctx: '',
|
|
||||||
centerPos: [0, 0]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
initCanvas () {
|
|
||||||
const { $nextTick } = this
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
|
||||||
$nextTick(e => {
|
|
||||||
const { $refs, ref, labelRef, canvasWH, centerPos } = 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')
|
|
||||||
|
|
||||||
centerPos[0] = canvasWH[0] / 2
|
|
||||||
centerPos[1] = canvasWH[1] / 2
|
|
||||||
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
clearCanvas () {
|
|
||||||
const { ctx, canvasWH } = this
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, ...canvasWH)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
export default {
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
defaultColors: [
|
|
||||||
'#9cf4a7', '#66d7ee', '#eee966',
|
|
||||||
'#a866ee', '#ee8f66', '#ee66aa'
|
|
||||||
],
|
|
||||||
|
|
||||||
drawColors: '',
|
|
||||||
|
|
||||||
drawColorsMul: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
initColors () {
|
|
||||||
const { colors, defaultColors } = this
|
|
||||||
|
|
||||||
const trueDrawColors = this.drawColors = colors || defaultColors
|
|
||||||
|
|
||||||
this.drawColorsMul = trueDrawColors instanceof Array
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,195 +0,0 @@
|
||||||
import { filterNull } from './methodsExtend'
|
|
||||||
|
|
||||||
export function drawLine (ctx, lineBegin, lineEnd, lineWidth = 2, lineColor = '#000', dashArray = [10, 0]) {
|
|
||||||
if (!ctx || !lineBegin || !lineEnd) return
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.moveTo(...lineBegin)
|
|
||||||
ctx.lineTo(...lineEnd)
|
|
||||||
|
|
||||||
ctx.closePath()
|
|
||||||
|
|
||||||
ctx.lineWidth = lineWidth
|
|
||||||
ctx.strokeStyle = lineColor
|
|
||||||
ctx.setLineDash(dashArray)
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function drawPolylinePath (ctx, points, close = false, newPath = false) {
|
|
||||||
if (!ctx || !points.length) return
|
|
||||||
|
|
||||||
newPath && ctx.beginPath()
|
|
||||||
|
|
||||||
points.forEach((point, i) =>
|
|
||||||
point && (i === 0 ? ctx.moveTo(...point) : ctx.lineTo(...point)))
|
|
||||||
|
|
||||||
close && ctx.lineTo(...points[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
export function drawPolyline (ctx, points, lineWidth = 2, lineColor = '#000', close = false, dashArray = [10, 0], newPath = false) {
|
|
||||||
if (!ctx || !points.length) return
|
|
||||||
|
|
||||||
drawPolylinePath(ctx, points, close, newPath)
|
|
||||||
|
|
||||||
ctx.lineWidth = lineWidth
|
|
||||||
ctx.strokeStyle = lineColor
|
|
||||||
ctx.setLineDash(dashArray)
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function drawSmoothlinePath (ctx, points, close = false, newPath = false, moveTo = false) {
|
|
||||||
const canDrawPoints = filterNull(points)
|
|
||||||
|
|
||||||
if (!ctx || canDrawPoints.length < 2) return
|
|
||||||
|
|
||||||
close && canDrawPoints.push(canDrawPoints[0])
|
|
||||||
|
|
||||||
newPath && ctx.beginPath()
|
|
||||||
|
|
||||||
if (canDrawPoints.length === 2) {
|
|
||||||
ctx.moveTo(...canDrawPoints[0])
|
|
||||||
ctx.lineTo(...canDrawPoints[1])
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastPointIndex = canDrawPoints.length - 1
|
|
||||||
|
|
||||||
moveTo && ctx.moveTo(...canDrawPoints[0])
|
|
||||||
|
|
||||||
canDrawPoints.forEach((t, i) =>
|
|
||||||
(i !== lastPointIndex) && drawBezierCurveLinePath(ctx,
|
|
||||||
...getBezierCurveLineControlPoints(canDrawPoints, i, false),
|
|
||||||
canDrawPoints[i + 1]))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getBezierCurveLineControlPoints (points, index, close = false, offsetA = 0.25, offsetB = 0.25) {
|
|
||||||
const pointNum = points.length
|
|
||||||
|
|
||||||
if (pointNum < 3 || index >= pointNum) return
|
|
||||||
|
|
||||||
let beforePointIndex = index - 1
|
|
||||||
beforePointIndex < 0 && (beforePointIndex = (close ? pointNum + beforePointIndex : 0))
|
|
||||||
|
|
||||||
let afterPointIndex = index + 1
|
|
||||||
afterPointIndex >= pointNum && (afterPointIndex = (close ? afterPointIndex - pointNum : pointNum - 1))
|
|
||||||
|
|
||||||
let afterNextPointIndex = index + 2
|
|
||||||
afterNextPointIndex >= pointNum && (afterNextPointIndex = (close ? afterNextPointIndex - pointNum : pointNum - 1))
|
|
||||||
|
|
||||||
const pointBefore = points[beforePointIndex]
|
|
||||||
const pointMiddle = points[index]
|
|
||||||
const pointAfter = points[afterPointIndex]
|
|
||||||
const pointAfterNext = points[afterNextPointIndex]
|
|
||||||
|
|
||||||
return [
|
|
||||||
[
|
|
||||||
pointMiddle[0] + offsetA * (pointAfter[0] - pointBefore[0]),
|
|
||||||
pointMiddle[1] + offsetA * (pointAfter[1] - pointBefore[1])
|
|
||||||
],
|
|
||||||
[
|
|
||||||
pointAfter[0] - offsetB * (pointAfterNext[0] - pointMiddle[0]),
|
|
||||||
pointAfter[1] - offsetB * (pointAfterNext[1] - pointMiddle[1])
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function drawSmoothline (ctx, points, lineWidth = 2, lineColor = '#000', close = false, dashArray = [10, 0], newPath = false, moveTo = false) {
|
|
||||||
if (!ctx || points.length < 3) return
|
|
||||||
|
|
||||||
drawSmoothlinePath(ctx, points, close, newPath, moveTo)
|
|
||||||
|
|
||||||
ctx.lineWidth = lineWidth
|
|
||||||
ctx.strokeStyle = lineColor
|
|
||||||
ctx.setLineDash(dashArray)
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function drawBezierCurveLinePath (ctx, ctlBefore, ctlAfter, end, newPath = false) {
|
|
||||||
if (!ctx || !ctlBefore || !ctlAfter || !end) return
|
|
||||||
|
|
||||||
newPath && ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.bezierCurveTo(...ctlBefore, ...ctlAfter, ...end)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function drawPoints (ctx, points, radius = 10, color = '#000') {
|
|
||||||
points.forEach(point => {
|
|
||||||
if (!point) return
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
ctx.arc(...point, radius, 0, Math.PI * 2)
|
|
||||||
|
|
||||||
ctx.fillStyle = color
|
|
||||||
|
|
||||||
ctx.fill()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLinearGradientColor (ctx, begin, end, color) {
|
|
||||||
if (!ctx || !begin || !end || !color.length) return
|
|
||||||
|
|
||||||
let colors = color
|
|
||||||
|
|
||||||
typeof colors === 'string' && (colors = [color, color])
|
|
||||||
|
|
||||||
const linearGradientColor = ctx.createLinearGradient(...begin, ...end)
|
|
||||||
|
|
||||||
const colorGap = 1 / (colors.length - 1)
|
|
||||||
|
|
||||||
colors.forEach((c, i) => linearGradientColor.addColorStop(colorGap * i, c))
|
|
||||||
|
|
||||||
return linearGradientColor
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRadialGradientColor (ctx, origin, begin = 0, end = 100, color) {
|
|
||||||
if (!ctx || !origin || !color.length) return
|
|
||||||
|
|
||||||
let colors = color
|
|
||||||
|
|
||||||
typeof colors === 'string' && (colors = [color, color])
|
|
||||||
|
|
||||||
const radialGradientColor = ctx.createRadialGradient(...origin, begin, ...origin, end)
|
|
||||||
|
|
||||||
const colorGap = 1 / (colors.length - 1)
|
|
||||||
|
|
||||||
colors.forEach((c, i) => radialGradientColor.addColorStop(colorGap * i, c))
|
|
||||||
|
|
||||||
return radialGradientColor
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCircleRadianPoint (x, y, radius, radian) {
|
|
||||||
const { sin, cos } = Math
|
|
||||||
|
|
||||||
return [x + cos(radian) * radius, y + sin(radian) * radius]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getTextsWidth (ctx, texts) {
|
|
||||||
if (!ctx || !texts) return
|
|
||||||
|
|
||||||
return texts.map(text => ctx.measureText(text).width)
|
|
||||||
}
|
|
||||||
|
|
||||||
const canvas = {
|
|
||||||
drawLine,
|
|
||||||
drawPolylinePath,
|
|
||||||
drawPolyline,
|
|
||||||
getBezierCurveLineControlPoints,
|
|
||||||
drawSmoothlinePath,
|
|
||||||
drawSmoothline,
|
|
||||||
drawBezierCurveLinePath,
|
|
||||||
drawPoints,
|
|
||||||
getLinearGradientColor,
|
|
||||||
getRadialGradientColor,
|
|
||||||
getCircleRadianPoint,
|
|
||||||
getTextsWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (Vue) {
|
|
||||||
Vue.prototype.canvas = canvas
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
const hexReg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
|
|
||||||
const rgbReg = /^(rgb|rgba|RGB|RGBA)/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description hex color to rgb / rgba color
|
|
||||||
* @param {string} rgba opacity
|
|
||||||
* @return {string} rgb / rgba color
|
|
||||||
*/
|
|
||||||
export function hexToRgb (hex, opacity) {
|
|
||||||
if (!hex || !hexReg.test(hex)) return false
|
|
||||||
|
|
||||||
hex = hex.toLowerCase().replace('#', '')
|
|
||||||
|
|
||||||
// deal 3bit hex color to 6bit hex color
|
|
||||||
hex.length === 3 && (hex = Array.from(hex).map(hexNum => hexNum + hexNum).join(''))
|
|
||||||
|
|
||||||
let rgb = []
|
|
||||||
|
|
||||||
for (let i = 0; i < 6; i += 2) {
|
|
||||||
rgb.push(parseInt(`0x${hex.slice(i, i + 2)}`))
|
|
||||||
}
|
|
||||||
|
|
||||||
rgb = rgb.join(',')
|
|
||||||
|
|
||||||
if (opacity) {
|
|
||||||
return `rgba(${rgb}, ${opacity})`
|
|
||||||
} else {
|
|
||||||
return `rgb(${rgb})`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description rgb / rgba color to hex color
|
|
||||||
* @return {string} hex color
|
|
||||||
*/
|
|
||||||
export function rgbToHex (rgb) {
|
|
||||||
if (!rgb || !rgbReg.test(rgb)) return false
|
|
||||||
|
|
||||||
rgb = rgb.toLowerCase().replace(/rgb\(|rgba\(|\)/g, '').split(',').slice(0, 3)
|
|
||||||
|
|
||||||
const hex = rgb.map((rgbNum) => Number(rgbNum).toString(16)).map((rgbNum) => rgbNum === '0' ? rgbNum + rgbNum : rgbNum).join('')
|
|
||||||
|
|
||||||
return `#${hex}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const color = {
|
|
||||||
hexToRgb,
|
|
||||||
rgbToHex
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (Vue) {
|
|
||||||
Vue.prototype.color = color
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
import methodsExtend from './methodsExtend'
|
|
||||||
|
|
||||||
import canvasExtend from './canvasExtend'
|
|
||||||
|
|
||||||
import colorExtend from './colorExtend'
|
|
||||||
|
|
||||||
export default function (Vue) {
|
|
||||||
methodsExtend(Vue)
|
|
||||||
canvasExtend(Vue)
|
|
||||||
colorExtend(Vue)
|
|
||||||
}
|
|
|
@ -1,155 +0,0 @@
|
||||||
const { parse, stringify } = JSON
|
|
||||||
|
|
||||||
export function deepClone (object) {
|
|
||||||
return parse(stringify(object))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteArrayAllItems (arrays) {
|
|
||||||
arrays.forEach(element => element.splice(0, element.length))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function debounce (delay, callback) {
|
|
||||||
let lastTime
|
|
||||||
|
|
||||||
return function () {
|
|
||||||
clearTimeout(lastTime)
|
|
||||||
|
|
||||||
const [that, args] = [this, arguments]
|
|
||||||
|
|
||||||
lastTime = setTimeout(() => {
|
|
||||||
callback.apply(that, args)
|
|
||||||
}, delay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function randomExtend (minNum, maxNum) {
|
|
||||||
if (arguments.length === 1) {
|
|
||||||
return parseInt(Math.random() * minNum + 1, 10)
|
|
||||||
} else {
|
|
||||||
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function multipleSum (...num) {
|
|
||||||
let sum = 0
|
|
||||||
|
|
||||||
num.forEach(n => (sum += n))
|
|
||||||
|
|
||||||
return sum
|
|
||||||
}
|
|
||||||
|
|
||||||
export function filterNull (arr) {
|
|
||||||
const tmpArr = []
|
|
||||||
|
|
||||||
arr.forEach(v => ((v || v === 0) && tmpArr.push(v)))
|
|
||||||
|
|
||||||
return tmpArr
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPointDistance (pointOne, pointTwo) {
|
|
||||||
const minusX = Math.abs(pointOne[0] - pointTwo[0])
|
|
||||||
|
|
||||||
const minusY = Math.abs(pointOne[1] - pointTwo[1])
|
|
||||||
|
|
||||||
return Math.sqrt(minusX * minusX + minusY * minusY)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPointToLineDistance (point, linePointOne, linePointTwo) {
|
|
||||||
const a = getPointDistance(point, linePointOne)
|
|
||||||
const b = getPointDistance(point, linePointTwo)
|
|
||||||
const c = getPointDistance(linePointOne, linePointTwo)
|
|
||||||
|
|
||||||
return 0.5 * Math.sqrt((a + b + c) * (a + b - c) * (a + c - b) * (b + c - a)) / c
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getArrayMaxMin (array) {
|
|
||||||
if (!array) return false
|
|
||||||
|
|
||||||
return [getArrayMax(array), getArrayMin(array)]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getArrayMax (array) {
|
|
||||||
if (!array) return false
|
|
||||||
|
|
||||||
return Math.max(...filterNull(array).map(n =>
|
|
||||||
n instanceof Array ? getArrayMax(n) : n))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getArrayMin (array) {
|
|
||||||
if (!array) return false
|
|
||||||
|
|
||||||
return Math.min(...filterNull(array).map(n =>
|
|
||||||
n instanceof Array ? getArrayMin(n) : n))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAxisPointsPos ([max, min], values, axisOriginPos, axisWH, tagPos, horizon) {
|
|
||||||
let minus = max - min
|
|
||||||
|
|
||||||
minus === 0 && (minus = 1)
|
|
||||||
|
|
||||||
return values.map((value, i) => {
|
|
||||||
if (!value && value !== 0) return false
|
|
||||||
|
|
||||||
if (value instanceof Array) {
|
|
||||||
return value.map(v =>
|
|
||||||
getAxisPointPos([max, min], v, axisOriginPos, axisWH, tagPos[i], horizon))
|
|
||||||
}
|
|
||||||
|
|
||||||
const percent = (value - min) / minus
|
|
||||||
|
|
||||||
const length = percent * (horizon ? axisWH[0] : axisWH[1])
|
|
||||||
|
|
||||||
return horizon ? [
|
|
||||||
axisOriginPos[0] + length,
|
|
||||||
tagPos[i][1]
|
|
||||||
] : [
|
|
||||||
tagPos[i][0],
|
|
||||||
axisOriginPos[1] - length
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAxisPointPos ([max, min], value, axisOriginPos, axisWH, tagPos, horizon) {
|
|
||||||
if (!value && value !== 0) return false
|
|
||||||
|
|
||||||
const minus = max - min
|
|
||||||
|
|
||||||
const percent = (value - min) / minus
|
|
||||||
|
|
||||||
const length = percent * (horizon ? axisWH[0] : axisWH[1])
|
|
||||||
|
|
||||||
return horizon ? [
|
|
||||||
axisOriginPos[0] + length,
|
|
||||||
tagPos[1]
|
|
||||||
] : [
|
|
||||||
tagPos[0],
|
|
||||||
axisOriginPos[1] - length
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function observerDomResize (dom, callback) {
|
|
||||||
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
|
|
||||||
|
|
||||||
const observer = new MutationObserver(callback)
|
|
||||||
|
|
||||||
observer.observe(dom, { attributes: true, attributeFilter: ['style'], attributeOldValue: true })
|
|
||||||
|
|
||||||
return observer
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (Vue) {
|
|
||||||
Vue.prototype.deepClone = deepClone
|
|
||||||
Vue.prototype.deleteArrayAllItems = deleteArrayAllItems
|
|
||||||
Vue.prototype.debounce = debounce
|
|
||||||
Vue.prototype.multipleSum = multipleSum
|
|
||||||
Vue.prototype.randomExtend = randomExtend
|
|
||||||
Vue.prototype.filterNull = filterNull
|
|
||||||
Vue.prototype.getPointDistance = getPointDistance
|
|
||||||
Vue.prototype.getPointToLineDistance = getPointToLineDistance
|
|
||||||
Vue.prototype.getArrayMaxMin = getArrayMaxMin
|
|
||||||
Vue.prototype.getArrayMax = getArrayMax
|
|
||||||
Vue.prototype.getArrayMin = getArrayMin
|
|
||||||
Vue.prototype.getAxisPointPos = getAxisPointPos
|
|
||||||
Vue.prototype.getAxisPointsPos = getAxisPointsPos
|
|
||||||
Vue.prototype.observerDomResize = observerDomResize
|
|
||||||
}
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export function randomExtend (minNum, maxNum) {
|
||||||
|
if (arguments.length === 1) {
|
||||||
|
return parseInt(Math.random() * minNum + 1, 10)
|
||||||
|
} else {
|
||||||
|
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10)
|
||||||
|
}
|
||||||
|
}
|