SVG stroke-linecap

stroke-linecap用來描述線段<line>的端點呈現方式,提供三種屬性

  • stroke-linecap: butt 預設值,端點不外擴也不變形
  • stroke-linecap: round 端點外擴並呈現圓角
  • stroke-linecap: square 端點外擴並呈現方角

(中間畫一條<path>比較好觀察外擴情形)

butt(預設值)

svg(viewbox="-50 -50 100 100" width="500")
    line(x1=-40 y1=0 x2=40 y2=0 stroke="black" stroke-width=8 stroke-linecap="butt")
    path(d="M-40, 00 L40, 0" stroke="white")

round

svg(viewbox="-50 -50 100 100" width="500")
    line(x1=-40 y1=0 x2=40 y2=0 stroke="black" stroke-width=8 stroke-linecap="round")
    path(d="M-40, 00 L40, 0" stroke="white")

square

svg(viewbox="-50 -50 100 100" width="500")
    line(x1=-40 y1=0 x2=40 y2=0 stroke="black" stroke-width=8 stroke-linecap="square")
    path(d="M-40, 00 L40, 0" stroke="white")

SVG漸層

<defs>呼叫<linearGradient>

SVG的漸層要先在文件開頭定義<def>標籤,並且在裡面設定<linearGradient>寫漸層指示
然後再回到CSS用fill呼應漸層的ID

svg(viewbox="-50 -50 100 100")
 defs
  linearGradient#mycolor
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 circle(cx=0 cy=0 r=20)
svg
 border: 1px solid #000
 width: 500px
circle
 fill: url(#mycolor)

預設的漸層方向是由左至右

漸層方向

預設漸層方向是由左至右,設定漸層方向要在<linearGradient>寫參數設定。值填入0%100%

  • 起點:x1y1
  • 終點:x2y2

由左至右(預設值)

svg(viewbox="-50 -50 100 100" width="500")
 defs
  linearGradient#mycolor(x1="0%" y1="0%" x2="100%" y2="0%")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 rect(x=-25 y=-25 width=50 height=50 fill="url(#mycolor)")

由右至左

svg(viewbox="-50 -50 100 100" width="500")
 defs
  linearGradient#mycolor(x1="100%" y1="0%" x2="0%" y2="0%")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 rect(x=-25 y=-25 width=50 height=50 fill="url(#mycolor)")

由下至上

svg(viewbox="-50 -50 100 100" width="500")
 defs
  linearGradient#mycolor(x1="0%" y1="100%" x2="0%" y2="0%")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 rect(x=-25 y=-25 width=50 height=50 fill="url(#mycolor)")

由上至下

svg(viewbox="-50 -50 100 100" width="500")
 defs
  linearGradient#mycolor(x1="0%" y1="0%" x2="0%" y2="100%")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 rect(x=-25 y=-25 width=50 height=50 fill="url(#mycolor)")

漸層渲染方式 gradientUnits

gradientUnits指定漸層的渲染方式,看是要以該生效物件為區域,在裡面切%畫漸層,還是以整個SVG為區域,整份SVG切%畫漸層,然後再把指定物件框出來

objectBoundingBox (預設值)

gradientUnits=objectBoundingBox 只針對生效物件畫

svg(viewbox="-50 -50 100 100")
 defs
 
 linearGradient#mycolor(gradientUnits="objectBoundingBox")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 rect(x=-25 y=-25 width=50 height=50)
svg
 border: 1px solid #000
 width: 500px
rect
 fill: url(#mycolor)

userSpaceOnUse

gradientUnits=userSpaceOnUse 針對整份SVG畫,在切出指定元素份的漸層
※這裡有一個Bug,當在使用userSpaceOnUse的時候,SVG的viewbox起點一定要從0,0開始。不然漸層的位置也會一起偏掉

svg(viewbox="0 0 100 100")
 defs
 
 linearGradient#mycolor(gradientUnits="userSpaceOnUse")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 rect(x=25 y=25 width=50 height=50)
svg
 border: 1px solid #000
 width: 500px
rect
 fill: url(#mycolor)

<line>漸層

由於<line>不視作一個區域(他不像<rect><circle>可以用fill填色)
所以<line>不能用好用的gradientUnits漸層,只能用userSpaceOnUse漸層。所以效果那些就不是那麼一目瞭然

svg(viewbox="0 0 100 100")
 defs
  linearGradient#mycolor(gradientUnits="userSpaceOnUse")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 line(x1=25 y1=50 x2=75 y2=50)
svg
 border: 1px solid #000
 width: 500px
line
 stroke: url(#mycolor)
 stroke-width: 3px

會動的圓形進度條

本範例使用SVG處理圖形,並搭配使用stroke-dasharraystroke-dashoffset讓圖形能夠動態增長
注意本範例僅能適用在stroke類圖形,如果UI設計師把圖形轉成path的話,它就不適用了

stroke-dasharray屬性介紹

stroke-dasharray描述實線與虛線的配置,用法如下

stroke-dasharray: 實線值 虛線值

實線值與虛線值的單位不是px也不是pt
他的單位會跟viewbox的設定一起跑

svg(viewbox="-50 -50 100 100")
 line(x1=-50 y1=0 x2=50 y2=0)
svg
 width: 300px
 height: 300px
 border: 1px solid #000
 line
  stroke: #000
  stroke-dasharray: 10 20

本例viewbox的設定值為-50 -50 100 100,即表示x軸起點值為-50,且長度為100,因此x軸的終點值為50

stroke-dasharray: 10 20的指定下,設定實線(黑線)為10,虛線為20
所以整個線段的虛實分佈為

-50-40實線段
-40-20虛線段
-20-10實線段
-1010虛線段
1020實線段
2040實線段
4050實線段

上述範例中viewbox的長度設定為100,所以整個實線/虛線可以用的總額就是100
寫成stroke-dasharray: 0 100的話,線就會不見(全都是虛線)
寫成stroke-dasharray: 100 0的話,就會看到一條全黑的實線(全都是實線)

補充:stroke-dasharray的多層次指定

stroke-dasharray可以指定多個值,一樣都是奇數值放實線長度,偶數值放虛線的長度描述
只要viewbox的長度不夠他走完,它就會回到最前面輪迴
前例的輪迴是2 base會比較單純,多層次指定的輪迴的虛實設定就會更複雜
(本章動態圓形進度條只需要用到2個值就可以了)

svg(viewbox="-50 -50 100 100")
 line(x1=-50 y1=0 x2=50 y2=0)
svg
 width: 300px
 height: 300px
 border: 1px solid #000
 line
  stroke: #000
  stroke-dasharray: 10 15 20 25
位置虛線/實線段stroke-dasharray線段輪迴
-50-40實線段10
-40-25虛線段15
-25-5實線段20
-520虛線段25
2030實線段10
3045虛線段15
4550實線段20
這裡沒走完所以被卡掉
只剩短短一段而已

stroke-dashoffset屬性介紹

stroke-dashoffset提供開始位置偏移的效果
其實就是更動x1的數值,如果是stroke-dashoffset: 10的話x1的數值就會比原本的還要在-10

舉例來說

svg(viewbox="-50 -50 100 100")
 line(x1=-50 y1=0 x2=50 y2=0)
svg
 width: 300px
 height: 300px
 border: 1px solid #000
 line
  stroke: #000
  stroke-dasharray: 100 100
  stroke-dashoffset: 10

這個範例中可以看到線段整個往左邊偏移了10,但是他其實也就是等於這個意思

svg(viewbox="-50 -50 100 100")
 line(x1=-60 y1=0 x2=50 y2=0)
svg
 width: 300px
 height: 300px
 border: 1px solid #000
 line
  stroke: #000
  stroke-dasharray: 100 100
  // stroke-dashoffset: 10

圓形的stroke:百分比進度條

理解上述stoke-dasharraystroke-dashoffset原理之後,我們來看看圓形的版本要怎麼做

首先,普通的畫一個圓

svg(viewbox="-50 -50 100 100")
 circle(cx=0 cy=0 r=40)
svg
 width: 300px
 height: 300px
 border: 1px solid #000
 circle
  fill: none
  stroke: teal

接著,再用stroke-dasharray讓這個圓具有進度條的感覺(嘗試畫75%的圓)

svg
 width: 300px
 height: 300px
 border: 1px solid #000
 circle
  fill: none
  stroke: teal
  stroke-dasharray: 188.4 62.8

圓周長的算法為

半徑(r)×2×3.14

上述範例的半徑r40,所以總圓周長為40×2×3.14251.2
再往下規劃實線/虛線的分配,實線線段要配給75%,虛線線段要配給25%,所以

  • 實線長度:251.2×75%=188.4
  • 虛線長度:251.2×25%=62.8

雖然成功畫出了75%的實線段與25%的虛線段,但是…位置看起來不像一個進度條
這是因為,<circle>的起始點是位於90deg的位置,也就是圓形的右方
然後再順時鐘往前畫出去

順時鐘這個方向是對的,但是90deg這個起點不理想
所以要再透過stroke-dashoffset偏移起始點,讓起始點變成0deg

svg
 width: 300px
 height: 300px
 border: 1px solid #000
 circle
  fill: none
  stroke: teal
  stroke-dasharray: 188.4 62.8
  stroke-dashoffset: 62.8

stroke-dashoffset的值也是以圓周長來算,由於要偏移1/4的圓周長的距離,所以給定總長度251.225%,也就是62.8的偏移量

這樣就是我們所熟悉的圓形進度條了!

接下來來透過stroke-dasharrayanimation實作動態
動態的技巧是:讓實線的值從0188.4,視覺上就能做出圓形長出來的效果了

svg(viewbox="-50 -50 100 100")
 circle(cx=0 cy=0 r=40)
 text(x=0 y=0) 75%
svg
 width: 300px
 height: 300px
 border: 1px solid #000
 circle
  fill: none
  stroke: teal
  stroke-dashoffset: 62.8
  animation: mycircle 3s infinite
 text
  fill: teal
  dominant-baseline: middle
  text-anchor: middle
  
 @keyframes mycircle
  0%
   stroke-dasharray: 0 251.2
  100%
   stroke-dasharray: 188.4 62.8

SVG animate

稱為SMIL Animation,有別於CSS keyframes、JavaScript Animation,SMIL Animation為第三種前端動畫技巧

使用<animate>控制

<animate>寫在要動的元素之內

  • attributeName:控制要動的屬性,如半徑r 、寬度width等等
  • values:屬性移動的參數,用;分割
    attributeName=r values=10;30;10即表示
    動畫0%的時候半徑為10
    動畫50%的時候半徑為30
    動畫100%的時候半徑為10
  • dur:動作時間,單位為s
  • repeatCount:重複次數,如果要重複無限次的話則填入indefinite
svg(viewbox="0 0 100 100" width=300 height=300)
 line(x1=30 y1=50 x2=70 y2=50)
 line(x1=50 y1=30 x2=50 y2=70)
 circle(cx=50 cy=50 r=10)
  animate(attributeName="r" values="10;30;10" dur="2s" repeatCount="indefinite")
svg
 border: 1px solid #000
 *
  stroke-width: 3
  stroke: #000
  fill: #fff

animateTransform變形動畫

比起animate多了一個type屬性,用來指定變形動作例如rotatescaletranslate等等

transform預設的旋轉中心點為元素左上角,所以也跟CSS一樣,需要transform-origin來控制旋轉中心點
參考前文

svg(viewbox="0 0 100 100" width=300 height=300)
 rect(x=30 y=30 width=40 height=40)
  animateTransform(attributeName="transform" type="rotate" values="0;360" dur="2s" repeatCount="indefinite")
 circle(cx=50 cy=50 r=10)
 line(x1=30 y1=50 x2=70 y2=50)
 line(x1=50 y1=30 x2=50 y2=70) 
svg
 border: 1px solid #000
 rect
  transform-origin: center center
 *
  stroke-width: 3
  stroke: #000
  fill: #fff

group

使用g標籤,將需要群組化的元素都放進去後,再指定動畫,這坨元素都能一起動了
※使用g後,animate標籤就放在g以下,跟群組內元素平行

svg(viewbox="0 0 100 100" width=300 height=300)
 g
  rect(x=30 y=30 width=40 height=40)
  circle(cx=50 cy=50 r=10)
  line(x1=30 y1=50 x2=70 y2=50)
  line(x1=50 y1=30 x2=50 y2=70) 
  animateTransform(attributeName="transform" type="rotate" values="0;360" dur="2s" repeatCount="indefinite")
svg
 border: 1px solid #000
 g
  transform-origin: center center
 *
  stroke-width: 3
  stroke: #000
  fill: #fff

begin

透過begin指定開始時間,也可以指定負數,負數的話會在一開始時就直接動

svg(viewbox="0 0 100 100" width=300 height=300)
 rect(x=0 y=0 width=10 height=70)
  animate(attributeName="height" begin="0s" dur="2s" values="50;100;50" repeatCount="indefinite")
 rect(x=15 y=0 width=10 height=60)
  animate(attributeName="height" begin="-0.3s" dur="2s" values="50;100;50" repeatCount="indefinite")
 rect(x=30 y=0 width=10 height=80)
  animate(attributeName="height" begin="-0.6s" dur="2s" values="50;100;50" repeatCount="indefinite")
 rect(x=45 y=0 width=10 height=90)
  animate(attributeName="height" begin="-0.9s" dur="2s" values="50;100;50" repeatCount="indefinite")
 rect(x=60 y=0 width=10 height=50)
  animate(attributeName="height" begin="-1.2s" dur="2s" values="50;100;50" repeatCount="indefinite")
 rect(x=75 y=0 width=10 height=40)
  animate(attributeName="height" begin="-1.5s" dur="2s" values="50;100;50" repeatCount="indefinite")
 rect(x=90 y=0 width=10 height=30)
  animate(attributeName="height" begin="-1.8s" dur="2s" values="50;100;50" repeatCount="indefinite")
svg
 border: 1px solid #000

SVG

viewbox

viewbox="右上角的X座標 右上角的Y座標 寬度 高度"

右上角的X座標,建議設定為0
右上角的Y座標,建議設定為0
這樣,svg圖形的中心點會為(寬度/2, 高度/2)

圓形

circle(cx=圓心X座標 cy=圓心Y座標 r=半徑)
svg(viewbox="0 0 100 100" width="300" height="300")
 circle(cx=50 cy=50 r=10)
svg
 border: 1px solid #000

方形

rect(右上角X座標 右上角Y座標 寬度 高度)
svg(viewbox="0 0 100 100" width="300" height="300")
 rect(x=40 y=40 width=20 height=20)

stroke與fill:框線與填色

svg圖形的填色不使用backgroundborder等CSS屬性
他要使用專門的定義顏色的系統

svg(viewbox="0 0 100 100" width="300" height="300")
 rect(x=40 y=40 width=20 height=20 stroke="skyblue" fill="aliceblue")

這些指定樣式的屬性也可以寫到CSS裡面

svg(viewbox="0 0 100 100")
 rect(x=40 y=40 width=20 height=20)
svg
 border: 1px solid #000
 width: 300px
 height: 300px
 rect
  stroke: skyblue
  fill: aliceblue

上圖將<svg>widthheight<rect>strokefill都寫到CSS裡了

直線

line(x1=起始點X座標 y1=起始點Y座標 x2=終結點X座標 y2=終結點Y座標 stroke=線條顏色) 

stroke不是必備屬性,只是不指定的話就看不到線條了

svg(viewbox="0 0 100 100" width="300" height="300")
 line(x1=30 y1=50 x2=70 y2=50 stroke="black")

文字

text(x=起始X座標 y=起始Y座標) ★★文字內容★★
svg(viewbox="0 0 100 100" width="300" height="300")
 text(x=50 y=50) 文字

文字的中心線

仔細看上圖,雖然xy都設定了中心點的值,但是描繪出來的文字看起來還是偏一邊
這是因為,<text>的中心線預設位在右下方,所以他是以右下方等於(xy)為基準來描繪文字

我們可以透過dominant-baselinetext-anchor來重新定義<text>的垂直/水平中心線

svg(viewbox="0 0 100 100" width="300" height="300")
 text(x=50 y=50) 文字
svg
 border: 1px solid #000
 text
  dominant-baseline: middle
  text-anchor: middle

存成svg檔案

  • 確認widthheight有寫在<svg>裡面
  • <svg>加上xmlns="http://www.w3.org/2000/svg",指定xml檔案類型為svg
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewbox="0 0 100 100">
    <circle cx="50" cy="50" r="10"></circle>
</svg>