SVG 實(shí)現(xiàn)半環(huán)形進(jìn)度條

要實(shí)現(xiàn)的效果圖如下

image.png

svg 語(yǔ)法學(xué)習(xí)

可以參考https://developer.mozilla.org/zh-CN/docs/Web/SVG網(wǎng)站上的語(yǔ)法

元素參考

<path>

path元素用來(lái)定義形狀的通用元素。
下面的命令可用于路徑數(shù)據(jù):

  • M = moveto
  • L = lineto
  • H = horizontal lineto
  • V = vertical lineto
  • C = curveto
  • S = smooth curveto
  • Q = quadratic Belzier curve
  • T = smooth quadratic Belzier curveto
  • A = elliptical Arc
  • Z = closepath
注釋:以上所有命令均允許小寫(xiě)字母。大寫(xiě)表示絕對(duì)定位,小寫(xiě)表示相對(duì)定位。

例如:畫(huà)個(gè)圓環(huán)

<path d="
        M 50 50
        m 0 -47
        a 47 47 0 1 1 0 94
        a 47 47 0 1 1 0 -94
        " stroke="#e5e9f2" stroke-width="4.8" fill="none" ></path>

解析:


image.png

<symbol>

symbol元素用來(lái)定義一個(gè)圖形模板對(duì)象,它可以用一個(gè)<use>元素實(shí)例化。symbol元素對(duì)圖形的作用是在同一文檔中多次使用,添加結(jié)構(gòu)和語(yǔ)義。結(jié)構(gòu)豐富的文檔可以更生動(dòng)地呈現(xiàn)出來(lái),類似講演稿或盲文,從而提升了可訪問(wèn)性。注意,一個(gè)symbol元素本身是不呈現(xiàn)的。只有symbol元素的實(shí)例(亦即,一個(gè)引用了symbol<use>元素)才能呈現(xiàn)。

<use>

use元素在SVG文檔內(nèi)取得目標(biāo)節(jié)點(diǎn),并在別的地方復(fù)制它們。它的效果等同于這些節(jié)點(diǎn)被深克隆到一個(gè)不可見(jiàn)的DOM中,然后將其粘貼到use元素的位置,很像HTML5中的克隆模板元素。因?yàn)榭寺〉墓?jié)點(diǎn)是不可見(jiàn)的,所以當(dāng)使用CSS樣式化一個(gè)use元素以及它的隱藏的后代元素的時(shí)候,必須小心注意。隱藏的、克隆的DOM不能保證繼承CSS屬性,除非你明文設(shè)置使用[CSS繼承]
(https://developer.mozilla.org/en/CSS/inheritance)。
一般情況下symbol 和use配合著使用的。
例如: 效果中的小人,如果自己畫(huà)的話,就非常耗時(shí)間,可以在iconfont 里找一個(gè),然后引入進(jìn)來(lái);
在自己的svg 里引入

<use xlink:href="#male" x="180" y="135"/>
<use xlink:href="#female" x="230" y="135"/>

在阿里圖標(biāo)庫(kù)復(fù)制svg,如下:兩個(gè)小人

<svg class="icon" >
      <symbol id="male" viewBox="0 0 1024 1024" width="80" height="80" fill="#23C8F5">
        <path d="M631.091 661.333l-13.79 319.932A44.988 44.988 0 0 1 572.416 1024l-64.717-42.667L443.017 1024a44.988 44.988 0 0 1-44.852-42.735l-13.79-319.932h-27.682a45.807 45.807 0 0 1-45.397-42.564l-38.161-277.538a40.073 40.073 0 0 1 40.55-42.564h388.096c23.723 0 41.882 19.046 40.55 42.564l-38.126 277.538a45.807 45.807 0 0 1-45.398 42.564h-27.716zM507.733 256a128 128 0 1 1 0-256 128 128 0 0 1 0 256z"></path>
      </symbol>
      <symbol id="female" viewBox="0 0 1024 1024" width="80" height="80" fill="#f4ea2a">
        <path d="M512 160m-160 0a160 160 0 1 0 320 0 160 160 0 1 0-320 0Z" ></path>
        <path d="M384 800h256l-77.888 224H456.992z" p-id="2418" ></path>
        <path d="M480 320h63.424a96 96 0 0 1 88.768 59.456l144.928 352A96 96 0 0 1 688.32 864H335.04a96 96 0 0 1-88.8-132.544l144.96-352A96 96 0 0 1 480 320z"></path>
      </symbol>
    </svg>

解析結(jié)果:


image.png

<line>

line元素是一個(gè)SVG基本形狀,用來(lái)創(chuàng)建一條連接兩個(gè)點(diǎn)的線。

  • x1 屬性在 x 軸定義線條的開(kāi)始
  • y1 屬性在 y 軸定義線條的開(kāi)始
  • x2 屬性在 x 軸定義線條的結(jié)束
  • y2 屬性在 y 軸定義線條的結(jié)束
    例如:
<line
        x1="210"
        y1="174"
        x2="0"
        y2="174"
        stroke="rgba(35, 200, 245, 0.39)"
        stroke-dasharray="2,2"
        stroke-width="2"
      />

解析:


image.png

<text>

text元素定義了一個(gè)由文字組成的圖形。
例如:

   <text x="0" y="160" fill="#23C8F5" class="svg-text">
        男:45%
      </text>

解析:


image.png

<linearGradient>

linearGradient元素用來(lái)定義線性漸變,用于圖形元素的填充或描邊。
例如:

<svg width="120" height="120"  viewBox="0 0 120 120"
     xmlns="http://www.w3.org/2000/svg" version="1.1"
     xmlns:xlink="http://www.w3.org/1999/xlink" >

    <defs>
        <linearGradient id="MyGradient">
            <stop offset="5%"  stop-color="green"/>
            <stop offset="95%" stop-color="gold"/>
        </linearGradient>
    </defs>

    <rect fill="url(#MyGradient)"
          x="10" y="10" width="100" height="100"/>
</svg>

解析:


svg 屬性參考

<viewBox>

viewBox屬性允許指定一個(gè)給定的一組圖形伸展以適應(yīng)特定的容器元素。
viewBox屬性的值是一個(gè)包含4個(gè)參數(shù)的列表 min-x, min-y, width and height, 以空格或者逗號(hào)分隔開(kāi)。

<stroke-dasharray>

stroke-dasharray 屬性可控制用來(lái)描邊的點(diǎn)劃線的圖案范式,作為一個(gè)外觀屬性,它也可以直接用作一個(gè)CSS樣式表內(nèi)部的屬性,用于創(chuàng)建虛線,之所以后面跟的是array的,是因?yàn)橹灯鋵?shí)是數(shù)組。
例如:

stroke-dasharray = '10'
stroke-dasharray = '10, 5'
stroke-dasharray = '20, 10, 5'

解析:


image.png

stroke-dasharray為一個(gè)參數(shù)時(shí): 其實(shí)是表示虛線長(zhǎng)度和每段虛線之間的間距
  如:stroke-dasharray = '10' 表示:虛線長(zhǎng)10,間距10,然后重復(fù) 虛線長(zhǎng)10,間距10

<stroke-dashoffset >

offset:偏移的意思,這個(gè)屬性是相對(duì)于起始點(diǎn)的偏移,正數(shù)偏移x值的時(shí)候,相當(dāng)于往左移動(dòng)了x個(gè)長(zhǎng)度單位,負(fù)數(shù)偏移x的時(shí)候,相當(dāng)于往右移動(dòng)了x個(gè)長(zhǎng)度單位。
需要注意的是,不管偏移的方向是哪邊,要記得dasharray 是循環(huán)的,也就是 虛線-間隔-虛線-間隔。
這個(gè)屬性要搭配stroke-dashoffset才能看得出來(lái)效果,非虛線的話,是無(wú)法看出偏移的。
\color{red}{注意:dashoffset 偏移是順時(shí)針的字}

思路

半弧度進(jìn)度條是最難的,可使用stroke-dasharray配合stroke-dashoffset 來(lái)畫(huà)。

弧度講解1.png
圖2.png

進(jìn)度紅色部分AC為實(shí)線弧度,灰色部分BC為虛線弧度,空白部分AB弧度為間隔

  • 實(shí)線弧度AC = 圓周長(zhǎng)P - 弧線弧長(zhǎng)BC - 空白弧長(zhǎng)AB
  • 偏移角度:左半邊偏移量只和空白弧長(zhǎng)AB有關(guān),右半邊偏移量和弧長(zhǎng)BE+空白弧長(zhǎng)AB有關(guān)
  • 空白弧長(zhǎng)AB = 2Rarcsin(a/2R) (注意a為弦長(zhǎng)AB)
  • 圖 2 里的4個(gè)半弧長(zhǎng)是有4個(gè)path 化成的
  • 由于弧長(zhǎng)有寬度,弧長(zhǎng)AB的半徑 = 進(jìn)度條弧長(zhǎng)的半徑- 弧長(zhǎng)的寬度

實(shí)現(xiàn)代碼

<template>
  <div class="circle-wrap">
    <svg class="circle" viewBox="0 0 480 350">
      <defs>
        <linearGradient id="greenGradient" x1="100%" y1="100%">
          <stop offset="0%" stop-color="rgba(32, 174, 214, 0.07)">
            <!-- <animate attributeName="stop-color" values="lightblue;blue;red;red;black;red;red;purple;lightblue" dur="14s" repeatCount="indefinite" /> -->
          </stop>
          <stop offset="100%" stop-color="rgba(8, 24, 88, 0.07)">
            <!-- <animate attributeName="stop-color" values="lightblue;orange;purple;purple;black;purple;purple;blue;lightblue" dur="14s" repeatCount="indefinite" />
            <animate attributeName="offset" values=".95;.80;.60;.40;.20;0;.20;.40;.60;.80;.95" dur="14s" repeatCount="indefinite" /> -->
          </stop>
        </linearGradient>
      </defs>
      <polygon
        points="80 260,400 260,480 340,0 340"
        fill="url(#greenGradient)"
      />
      <!-- 內(nèi)圓 -->
      <path
        d="
        M 240 175
        m 0 110
        a 110 110 0 1 1 0 -220
        a 110 110 0 1 1 0 220
        "
        stroke="#09265E"
        stroke-width="1"
        fill="none"
        stroke-linecap="round"
        class="inside-circle"
        style="stroke-dasharray: 5px 5px"
      ></path>
      <!-- 中間兩個(gè)半圓圓 -->
      <path
        d="
        M 240 175
        m 0 130
        a 130 130 0 1 1 0 -260
        a 130 130 0 1 1 0 260
        "
        stroke="#09265E"
        stroke-width="20"
        fill="none"
        stroke-linecap="round"
        class="left-below-circle"
        :style="{
          strokeDasharray:
            midBelowArcLength + 'px   ' + (midP - midBelowArcLength) + 'px',
          strokeDashoffset: -Math.floor(midIntervalLeng / 2) + 'px',
        }"
      ></path>

      <path
        d="
        M 240 175
        m 0 130
        a 130 130 0 1 1 0 -260
        a 130 130 0 1 1 0 260
        "
        stroke="#23C8F5"
        stroke-width="20"
        fill="none"
        stroke-linecap="round"
        v-show="leftMidSolidLine"
        class="left-above-circle"
        :style="{
          strokeDasharray:
            leftMidSolidLine + 'px   ' + leftMidDottedLine + 'px',
          strokeDashoffset: -Math.floor(midIntervalLeng / 2) + 'px',
        }"
      >
        >
      </path>
      <path
        d="
          M 240 175
          m 0 130
          a 130 130 0 1 1 0 -260
          a 130 130 0 1 1 0 260"
        stroke="#09265E"
        stroke-width="20"
        fill="none"
        stroke-linecap="round"
        class="right-below-circle"
        :style="{
          strokeDasharray:
            midBelowArcLength + 'px   ' + (midP - midBelowArcLength) + 'px',
          strokeDashoffset:
            Math.floor(midIntervalLeng / 2 + midBelowArcLength) + 'px',
        }"
      ></path>
      <path
        d="
          M 240 175
          m 0 130
          a 130 130 0 1 1 0 -260
          a 130 130 0 1 1 0 260"
        stroke="#E7B417"
        stroke-width="20"
        fill="none"
        stroke-linecap="round"
        class="right-above-circle"
        :style="{
          strokeDasharray:
            rightMidSolidLine + 'px   ' + rightMidDottedLine + 'px',
          strokeDashoffset:
            Math.floor(midIntervalLeng / 2 + rightMidSolidLine) + 'px',
        }"
      ></path>
      <!-- 外邊圓 -->
      <path
        d="
        M 240 175
        m 0 145
        a 145 145 0 1 1 0 -290
        a 145 145 0 1 1 0 290
        "
        stroke="#09265E"
        stroke-width="2"
        fill="none"
        stroke-linecap="round"
        class="left-below-circle"
        :style="{
          strokeDasharray:
            outBelowArcLength + 'px   ' + (outP - outBelowArcLength) + 'px',
          strokeDashoffset: -Math.floor(outIntervalLeng / 2) + 'px',
        }"
      ></path>
      <path
        d="
        M 240 175
        m 0 145
        a 145 145 0 1 1 0 -290
        a 145 145 0 1 1 0 290
        "
        stroke="#23C8F5"
        stroke-width="2"
        fill="none"
        stroke-linecap="round"
        v-show="leftOutSolidLine"
        class="left-above-circle"
        :style="{
          strokeDasharray:
            leftOutSolidLine + 'px   ' + leftOutDottedLine + 'px',
          strokeDashoffset: -Math.floor(outIntervalLeng / 2) + 'px',
        }"
      ></path>
      <path
        d="
        M 240 175
        m 0 145
        a 145 145 0 1 1 0 -290
        a 145 145 0 1 1 0 290
        "
        stroke="#09265E"
        stroke-width="2"
        fill="none"
        stroke-linecap="round"
        class="right-below-circle"
        :style="{
          strokeDasharray:
            outBelowArcLength + 'px   ' + (outP - outBelowArcLength) + 'px',
          strokeDashoffset:
            Math.floor(outIntervalLeng / 2 + outBelowArcLength) + 'px',
        }"
      ></path>
      <path
        d="
          M 240 175
          m 0 145
          a 145 145 0 1 1 0 -290
          a 145 145 0 1 1 0 290"
        stroke="#E7B417"
        stroke-width="2"
        fill="none"
        stroke-linecap="round"
        class="right-above-circle"
        :style="{
          strokeDasharray:
            rightOutSolidLine + 'px   ' + rightOutDottedLine + 'px',
          strokeDashoffset:
            Math.floor(outIntervalLeng / 2 + rightOutSolidLine) + 'px',
        }"
      ></path>
      <text x="0" y="160" fill="#23C8F5" class="svg-text">
        男:{{ leftPer }}%
      </text>
      <text x="410" y="160" fill="#E7B417" class="svg-text">
        女:{{ rightPer }}%
      </text>
      <!-- 左邊線 -->
      <line
        x1="210"
        y1="174"
        x2="0"
        y2="174"
        stroke="rgba(35, 200, 245, 0.39)"
        stroke-dasharray="2,2"
        stroke-width="2"
      />
      <!-- 右邊線 -->
      <line
        x1="275"
        y1="174"
        x2="480"
        y2="174"
        stroke="rgba(216, 162, 18, .39)"
        stroke-dasharray="2,2"
        stroke-width="2"
      />
      <use xlink:href="#male" x="180" y="135" />
      <use xlink:href="#female" x="230" y="135" />
    </svg>

    <svg class="icon">
      <symbol
        id="male"
        viewBox="0 0 1024 1024"
        width="80"
        height="80"
        fill="#23C8F5"
      >
        <path
          d="M631.091 661.333l-13.79 319.932A44.988 44.988 0 0 1 572.416 1024l-64.717-42.667L443.017 1024a44.988 44.988 0 0 1-44.852-42.735l-13.79-319.932h-27.682a45.807 45.807 0 0 1-45.397-42.564l-38.161-277.538a40.073 40.073 0 0 1 40.55-42.564h388.096c23.723 0 41.882 19.046 40.55 42.564l-38.126 277.538a45.807 45.807 0 0 1-45.398 42.564h-27.716zM507.733 256a128 128 0 1 1 0-256 128 128 0 0 1 0 256z"
        ></path>
      </symbol>
      <symbol
        id="female"
        viewBox="0 0 1024 1024"
        width="80"
        height="80"
        fill="#f4ea2a"
      >
        <path
          d="M512 160m-160 0a160 160 0 1 0 320 0 160 160 0 1 0-320 0Z"
        ></path>
        <path d="M384 800h256l-77.888 224H456.992z" p-id="2418"></path>
        <path
          d="M480 320h63.424a96 96 0 0 1 88.768 59.456l144.928 352A96 96 0 0 1 688.32 864H335.04a96 96 0 0 1-88.8-132.544l144.96-352A96 96 0 0 1 480 320z"
        ></path>
      </symbol>
    </svg>
  </div>
</template>
<script>
export default {
  components: {},
  props: {},
  data() {
    return {
      pi: 3.1415926, // π
      midR: 130, // 中間圓半徑
      midR2: 110,
      outR: 145, // 外圓半徑
      midP: '', // 中間圓周長(zhǎng)
      outP: '', // 外圓周長(zhǎng)
      a: 70, // 弦長(zhǎng)
      midBelowArcLength: '', // 中間半弧長(zhǎng)
      outBelowArcLength: '', // 外面半弧長(zhǎng)
      leftMidSolidLine: '', // 中間左半圓實(shí)線弧長(zhǎng)
      leftMidDottedLine: '', // 中間左半圓虛線弧長(zhǎng)
      rightMidSolidLine: '', // 中間右半圓實(shí)線弧長(zhǎng)
      rightMidDottedLine: '', // 中間半圓虛線弧長(zhǎng)
      rightMidrotate: '', // 中間半圓旋轉(zhuǎn)角度
      leftOutSolidLine: '', // 外邊左半圓實(shí)線弧長(zhǎng)
      leftOutDottedLine: '', // 外邊左半圓虛線弧長(zhǎng)
      rightOutSolidLine: '', // 外邊右半圓實(shí)線弧長(zhǎng)
      rightOutDottedLine: '', // 外邊半圓虛線弧長(zhǎng)
      rightOutrotate: '', // 外邊半圓旋轉(zhuǎn)角度
      midIntervalLeng: '', // 中間空白弧長(zhǎng)
      outIntervalLeng: '', // 外邊空白弧長(zhǎng)
      // leftAbovePer: 45, // 左邊進(jìn)度
      // rightAbovePer: 55, // 右邊進(jìn)度
    };
  },
  mounted() {
    
    this.getP();
    this.getArcLeng();
    this.getAboveArcLeng();
  },
  props:{
    leftPer: { // 左邊進(jìn)度
      type: Number,
      default() {
        return 45;
      }
    },
    rightPer:{ // 右邊進(jìn)度
      type: Number,
      default() {
        return 55;
      }
    }
  },
  watch:{
    leftPer(newVal,oldVal) {
      // this.rightPer = 100 - newVal;
      this.getAboveArcLeng()

    }
  },
  methods: {
    /**
     * 計(jì)算周長(zhǎng)函數(shù)
     */
    getP() {
      this.midP = this.pi * 2 * this.midR;
      console.log('中間圓周長(zhǎng)=', this.midP);
      this.outP = this.pi * 2 * this.outR;
      console.log('外邊圓周長(zhǎng)=', this.outP);
    },
    /**
     * 計(jì)算弦長(zhǎng)函數(shù)
     */
    getArcLeng() {
      this.midIntervalLeng =
        2 * this.midR * Math.asin(this.a / (2 * this.midR2)); // 中間圓空白弧長(zhǎng)
      console.log('中間圓空白弧長(zhǎng)=', this.midIntervalLeng);
      this.midBelowArcLength = (this.midP - this.midIntervalLeng * 2) / 2; // 中間圓底層半弧長(zhǎng)
      console.log('中間圓底層半弧長(zhǎng)=', this.midBelowArcLength);

      this.outIntervalLeng =
        2 * this.outR * Math.asin(this.a / (2 * this.outR)); // 外邊圓空白弧長(zhǎng)
      console.log('外邊圓空白弧長(zhǎng)=', this.outIntervalLeng);
      this.outBelowArcLength = (this.outP - this.outIntervalLeng * 2) / 2; // 外邊圓底層半弧長(zhǎng)
      console.log('外邊圓底層半弧長(zhǎng)=', this.outBelowArcLength);
    },
    /**
     * 計(jì)算進(jìn)度弧長(zhǎng)
     */
    getAboveArcLeng() {
      // 中間圓左邊實(shí)線弧長(zhǎng)
      this.leftMidSolidLine = this.leftPer
        ? (this.midBelowArcLength / 100) * this.leftPer - 10
        : 0;
      // console.log('中間圓左邊實(shí)線弧長(zhǎng)=', this.leftMidSolidLine);
      // 中間圓左邊虛線弧長(zhǎng)
      this.leftMidDottedLine = this.midP - this.leftMidSolidLine;
      // console.log('中間圓左邊虛線弧長(zhǎng)=', this.leftMidDottedLine);

      // 中間圓右邊實(shí)線弧長(zhǎng)
      this.rightMidSolidLine = this.rightPer
        ? (this.midBelowArcLength / 100) * this.rightPer - 10
        : 0;
      // console.log('中間圓右邊實(shí)線弧長(zhǎng)=', this.rightMidSolidLine);
      // 中間圓右邊虛線弧長(zhǎng)
      this.rightMidDottedLine = this.midP - this.rightMidSolidLine;
      // console.log('中間圓右邊虛線弧長(zhǎng)=', this.rightMidDottedLine);

      // 外邊圓左邊實(shí)線弧長(zhǎng)
      this.leftOutSolidLine = this.leftPer
        ? (this.outBelowArcLength / 100) * this.leftPer
        : 0;
      // console.log('外邊圓左邊實(shí)線弧長(zhǎng)=', this.outBelowArcLength);
      // 外邊圓左邊虛線弧長(zhǎng)
      this.leftOutDottedLine = this.outP - this.leftOutSolidLine;
      // console.log('外邊圓左邊虛線弧長(zhǎng)=', this.leftOutDottedLine);

      // 外邊圓右邊實(shí)線弧長(zhǎng)
      this.rightOutSolidLine = this.rightPer
        ? (this.outBelowArcLength / 100) * this.rightPer - 10
        : 0;
      // console.log('外邊圓右邊實(shí)線弧長(zhǎng)=', this.rightOutSolidLine);
      // 外邊圓右邊虛線弧長(zhǎng)
      this.rightOutDottedLine = this.outP - this.rightOutSolidLine;
      // console.log('外邊圓右邊虛線弧長(zhǎng)=', this.rightOutDottedLine);
    },
  },
};
</script>
<style lang="scss" scoped>
.circle-wrap {
  position: relative;
  width: 100%;
  height: 100%;
}
.circle {
  width: 100%;
  height: 100%;
}

.svg-text {
  font-size: 16px;
}
</style>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容