SVG 動畫的排版小陷阱:讓線條和箭頭同步出現

最近在處理 SVG 動畫時,遇到了一個有趣的畫面瑕疵。當我們使用 path 標籤搭配 marker-end 來加上箭頭時,瀏覽器展現動畫的行為跟我們預期的有點落差。

這篇文章來記錄這個問題的根本原因,以及如何透過簡單的技巧完美解決。

問題是甚麼

在網頁中,如果我們寫了 path 結合 marker-end 這樣的語法,本意是希望路徑在畫完的時候,終點才出現這個箭頭。

但是實際執行動畫時,只要路徑開始顯現,也就是透明度變成 1 的那一個瞬間,瀏覽器就會在終點立刻把這個箭頭渲染出來。

這會導致一個很不協調的現象:線條還在慢慢從起點畫過來,但終點已經提早立好了一個箭頭。

為什麼會這樣

這是因為 SVG 的 marker-end 本質上就是一個綁定在路徑端點上的裝飾元件。

當我們使用 stroke-dashoffset 來控制線條慢慢畫出來時,路徑只要一有顯示的跡象,瀏覽器就會認為端點已經存在,於是提前把裝飾的箭頭畫上去,沒有辦法隨著線條進度同步延遲出現。

解決方案

為了讓箭頭配合線條畫到的時間點才出現,我們需要分開處理路徑和箭頭。

第一步是移除 SVG 的 marker-end 屬性,這樣可以避免瀏覽器在路徑終點預先繪製箭頭。

第二步是獨立一個箭頭元件出來,這裡使用 polygon 標籤來當作獨立的箭頭,並且根據路徑終點的座標以及切線斜率來旋轉對齊端點。

程式碼實作

調整過後的程式碼像這樣:

HTML

<svg class="marathon-svg" viewBox="0 0 1400 260" xmlns="http://www.w3.org/2000/svg">
    <!-- Track path -->
    <path id="track" d="M80,150 Q280,40 480,120 Q680,240 880,140 Q1080,50 1280,150 L1360,180" stroke="#C8922A" stroke-width="6" stroke-linecap="round" fill="none" stroke-dasharray="1360" stroke-dashoffset="1360" style="
            animation: dashDraw 3s ease forwards;
            --dash-len: 1360;
            animation-delay: calc(var(--scene-t,0s) * -1 + 9s);
            animation-play-state: paused;
            "></path>
    <!-- Arrowhead that appears when drawing reaches the end (at 11.5s) -->
    <polygon points="0 -9, 22 0, 0 9" fill="#C8922A" transform="translate(1360, 180) rotate(20.56)" style="
            opacity: 0;
            animation: fadeIn 0.3s ease forwards;
            animation-delay: calc(var(--scene-t,0s) * -1 + 11.5s);
            animation-play-state: paused;
            "></polygon>
</svg>

透過把箭頭拉出來獨立控制,並且設定好對應的 animation-delay 讓它在時間點剛好對上線條畫完的一刻,就能解決箭頭提前出現的不協調感。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *