DemuMesDataV/components/waterLevelPond/index.vue

354 lines
7.3 KiB
Vue
Raw Permalink Normal View History

2018-12-17 16:49:09 +08:00
<template>
2019-06-25 19:57:04 +08:00
<div class="dv-water-pond-level">
<svg v-if="render">
2018-12-17 16:49:09 +08:00
<defs>
2019-06-25 19:57:04 +08:00
<linearGradient :id="gradientId" x1="0%" y1="0%" x2="0%" y2="100%">
<stop v-for="lc in svgBorderGradient" :key="lc[0]"
2018-12-17 16:49:09 +08:00
:offset="lc[0]"
:stop-color="lc[1]" />
</linearGradient>
</defs>
2019-06-25 19:57:04 +08:00
<text
v-if="render"
:stroke="`url(#${gradientId})`"
:fill="`url(#${gradientId})`"
:x="render.area[0] / 2 + 8"
:y="render.area[1] / 2 + 8"
>
{{ details }}
2018-12-17 16:49:09 +08:00
</text>
2019-06-25 19:57:04 +08:00
<ellipse v-if="!shape || shape === 'round'"
:cx="render.area[0] / 2 + 8"
:cy="render.area[1] / 2 + 8"
:rx="render.area[0] / 2 + 5"
:ry="render.area[1] / 2 + 5"
:stroke="`url(#${gradientId})`" />
2018-12-17 16:49:09 +08:00
<rect v-else
x="2" y="2"
2019-06-25 19:57:04 +08:00
:rx="shape === 'roundRect' ? 10 : 0"
:ry="shape === 'roundRect' ? 10 : 0"
:width="render.area[0] + 12"
:height="render.area[1] + 12"
:stroke="`url(#${gradientId})`" />
2018-12-17 16:49:09 +08:00
</svg>
2019-06-25 19:57:04 +08:00
<canvas ref="water-pond-level" :style="`border-radius: ${radius};`" />
2018-12-17 16:49:09 +08:00
</div>
</template>
<script>
2019-06-25 19:57:04 +08:00
import { deepMerge } from '@jiaminghi/charts/lib/util/index'
import { deepClone } from '@jiaminghi/c-render/lib/plugin/util'
import CRender from '@jiaminghi/c-render'
2019-01-16 16:56:11 +08:00
2018-12-17 16:49:09 +08:00
export default {
2019-06-25 19:57:04 +08:00
name: 'waterLevelPond',
props: {
config: Object,
default: () => ({})
},
2018-12-17 16:49:09 +08:00
data () {
return {
2019-06-25 19:57:04 +08:00
gradientId: `water-level-pond-${(new Date()).getTime()}`,
defaultConfig: {
/**
* @description Data
* @type {Array<Number>}
* @default data = []
* @example data = [60, 40]
*/
data: [],
/**
* @description Shape of wanter level pond
* @type {String}
* @default shape = 'rect'
* @example shape = 'rect' | 'roundRect' | 'round'
*/
shape: 'rect',
/**
* @description Water wave number
* @type {Number}
* @default waveNum = 3
*/
waveNum: 3,
/**
* @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}%'
},
mergedConfig: {},
render: null,
svgBorderGradient: [],
details: '',
waves: [],
animation: false
}
},
computed: {
radius () {
const { shape } = this.mergedConfig
2019-01-16 16:56:11 +08:00
2019-06-25 19:57:04 +08:00
if (shape === 'round') return '50%'
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
if (shape === 'rect') return '0'
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
if (shape === 'roundRect') return '10px'
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
return '0'
},
shape () {
const { shape } = this.mergedConfig
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
if (!shape) return 'rect'
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
return shape
2018-12-17 16:49:09 +08:00
}
},
2019-01-16 16:56:11 +08:00
watch: {
2019-06-25 19:57:04 +08:00
config () {
const { calcData, render } = this
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
render.delAllGraph()
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
this.waves = []
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
setTimeout(calcData, 0)
2018-12-17 16:49:09 +08:00
}
},
methods: {
2019-06-25 19:57:04 +08:00
init () {
const { initRender, config, calcData } = this
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
initRender()
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
if (!config) return
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
calcData()
2018-12-17 16:49:09 +08:00
},
2019-06-25 19:57:04 +08:00
initRender () {
const { $refs } = this
2019-01-16 16:56:11 +08:00
2019-06-25 19:57:04 +08:00
this.render = new CRender($refs['water-pond-level'])
},
calcData () {
const { mergeConfig, calcSvgBorderGradient, calcDetails } = this
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
mergeConfig()
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
calcSvgBorderGradient()
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
calcDetails()
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
const { addWave, animationWave } = this
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
addWave()
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
animationWave()
2018-12-17 16:49:09 +08:00
},
2019-06-25 19:57:04 +08:00
mergeConfig () {
const { config, defaultConfig } = this
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
this.mergedConfig = deepMerge(deepClone(defaultConfig, true), config)
2018-12-17 16:49:09 +08:00
},
2019-06-25 19:57:04 +08:00
calcSvgBorderGradient () {
const { colors } = this.mergedConfig
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
const colorNum = colors.length
2018-12-17 16:49:09 +08:00
const colorOffsetGap = 100 / (colorNum - 1)
2019-06-25 19:57:04 +08:00
this.svgBorderGradient = colors.map((c, i) => [colorOffsetGap * i, c])
2018-12-17 16:49:09 +08:00
},
2019-06-25 19:57:04 +08:00
calcDetails () {
const { data, formatter } = this.mergedConfig
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
if (!data.length) {
this.details = ''
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
return
}
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
const maxValue = Math.max(...data)
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
this.details = formatter.replace('{value}', maxValue)
},
addWave () {
const { render, getWaveShapes, getWaveStyle, drawed } = this
const shapes = getWaveShapes()
const style = getWaveStyle()
this.waves = shapes.map(shape => render.add({
name: 'smoothline',
animationFrame: 300,
shape,
style,
drawed
}))
2018-12-17 16:49:09 +08:00
},
2019-06-25 19:57:04 +08:00
getWaveShapes () {
const { mergedConfig, render, mergeOffset } = this
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
const { waveNum, waveHeight, data } = mergedConfig
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
const [w, h] = render.area
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
const pointsNum = waveNum * 4 + 4
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
const pointXGap = w / waveNum / 2
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
return data.map(v => {
let points = new Array(pointsNum).fill(0).map((foo, j) => {
const x = w - pointXGap * j
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
const startY = (1 - v / 100) * h
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
const y = j % 2 === 0 ? startY : startY - waveHeight
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
return [x, y]
})
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
points = points.map(p => mergeOffset(p, [pointXGap * 2, 0]))
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
return { points }
})
2018-12-17 16:49:09 +08:00
},
2019-06-25 19:57:04 +08:00
mergeOffset ([x, y], [ox, oy]) {
return [x + ox, y + oy]
2018-12-17 16:49:09 +08:00
},
2019-06-25 19:57:04 +08:00
getWaveStyle () {
const { render, mergedConfig } = this
const h = render.area[1]
return {
gradientColor: mergedConfig.colors,
gradientType: 'linear',
gradientParams: [0, 0, 0, h],
gradientWith: 'fill',
opacity: mergedConfig.waveOpacity,
translate: [0, 0]
}
},
drawed ({ shape: { points } }, { ctx, area }) {
const firstPoint = points[0]
const lastPoint = points.slice(-1)[0]
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
const [w, h] = area
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
ctx.lineTo(lastPoint[0], h)
ctx.lineTo(firstPoint[0], h)
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
ctx.closePath()
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
ctx.fill()
},
async animationWave (repeat = 1) {
const { waves, render, animation } = this
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
if (animation) return
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
this.animation = true
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
const w = render.area[0]
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
waves.forEach(graph => {
const reset = repeat % 2 === 0
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
graph.attr('style', { translate: [0, 0] })
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
graph.animation('style', {
translate: [w, 0]
}, true)
2018-12-17 16:49:09 +08:00
})
2019-06-25 19:57:04 +08:00
await render.launchAnimation()
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
this.animation = false
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
if (!render.graphs.length) return
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
this.animationWave(repeat + 1)
2018-12-17 16:49:09 +08:00
}
},
mounted () {
const { init } = this
init()
},
2019-06-25 19:57:04 +08:00
beforeDestroy () {
const { calcData, render } = this
render.delAllGraph()
2018-12-17 16:49:09 +08:00
2019-06-25 19:57:04 +08:00
this.waves = []
2018-12-17 16:49:09 +08:00
}
}
</script>
<style lang="less">
2019-06-25 19:57:04 +08:00
.dv-water-pond-level {
2018-12-17 16:49:09 +08:00
position: relative;
2019-06-25 19:57:04 +08:00
svg {
2018-12-17 16:49:09 +08:00
position: absolute;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
}
text {
font-size: 25px;
font-weight: bold;
text-anchor: middle;
dominant-baseline: middle;
}
ellipse, rect {
fill: none;
stroke-width: 3;
}
canvas {
margin-top: 8px;
margin-left: 8px;
width: calc(~"100% - 16px");
height: calc(~"100% - 16px");
box-sizing: border-box;
}
}
</style>