tag: Web; JavaScript; SVG; DOM; 動畫 SVGhtml
最近在項目中遇到了「帶動畫 SVG 圖標」與 「image」標籤結合使用的場景,使用過程當中發現水仍是有點深,所以整理出來,供有類似場景的童鞋以參考。git
咱們這裏有一個的帶動畫 SVG 文件 github
這是一個水波紋效果的 SVG,動畫時長是固定的,可是咱們但願不一樣的標記動畫的播放時長能夠略有不一樣,進而產生錯落交錯的感受。期待效果以下:web
其中控制動畫時長的屬性是寫死在 SVG 文件中的(animate 標籤的 dur 屬性)。SVG 部份內容以下:正則表達式
<circle cx="22" cy="22" r="6" stroke-opacity="0">
<animate attributeName="r" begin="1.5s" dur="3s" values="6;22" calcMode="linear" repeatCount="indefinite" />
<animate attributeName="stroke-opacity" begin="1.5s" dur="3s" values="1;0" calcMode="linear" repeatCount="indefinite" />
</circle>
複製代碼
另外受限於組件要求,僅能使用 image
標籤進行加載 SVG。所以只能從 image.src 屬性做爲突破入口。瀏覽器
先肯定基本思路:在 SVG 被插入到 image 標籤前,修改 SVG 文件中動畫 animate 標籤的屬性 dur,以達到修改動畫時間的目的。app
那麼咱們先來看下 image 加載 SVG 文件幾種的方式。dom
<img src="./assets/rings.svg">
複製代碼
這種方式加載出來的 SVG 內容無法被 JS 獲取到,更不要提修改屬性,所以該方案 放棄。svg
這種方式中,依靠的是 image 標籤支持 base64 和 Blob 類型 URL 的特性。測試
考慮能夠先將 SVG 文件轉換爲對應的字符串,而後經過正則表達式將動畫屬性修改,而後傳入 src 中來實現。不過這種方式須要編寫複雜的正則表達式,而且字符串形式的 SVG 內容可讀性較差,所以這種方式也 不是最優的。
若是能夠先以 dom 形式修改 SVG 的動畫屬性,再將 dom 轉換爲字符串,最後再將字符串轉換爲 base64 或者 Blob 類型,就能夠實現以上的需求了。
看起來這個思路靠譜,那麼就按照這個方向繼續探索。
獲取 SVG 的方式有不少,但獲取方式不在本文重點,故只給出原生 JS 實現方式。
const xhr = new XMLHttpRequest();
xhr.addEventListener('load', () => {
const resXML = xhr.responseXML;
const svgDom = resXML.documentElement.cloneNode(true);
});
xhr.open('GET', './rings.svg');
xhr.send();
複製代碼
上文獲取的 svgDom 就是可操做的 dom 節點,接下來和操做 dom 同樣來操做它。
// 獲取 svg 中的 animate 標籤,使用 setAttribute 進行修改 dur、begin 等屬性,如下代碼僅爲示例
// 獲取 animation 節點
const ani = svgDom.children[0];
// 修改節點上的動畫時長 dur 屬性
ani.setAttribute('dur', Math.random() + 2 + 's');
複製代碼
接來下便須要將 dom 轉換成字符串:經過 XMLSerializer
將 XML 轉換成 String 類型。
const svgStr = new XMLSerializer().serializeToString(svgDom);
複製代碼
可是上面的 svgStr 是無法直接傳給 image.src 的,須要將其轉換爲 image 能夠解析的 base64 或者 Blob URL 類型。
這裏我選擇了 Blob 類型進行轉換,先用 new Blob([svgStr])
轉換成 Blob 類型,再經過 URL.createObjectURL(blob)
方法將字符串轉換成 Blob
類型 URL 傳入。
const blob = new Blob([svgStr], {
type: 'image/svg+xml'
});
const blobStr = URL.createObjectURL(blob);
const template = `<img src="${blobStr}">`;
// 最後插入模板
複製代碼
固然除了使用 Blob 數據類型外,咱們也能夠使用window.btoa()
方法將 svgStr 轉換爲 base64 類型傳入。
const base64 = window.btoa(svgStr);
const template = `<img src="data:image/svg+xml;base64,${base64}">`
// 最後插入模板
複製代碼
經過「讀入 SVG -> 修改屬性 -> 轉換 URL -> 傳入 image」 這樣一個流程,最終實現了咱們的預期。
以上思路但願對有相似場景的同窗們有所啓發。 最後強調的是使用環境爲 Chrome 瀏覽器,其餘瀏覽器未作測試。
XMLHttpRequest
SVG Animate
XMLSerializer
Blob
URL.createObjectURL()
Using object URLs to display images
window.btoa()