在Vue中用SVG画一个油罐

 效果如下

这是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>

相关推荐

  1. 如何 vue 项目创建 svg 组件

    2024-07-16 03:40:03       52 阅读
  2. html一个烟花特效

    2024-07-16 03:40:03       26 阅读
  3. 如何CSS3一个三角形?

    2024-07-16 03:40:03       24 阅读
  4. vue如何一个数组减去另一个数组

    2024-07-16 03:40:03       34 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-16 03:40:03       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-16 03:40:03       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-16 03:40:03       58 阅读
  4. Python语言-面向对象

    2024-07-16 03:40:03       69 阅读

热门阅读

  1. 玩转springboot之SpringBoot使用jsp

    2024-07-16 03:40:03       20 阅读
  2. 神经网络调参技巧(入门案例教程)

    2024-07-16 03:40:03       20 阅读
  3. 双缓存机制

    2024-07-16 03:40:03       16 阅读
  4. CNN -1 神经网络-概述

    2024-07-16 03:40:03       19 阅读
  5. 输入两个整数,输出最大公约数与最小公倍数。

    2024-07-16 03:40:03       18 阅读