效果如下
这是3年前做的一个数字大屏项目,如今接到个活,可能又要用到SVG了,所以把之前的代码找出来回顾一下,都快忘了怎么玩这个东西了,核心用的是 snapsvg 这个前端库
<template>
<div>
<svg ref='tank' class="tank" :height="tankHeight + 'px'" :width="tankWidth + 'px'"></svg>
</div>
</template>
<script>
import Snap from 'snapsvg-cjs'
export default {
name: 'Tank',
props: {
// 罐宽
tankWidth: {
type: Number,
default: 0
},
// 罐高
tankHeight: {
type: Number,
default: 0
},
// 油液位椭圆水平面上显示的内容
title: {
type: String,
default: ''
},
// 油的吨数跟最大罐容的占比
percent: {
type: Number,
default: 0
},
// 椭圆的Y轴半径
ellipseRadius: {
type: Number,
default: 15
},
// 罐身渐变起始色
startColor: {
type: String,
default: ''
},
// 罐身渐变终止色
endColor: {
type: String,
default: ''
},
// 栏杆颜色
railingColor: {
type: String,
default: 'white'
}
},
watch: {
percent: {
handler (newV, oldV) {
if (newV !== oldV) {
this.$nextTick(() => {
if (this.$refs.tank) {
this.drowTank(this.$refs.tank)
}
})
}
}
}
},
mounted () {
this.$nextTick(() => {
if (this.$refs.tank) {
this.drowTank(this.$refs.tank)
}
})
},
data() {
return {
railingHeight : this.ellipseRadius * 2
}
},
methods: {
// 绘制油罐
drowTank (tank) {
tank.innerHtml = ''
let svg = Snap(tank)
svg.paper.clear()
// 栏杆粗细
let railingWidth = 1
// 栏杆间隔
let intervalWidth = this.ellipseRadius / 4
let intervalHeight = intervalWidth * 4
// 定义线性渐变
svg.paper.path(`M 0 ${this.ellipseRadius} A ${this.tankWidth / 2} ${this.ellipseRadius - railingWidth} 1 1 1 ${this.tankWidth} ${this.ellipseRadius - railingWidth}`).attr({
fill: 'none',
stroke: this.railingColor,
strokeLinecap: 'round',
strokeWidth: railingWidth,
})
// 画圆弧栏杆
svg.paper.path(`M 0 ${this.ellipseRadius} A ${this.tankWidth / 2} ${this.ellipseRadius - railingWidth} 1 1 1 ${this.tankWidth} ${this.ellipseRadius - railingWidth}`).attr({
fill: 'none',
stroke: this.railingColor,
strokeLinecap: 'round',
strokeWidth: railingWidth,
})
// 画圆弧栏杆
svg.paper.path(`M 0 ${this.ellipseRadius + intervalWidth} A ${this.tankWidth / 2} ${this.ellipseRadius - railingWidth} 1 1 1 ${this.tankWidth} ${this.ellipseRadius + intervalWidth - railingWidth}`).attr({
fill: 'none',
stroke: this.railingColor,
strokeLinecap: 'round',
strokeWidth: railingWidth,
})
// 画圆弧栏杆
svg.paper.path(`M 0 ${this.ellipseRadius + 2*intervalWidth} A ${this.tankWidth / 2} ${this.ellipseRadius - railingWidth} 1 1 1 ${this.tankWidth} ${this.ellipseRadius + 2*intervalWidth - railingWidth}`).attr({
fill: 'none',
stroke: this.railingColor,
strokeLinecap: 'round',
strokeWidth: railingWidth,
})
// 画圆弧栏杆
svg.paper.path(`M 0 ${this.ellipseRadius + 3*intervalWidth} A ${this.tankWidth / 2} ${this.ellipseRadius - railingWidth} 1 1 1 ${this.tankWidth} ${this.ellipseRadius + 3*intervalWidth - railingWidth}`).attr({
fill: 'none',
stroke: this.railingColor,
strokeLinecap: 'round',
strokeWidth: railingWidth,
})
// 画圆弧栏杆
svg.paper.path(`M 0 ${this.ellipseRadius + 4*intervalWidth} A ${this.tankWidth / 2} ${this.ellipseRadius - railingWidth} 1 1 1 ${this.tankWidth} ${this.ellipseRadius + 4*intervalWidth - railingWidth}`).attr({
fill: 'none',
stroke: this.railingColor,
strokeLinecap: 'round',
strokeWidth: railingWidth,
})
// 画垂直直线栏杆
svg.paper.line(0, this.ellipseRadius, 0, 4*intervalWidth).attr({
stroke: this.railingColor,
strokeWidth: railingWidth,
})
// 画垂直直线栏杆
svg.paper.line(this.tankWidth / 4, this.ellipseRadius / 4, this.tankWidth / 4, 4*intervalWidth).attr({
stroke: this.railingColor,
strokeWidth: railingWidth,
})
// 画垂直直线栏杆
svg.paper.line(this.tankWidth / 2, 0, this.tankWidth / 2, 4*intervalWidth).attr({
stroke: this.railingColor,
strokeWidth: railingWidth,
})
// 画垂直直线栏杆
svg.paper.line(this.tankWidth * (3/4), this.ellipseRadius / 4, this.tankWidth * (3/4), 4*intervalWidth).attr({
stroke: this.railingColor,
strokeWidth: railingWidth,
})
// 画垂直直线栏杆
svg.paper.line(this.tankWidth, this.ellipseRadius, this.tankWidth, 4*intervalWidth).attr({
stroke: this.railingColor,
strokeWidth: railingWidth,
})
// 定义线性渐变
let bodyGradient = svg.paper.gradient(`l(5%, 0%, 90%, 0%)${this.startColor}-${this.endColor}-${this.startColor}`)
// 罐顶一半椭圆
svg.paper.path(`M 0 ${this.ellipseRadius + intervalHeight} A ${this.tankWidth / 2} ${this.ellipseRadius} 1 1 1 ${this.tankWidth} ${this.ellipseRadius + intervalHeight}`).attr({
fill: bodyGradient,
fillOpacity: '0.8'
})
// 罐身矩形线性渐变
svg.paper.rect(0, this.ellipseRadius + intervalHeight, this.tankWidth, this.tankHeight - 2 * this.ellipseRadius - intervalHeight).attr({
fill: bodyGradient,
fillOpacity: '0.8'
})
// 罐底半个椭圆
svg.paper.path(`M 0 ${this.tankHeight - this.ellipseRadius} A ${this.tankWidth / 2} ${this.ellipseRadius} 1 1 0 ${this.tankWidth} ${this.tankHeight - this.ellipseRadius}`).attr({
fill: bodyGradient,
fillOpacity: '0.8'
})
// 如果有油的情况,画出油液位
if (this.percent > 0) {
// 底部油的半个椭圆
svg.paper.path(`M 0 ${this.tankHeight - this.ellipseRadius} A ${this.tankWidth / 2} ${this.ellipseRadius} 1 1 0 ${this.tankWidth} ${this.tankHeight - this.ellipseRadius}`).attr({
fill: '#F4D700',
fillOpacity: '0.8'
})
// 油的液位起始点
let oilStartY = (this.tankHeight - 2 * this.ellipseRadius) * (1 - this.percent) + this.ellipseRadius + intervalHeight
// 矫正最小值
if(((this.tankHeight - 2 * this.ellipseRadius) * this.percent - intervalHeight) < 0)
{
oilStartY = this.tankHeight - intervalHeight
}
// 油的液位高度矩形
svg.paper.rect(0, oilStartY, this.tankWidth, (this.tankHeight - 2 * this.ellipseRadius) * this.percent - intervalHeight).attr({
fill: '#F4D700',
fillOpacity: '0.8'
})
// 顶部油的椭圆
svg.paper.ellipse(this.tankWidth / 2, oilStartY, this.tankWidth / 2, this.ellipseRadius).attr({
fill: '#F4D700',
stroke: '#ffffff',
strokeWidth: '0.5px'
})
// 油的液位椭圆上显示的文字
let text = svg.text(this.tankWidth / 2, oilStartY, this.title).attr({
stroke: '#faf7ec',
strokeOpacity: '.4',
dominantBaseline: 'middle',
fontSize: this.ellipseRadius
})
// 文本居中
let xPosition = this.tankWidth / 2 - text.getBBox().width / 2
if (xPosition < 0) {
text.attr('x', this.tankWidth / 2)
} else {
text.attr('x', xPosition)
}
}
}
}
}
</script>
<style lang='less' scoped>
.tank {
padding: 0px !important;
margin: 0px !important;
}
</style>