《SVG 動畫開發實戰》 - ✏️ SVG 描邊動畫(Stroke)

✏️ SVG 描邊動畫(Stroke)

演示

chapter5-1

查看在線版本css

原理

要實現上面酷炫的描邊動畫,首先咱們須要瞭解 SVG 中實現描邊動畫的三個相關屬性:分別是 strokestroke-dasharraystroke-dashoffset。這三個屬性做爲外觀顯示屬性,均可以做爲 CSS 屬性來使用。html

stroke

SVG 中的 stroke 屬性用來控制繪製描邊的方式,咱們也可使用 CSS 來控制 SVG 的描邊樣式。git

https://codepen.io/xiaoluobod...github

stroke-dasharray

咱們知道在 CSS 中實現虛線邊框,要使用 border: 1px dashed #f4a; ;在 SVG 中要實現虛線效果就要使用 stroke-dasharray 屬性。segmentfault

https://codepen.io/xiaoluobod...微信

stroke-dashoffset

stroke-dashoffset 屬性用來指定路徑從開始位置的偏移量。經過指定偏移量會讓繪製好的線偏移app

原來的位置一段空白dom

https://codepen.io/xiaoluobod...async

動起來

那麼結合上面三個屬性,咱們試着動態改變 stroke-dashoffset 屬性,看看會發生什麼。經過動態改變偏移量屬性,結合設定好的 width 以及 stroke-dasharray 屬性,會讓原來靜態的線有一種用畫筆描繪出來的效果。svg

https://codepen.io/xiaoluobod...

實戰

下面咱們使用 GreenSock 實現描邊效果,GreenSock 提供了 DrawSVGPlugin 用於控制各類圖形的描邊效果,原理主要是經過控制上面學過的 stroke-dasharraystroke-dashoffset 兩個CSS屬性來實現動畫效果。

chapter5-2

獲取 SVG 代碼

這裏就使用 vivus 中的 Hi There 動畫 做爲 Demo,打開 vivus 官網,打開開發者工具,這裏使用的是 Chrome,選中 Hi There dom 元素,能夠看到 Hi There 的 SVG 代碼。咱們選中元素右鍵複製出來。

chapter5-3

獲得以下代碼:

<div class="bloc bloc-head">
  <svg height="300" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 404.7 354" enable-background="new 0 0 404.7 354" id="hi-there" onclick="hi.reset().play();">

    <!-- HI -->
    <path data-duration="10" d="M324.6,61.2c16.6,0,29.5-12.9,29.5-29.5c0-16.6-12.9-29.5-29.5-29.5c-16.6,0-29.5,12.9-29.5,29.5C295.1,48.4,308,61.2,324.6,61.2z" style="stroke-dasharray: 186, 226; stroke-dashoffset: 0;"></path>
    <path data-duration="130" d="M366.2,204.2c-9.8,0-15-5.6-15-15.1V77.2h-85v28h19.5c9.8,0,8.5,2.1,8.5,11.6v72.4c0,9.5,0.5,15.1-9.3,15.1H277h-20.7c-8.5,0-14.2-4.1-14.2-12.9V52.4c0-8.5,5.7-12.3,14.2-12.3h18.8v-28h-127v28h18.1c8.5,0,9.9,2.1,9.9,8.9v56.1h-75V53.4c0-11.5,8.6-13.3,17-13.3h11v-28H2.2v28h26c8.5,0,12,2.1,12,7.9v142.2c0,8.5-3.6,13.9-12,13.9h-21v33h122v-33h-11c-8.5,0-17-4.1-17-12.2v-57.8h75v58.4c0,9.1-1.4,11.6-9.9,11.6h-18.1v33h122.9h5.9h102.2v-33H366.2z" style="stroke-dasharray: 2216, 2256; stroke-dashoffset: 0;"></path>

    <path data-async="" data-delay="20" d="M358.8,82.8c11.1-4.2,18.8-14.7,18.8-27.5c0-8.5-3.4-16-8.9-21.3" style="stroke-dasharray: 60, 100; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M124.2,105.7V77c0-11.5,9.1-13.8,17.5-13.8h10.5V44.7" style="stroke-dasharray: 84, 124; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M147.9,40.2L171.2,63.2L175.7,63.2" style="stroke-dasharray: 38, 78; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M295.1,32.1L275.2,12.2" style="stroke-dasharray: 29, 69; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M266.2,204.7V75.9c0-8.5,5.2-12.8,13.7-12.8h18.3V44.7" style="stroke-dasharray: 187, 227; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M265.9,105.2L289.2,129.2L293.7,129.2" style="stroke-dasharray: 38, 78; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M374.2,204.7L374.2,94.2L358.8,82.8L351.2,77.2" style="stroke-dasharray: 140, 180; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M148.2,237.2L171.2,261.2L294.6,261.2L300.5,261.2L402.2,261.2L402.2,228.2L379.2,204.2" style="stroke-dasharray: 331, 371; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M124.2,204.7L124.2,157.2L175.7,157.2" style="stroke-dasharray: 99, 139; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M147.7,228.2L129.2,204.2" style="stroke-dasharray: 31, 71; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M7.2,237.3L30.2,261.2L152.2,261.2L152.2,241.7" style="stroke-dasharray: 175, 215; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M1.9,40.2L26,63.2L39.7,63.2" style="stroke-dasharray: 48, 88; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M129.2,12.2L148.2,33.2" style="stroke-dasharray: 29, 69; stroke-dashoffset: 0;"></path>
    <path data-async="" d="M303.9,53L328.1,77.2" style="stroke-dasharray: 35, 75; stroke-dashoffset: 0;"></path>

    <path d="M345.1,10.5L368.7,34" style="stroke-dasharray: 34, 74; stroke-dashoffset: 0;"></path>

    <!-- there -->
    <path data-delay="30" data-duration="60" stroke-linecap="round" stroke-linejoin="round" d="M76.8,337.3c0,0,1.9,12.2,13.1,12.2c22.1,0,23.8-1.8,59-66.4c-19.7,35.7-36.4,66.2-19.3,66.2c15.2,0,22.9-14.2,28.3-23.7c3.3-0.5,24-3.2,35-25.5c4-8.1,4.1-17.8-8.1-15.2c-5.6,1.2-13.1,14.8-15.7,19.2c-7.6,12.7-22.4,45.2-22.4,45.2s10.3-22.4,21.5-22.4c15.5,0-9.4,22.4,4.7,22.4c4.9,0,11.7-11.4,16.6-20.9c7.5,4.7,19.7,1.7,24.5-8.1c10.1-20.4-14.4-12.8-24.5,8.1c-5.5,11.3-2.2,21.1,11.2,21.1c16.4,0,26.1-28.3,30.5-37.5c9.9,2.5,14,2.5,22.7-1.1c-3.5,5.1-24,38.1-8.3,38.1c6.7,0,11.7-11.4,16.6-20.9c7.5,4.7,19.7,1.7,24.5-8.1c10.1-20.4-14.4-12.8-24.5,8.1c-5.5,11.3-2.2,21.1,11.2,21.1c16.4,0,20.6-4,24.7-10.5" style="stroke-dasharray: 851, 891; stroke-dashoffset: 0;"></path>

    <path stroke-linecap="round" stroke-linejoin="round" d="M157.3,300.8c3.8-2.3-29,0.8-35.6,3.2" style="stroke-dasharray: 37, 77; stroke-dashoffset: 0;"></path>
  </svg>
</div>

分析動畫時間軸

仔細觀察 vivus 中的 Hi There 的描邊動畫中一共有四個階段:

  1. 先描邊一個 Hi 字母的圓點
  2. 而後描邊 Hi 字母
  3. 再描邊 Hi 字母的 3D 邊框
  4. 最後描邊 there 單詞

咱們給要描邊的圖形,例子中會涉及到 pathpolylineline 等定義好 class,用於選擇。將無用 vivus 屬性去掉,優化後的代碼以下:

<div class="bloc bloc-head">
  <svg height="300" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 404.7 354" id="hi-there">

    <!-- HI -->
    <path class="hi-o" fill="none" stroke="#FFFFFF" stroke-width="4" d="M324.6,61.2c16.6,0,29.5-12.9,29.5-29.5c0-16.6-12.9-29.5-29.5-29.5c-16.6,0-29.5,12.9-29.5,29.5C295.1,48.4,308,61.2,324.6,61.2z" />
    <path class="hi" fill="none" stroke="#FFFFFF" stroke-width="4" d="M366.2,204.2c-9.8,0-15-5.6-15-15.1V77.2h-85v28h19.5c9.8,0,8.5,2.1,8.5,11.6v72.4c0,9.5,0.5,15.1-9.3,15.1H277h-20.7c-8.5,0-14.2-4.1-14.2-12.9V52.4c0-8.5,5.7-12.3,14.2-12.3h18.8v-28h-127v28h18.1c8.5,0,9.9,2.1,9.9,8.9v56.1h-75V53.4c0-11.5,8.6-13.3,17-13.3h11v-28H2.2v28h26c8.5,0,12,2.1,12,7.9v142.2c0,8.5-3.6,13.9-12,13.9h-21v33h122v-33h-11c-8.5,0-17-4.1-17-12.2v-57.8h75v58.4c0,9.1-1.4,11.6-9.9,11.6h-18.1v33h122.9h5.9h102.2v-33H366.2z" />

    <path fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" d="M358.8,82.8c11.1-4.2,18.8-14.7,18.8-27.5c0-8.5-3.4-16-8.9-21.3" />
    <path fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" d="M124.2,105.7V77c0-11.5,9.1-13.8,17.5-13.8h10.5V44.7" />
    <polyline fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" points="147.9,40.2 171.2,63.2 175.7,63.2" />
    <line fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" x1="295.1" y1="32.1" x2="275.2" y2="12.2" />
    <path fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" d="M266.2,204.7V75.9c0-8.5,5.2-12.8,13.7-12.8h18.3V44.7" />
    <polyline fill="none" class="threedline" class="threedline" stroke="#FFFFFF" stroke-width="4" points="265.9,105.2 289.2,129.2 293.7,129.2" />
    <polyline fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" points="374.2,204.7 374.2,94.2 358.8,82.8 351.2,77.2" />
    <polyline fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" points="148.2,237.2 171.2,261.2 294.6,261.2 300.5,261.2 402.2,261.2 402.2,228.2 379.2,204.2" />
    <polyline fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" points="124.2,204.7 124.2,157.2 175.7,157.2" />
    <line fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" x1="147.7" y1="228.2" x2="129.2" y2="204.2" />
    <polyline fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" points="7.2,237.3 30.2,261.2 152.2,261.2 152.2,241.7" />
    <polyline fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" points="1.9,40.2 26,63.2 39.7,63.2" />
    <line fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" x1="129.2" y1="12.2" x2="148.2" y2="33.2" />
    <line fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" x1="303.9" y1="53" x2="328.1" y2="77.2" />
    <line fill="none" class="threedline" stroke="#FFFFFF" stroke-width="4" x1="345.1" y1="10.5" x2="368.7" y2="34" />

    <!-- there -->
    <path class="there" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M76.8,337.3c0,0,1.9,12.2,13.1,12.2c22.1,0,23.8-1.8,59-66.4c-19.7,35.7-36.4,66.2-19.3,66.2c15.2,0,22.9-14.2,28.3-23.7c3.3-0.5,24-3.2,35-25.5c4-8.1,4.1-17.8-8.1-15.2c-5.6,1.2-13.1,14.8-15.7,19.2c-7.6,12.7-22.4,45.2-22.4,45.2s10.3-22.4,21.5-22.4c15.5,0-9.4,22.4,4.7,22.4c4.9,0,11.7-11.4,16.6-20.9c7.5,4.7,19.7,1.7,24.5-8.1c10.1-20.4-14.4-12.8-24.5,8.1c-5.5,11.3-2.2,21.1,11.2,21.1c16.4,0,26.1-28.3,30.5-37.5c9.9,2.5,14,2.5,22.7-1.1c-3.5,5.1-24,38.1-8.3,38.1c6.7,0,11.7-11.4,16.6-20.9c7.5,4.7,19.7,1.7,24.5-8.1c10.1-20.4-14.4-12.8-24.5,8.1c-5.5,11.3-2.2,21.1,11.2,21.1c16.4,0,20.6-4,24.7-10.5" />
    <path class="there" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M157.3,300.8c3.8-2.3-29,0.8-35.6,3.2" />
  </svg>
</div>

實現動畫

GreenSock 提供了 timeline 用於建立連續的錯開補間動畫,很好理解,動畫是按時間軸有序進行的。

藉助 DrawSVGPlugin 實現了將 SVG path 從 0% 繪製到 100% 的動畫效果。

// 建立時間軸
let tl = gsap.timeline({ repeat: -1 })

/**
 * 一、先描邊一個 Hi 字母的圓點
 * 二、而後描邊 Hi 字母
 * 三、再描邊 Hi 字母的 3D 邊框
 * 四、最後描邊 there 單詞
 */
tl
  .fromTo(
    '.hi-o',
    { drawSVG: "0%" },
    { duration: .3, drawSVG: "100%", stagger: 0.1 },
  )
  .fromTo(
    '.hi',
    { drawSVG: "0%" },
    { duration: 3, drawSVG: "100%", stagger: 0.2 },
  )
  .fromTo(
    '.threedline',
    { drawSVG: "0%" },
    { duration: 0.8, drawSVG: "100%" },
    "+=0.2"
  )
  .fromTo(
    '.there',
    { drawSVG: "0%" },
    { duration: 2, drawSVG: "100%", stagger: 0.5 }
  )

最終效果:

https://codepen.io/xiaoluobod...

參考

關於

本文是《SVG 動畫開發實戰》 系列文章第五章。

Notion 版本

小冊是在 Notion 上完成撰寫的,因此我保留了 Notion 的分享版本,你也能夠點擊這裏查看。

GitHub 版本

小冊提供了 GitHub 版本的在線閱讀體驗,傳送門

微信公衆號版本

關注個人技術公號,一樣也能夠找到此小冊系列,目前在更新中。。。

xiaoluoboding

相關文章
相關標籤/搜索