要實現的效果圖如下
svg 語法學習
可以參考https://developer.mozilla.org/zh-CN/docs/Web/SVG網站上的語法
元素參考
path元素用來定義形狀的通用元素。
下面的命令可用於路徑數據:
- 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
注釋:以上所有命令均允許小寫字母。大寫表示絕對定位,小寫表示相對定位。
例如:畫個圓環
<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>
解析:
symbol元素用來定義一個圖形模板對象,它可以用一個<use>
元素實例化。symbol
元素對圖形的作用是在同一文檔中多次使用,添加結構和語義。結構豐富的文檔可以更生動地呈現出來,類似講演稿或盲文,從而提升了可訪問性。注意,一個symbol
元素本身是不呈現的。只有symbol
元素的實例(亦即,一個引用了symbol
的 <use>
元素)才能呈現。
use元素在SVG文檔內取得目標節點,並在別的地方復制它們。它的效果等同於這些節點被深克隆到一個不可見的DOM中,然后將其粘貼到use
元素的位置,很像HTML5中的克隆模板元素。因為克隆的節點是不可見的,所以當使用CSS樣式化一個use
元素以及它的隱藏的后代元素的時候,必須小心注意。隱藏的、克隆的DOM不能保證繼承CSS屬性,除非你明文設置使用[CSS繼承]
(https://developer.mozilla.org/en/CSS/inheritance)。
一般情況下symbol 和use配合着使用的。
例如: 效果中的小人,如果自己畫的話,就非常耗時間,可以在iconfont 里找一個,然后引入進來;
在自己的svg 里引入
<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>
解析結果:
line元素是一個SVG基本形狀,用來創建一條連接兩個點的線。
- x1 屬性在 x 軸定義線條的開始
- y1 屬性在 y 軸定義線條的開始
- x2 屬性在 x 軸定義線條的結束
- y2 屬性在 y 軸定義線條的結束
例如:
<line
x1="210"
y1="174"
x2="0"
y2="174"
stroke="rgba(35, 200, 245, 0.39)"
stroke-dasharray="2,2"
stroke-width="2"
/>
解析:
text元素定義了一個由文字組成的圖形。
例如:
<text x="0" y="160" fill="#23C8F5" class="svg-text">
男:45%
</text>
解析:
linearGradient元素用來定義線性漸變,用於圖形元素的填充或描邊。
例如:
<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屬性的值是一個包含4個參數的列表 min-x, min-y, width and height, 以空格或者逗號分隔開。
stroke-dasharray 屬性可控制用來描邊的點划線的圖案范式,作為一個外觀屬性,它也可以直接用作一個CSS樣式表內部的屬性,用於創建虛線,之所以后面跟的是array的,是因為值其實是數組。
例如:
stroke-dasharray = '10'
stroke-dasharray = '10, 5'
stroke-dasharray = '20, 10, 5'
解析:
stroke-dasharray為一個參數時: 其實是表示虛線長度和每段虛線之間的間距
如:stroke-dasharray = '10' 表示:虛線長10,間距10,然后重復 虛線長10,間距10
offset:偏移的意思,這個屬性是相對於起始點的偏移,正數偏移x值的時候,相當於往左移動了x個長度單位,負數偏移x的時候,相當於往右移動了x個長度單位。
需要注意的是,不管偏移的方向是哪邊,要記得dasharray 是循環的,也就是 虛線-間隔-虛線-間隔。
這個屬性要搭配stroke-dashoffset才能看得出來效果,非虛線的話,是無法看出偏移的。
$\color{red}{注意:dashoffset 偏移是順時針的字}$
思路
半弧度進度條是最難的,可使用stroke-dasharray配合stroke-dashoffset 來畫。
進度紅色部分AC為實線弧度,灰色部分BC為虛線弧度,空白部分AB弧度為間隔
- 實線弧度AC = 圓周長P - 弧線弧長BC - 空白弧長AB
- 偏移角度:左半邊偏移量只和空白弧長AB有關,右半邊偏移量和弧長BE+空白弧長AB有關
- 空白弧長AB = 2Rarcsin(a/2R) (注意a為弦長AB)
- 圖 2 里的4個半弧長是有4個path 化成的
- 由於弧長有寬度,弧長AB的半徑 = 進度條弧長的半徑- 弧長的寬度
實現代碼
<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)"
/>
<!-- 內圓 -->
<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>
<!-- 中間兩個半圓圓 -->
<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: '', // 中間圓周長
outP: '', // 外圓周長
a: 70, // 弦長
midBelowArcLength: '', // 中間半弧長
outBelowArcLength: '', // 外面半弧長
leftMidSolidLine: '', // 中間左半圓實線弧長
leftMidDottedLine: '', // 中間左半圓虛線弧長
rightMidSolidLine: '', // 中間右半圓實線弧長
rightMidDottedLine: '', // 中間半圓虛線弧長
rightMidrotate: '', // 中間半圓旋轉角度
leftOutSolidLine: '', // 外邊左半圓實線弧長
leftOutDottedLine: '', // 外邊左半圓虛線弧長
rightOutSolidLine: '', // 外邊右半圓實線弧長
rightOutDottedLine: '', // 外邊半圓虛線弧長
rightOutrotate: '', // 外邊半圓旋轉角度
midIntervalLeng: '', // 中間空白弧長
outIntervalLeng: '', // 外邊空白弧長
// leftAbovePer: 45, // 左邊進度
// rightAbovePer: 55, // 右邊進度
};
},
mounted() {
this.getP();
this.getArcLeng();
this.getAboveArcLeng();
},
props:{
leftPer: { // 左邊進度
type: Number,
default() {
return 45;
}
},
rightPer:{ // 右邊進度
type: Number,
default() {
return 55;
}
}
},
watch:{
leftPer(newVal,oldVal) {
// this.rightPer = 100 - newVal;
this.getAboveArcLeng()
}
},
methods: {
/**
* 計算周長函數
*/
getP() {
this.midP = this.pi * 2 * this.midR;
console.log('中間圓周長=', this.midP);
this.outP = this.pi * 2 * this.outR;
console.log('外邊圓周長=', this.outP);
},
/**
* 計算弦長函數
*/
getArcLeng() {
this.midIntervalLeng =
2 * this.midR * Math.asin(this.a / (2 * this.midR2)); // 中間圓空白弧長
console.log('中間圓空白弧長=', this.midIntervalLeng);
this.midBelowArcLength = (this.midP - this.midIntervalLeng * 2) / 2; // 中間圓底層半弧長
console.log('中間圓底層半弧長=', this.midBelowArcLength);
this.outIntervalLeng =
2 * this.outR * Math.asin(this.a / (2 * this.outR)); // 外邊圓空白弧長
console.log('外邊圓空白弧長=', this.outIntervalLeng);
this.outBelowArcLength = (this.outP - this.outIntervalLeng * 2) / 2; // 外邊圓底層半弧長
console.log('外邊圓底層半弧長=', this.outBelowArcLength);
},
/**
* 計算進度弧長
*/
getAboveArcLeng() {
// 中間圓左邊實線弧長
this.leftMidSolidLine = this.leftPer
? (this.midBelowArcLength / 100) * this.leftPer - 10
: 0;
// console.log('中間圓左邊實線弧長=', this.leftMidSolidLine);
// 中間圓左邊虛線弧長
this.leftMidDottedLine = this.midP - this.leftMidSolidLine;
// console.log('中間圓左邊虛線弧長=', this.leftMidDottedLine);
// 中間圓右邊實線弧長
this.rightMidSolidLine = this.rightPer
? (this.midBelowArcLength / 100) * this.rightPer - 10
: 0;
// console.log('中間圓右邊實線弧長=', this.rightMidSolidLine);
// 中間圓右邊虛線弧長
this.rightMidDottedLine = this.midP - this.rightMidSolidLine;
// console.log('中間圓右邊虛線弧長=', this.rightMidDottedLine);
// 外邊圓左邊實線弧長
this.leftOutSolidLine = this.leftPer
? (this.outBelowArcLength / 100) * this.leftPer
: 0;
// console.log('外邊圓左邊實線弧長=', this.outBelowArcLength);
// 外邊圓左邊虛線弧長
this.leftOutDottedLine = this.outP - this.leftOutSolidLine;
// console.log('外邊圓左邊虛線弧長=', this.leftOutDottedLine);
// 外邊圓右邊實線弧長
this.rightOutSolidLine = this.rightPer
? (this.outBelowArcLength / 100) * this.rightPer - 10
: 0;
// console.log('外邊圓右邊實線弧長=', this.rightOutSolidLine);
// 外邊圓右邊虛線弧長
this.rightOutDottedLine = this.outP - this.rightOutSolidLine;
// console.log('外邊圓右邊虛線弧長=', 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>