CSS & SVG foreignObject 實現文字鏤空波浪動畫

XboxYan發表於2022-03-04

之前看過一篇文章:CSS 奇技淫巧 | 妙用混合模式實現文字鏤空波浪效果,非常巧妙,將 CSS 動畫融入文字內部,主要利用了混合模式,效果是這樣的

Kapture 2022-02-24 at 11.09.36

為什麼要用到混合模式呢?因為這是文字,文字內部不可能放入 HTML 節點,所以下面介紹將另一種方式,在 HTML 完全受限的情況下,藉助 SVG foreignObject 也能很輕鬆的實現這一效果,而且會有更好的效果,一起看看吧

一、SVG foreignObject 是什麼

首先, foreignObjectSVG 中的一個元素,允許包含來自不同的XML名稱空間的元素。在瀏覽器的上下文中,一般是 XHTML / HTML。什麼意思呢?比如通常我們在各種設計軟體中匯出的 SVG 可能是這樣

<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.1785 10.9999C2.1785 6.12799..." fill="#7A7A7A"/>
</svg>

如果直接另存為 map.svg,然後放到瀏覽器中

image-20220223195703834

注意到SVG 有個屬性xmlns,這個就是名稱空間,它規定這段內容在瀏覽器中是如何渲染的,這裡的http://www.w3.org/2000/svg 就是 SVG 的名稱空間。假如沒有這個屬性,瀏覽器中就直接以普通 xml 文件渲染了,如下

image-20220223195609625

不過有一種情況可以不用指定這個名稱空間:如果我們的SVG檔案是直接內聯在XHTML頁面中或者直接以.html作為字尾名,是可以不指定名稱空間的,瀏覽器會自動識別。

image-20220223200559613

那麼,回到這裡,foreignObject 可以幹什麼呢?相信你已經猜到了,就是可以在 SVG 內部渲染 HTML 標籤!具體的做法就是,給foreignObject 內部加上 xml 的名稱空間就可以了,例如

<svg xmlns="http://www.w3.org/2000/svg">
  <foreignObject width="100%" height="100%">
      <body xmlns="http://www.w3.org/1999/xhtml">
        <style>
          p{
            color: red
          }
        </style>
        <p>xboxyan</p>
      </body>
    </foreignObject>
</svg>

注意,body標籤有一個屬性xmlns="http://www.w3.org/1999/xhtml",這個就是html的名稱空間,渲染結果如下

image-20220223201530308

和普通HTML幾乎一樣,下面重點來了,還能再加一些 CSS 動畫

<svg xmlns="http://www.w3.org/2000/svg">
  <foreignObject width="100%" height="100%">
      <body xmlns="http://www.w3.org/1999/xhtml">
        <style>
          p{
            color: red;
            animation: hue 5s infinite
          }
          @keyframes hue{
              to {
                  filter: hue-rotate(1turn)
              }
          }
        </style>
        <p>xboxyan</p>
      </body>
    </foreignObject>
</svg>

這樣就得到了一個有動畫的 SVG

Kapture 2022-02-23 at 20.24.00

一般情況下,我們都可以把 SVG 當做一種圖片,可以很方便地用在網頁中的各種地方,比如 imgsrc屬性,或者直接當做 CSS 背景圖片。簡單一句話,藉助 foreignObject,可以很輕易地將一段 HTML 轉換為圖片,包括 CSS 動畫

這樣就有趣了,很多受 HTML 結構限制的場景就可以用這種方式解決了,比如文章開頭提到的文字鏤空波浪動畫

二、文字背景圖片

在文字中嵌入背景圖是有固定套路的,藉助-webkit-background-clip裁切和透明文字,可以輕易的將任意背景置入文字中,例如

<p>CSS TEXT</p>
p{
    -webkit-background-clip: text;
    color: transparent;
      background: linear-gradient( #f44336, #ffc107);
}

效果如下

image-20220224112835213

試想一下,如果上面的波浪動畫做成了一張圖片,是不是可以直接用在這裡?

p{
    -webkit-background-clip: text;
    color: transparent;
      background: url(wave.svg);
}

三、CSS 動畫 轉 SVG 圖片

假設我們已經實現這樣一個波浪動畫(本文重點不在這),詳細原理可以參考 coco的文章純 CSS 實現波浪效果,這裡稍微做了修改

body::before, body::after {
  content: "";
  position: absolute;
  bottom: 50%;
  left: 50%;
  width: 400vw;
  height: 400vw;
  border-radius: 45% 48% 43% 47%;
  transform: translate(-50%, 0);
  box-shadow: 0 0 0 50vw #54caff9e;
  animation: rotate 10s infinite linear;
}
body::after {
  border-radius: 43% 47% 44% 48%;
  animation: rotate 10s infinite -1s linear;
}

@keyframes rotate {
  0% {
    transform: translate(-50%, 0) rotate(0);
  }
  100% {
    transform: translate(-50%, 0) rotate(360deg);
  }
}

可以得到這樣的效果

Kapture 2022-02-24 at 15.09.13

然後將這個放入一個 SVG foreignObject 中,就變成了這樣

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
  <foreignObject width="100%" height="100%">
    <style>
      html,body{
          width: 100%;
          height: 100%
      }
        body{
            margin: 0;
            background:transparent;
            transition: .3s;
        }
        body::before, body::after {
        content: '';
        position: absolute;
        bottom: 50%;
        left: 50%;
        width: 400vw;
        height: 400vw;
        border-radius: 45% 48% 43% 47%;
        transform: translate(-50%, 0);
        box-shadow: 0 0 0 50vw #54caff9e;
                animation: rotate 10s infinite linear;
      }
      body::after {
        border-radius: 43% 47% 44% 48%;
        animation: rotate 10s infinite -1s linear;
      }
      @keyframes rotate {
        0% {
          transform: translate(-50%, 0) rotate(0);
        }
        100% {
          transform: translate(-50%, 0) rotate(360deg);
        }
      }
    </style>
      <body xmlns="http://www.w3.org/1999/xhtml">
      </body>
    </foreignObject>
</svg>

將這段 SVG 另存為 wave.svg,在瀏覽器中就可以直接開啟預覽了!

Kapture 2022-02-24 at 15.18.22

這樣就得到了一張“動圖”

四、SVG 圖片的使用

回到前面的文字,直接將漸變改為這張圖就可以了

p{
      -webkit-text-stroke: 1px #333;
    -webkit-background-clip: text;
    color: transparent;
      background: url(wave.svg);
}

效果如下

Kapture 2022-02-24 at 15.27.32

當然,你也可以將這段 SVG 直接轉成 base 64 格式(推薦大家使用張鑫旭老師的 SVG 轉換工具 ,非常贊)

image-20220224153123161

轉換完就是這樣,完全沒有任何依賴了(依稀還能看得明白)

p{
      -webkit-text-stroke: 1px #333;
    -webkit-background-clip: text;
    color: transparent;
      background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3CforeignObject width='100%25' height='100%25'%3E%3Cstyle%3E@keyframes rotate%7B0%25%7Btransform:translate(-50%25,0) rotate(0)%7Dto%7Btransform:translate(-50%25,0) rotate(360deg)%7D%7Dbody%7Bwidth:100%25;height:100%25;margin:0;background:0 0;transition:.3s%7Dbody::after,body::before%7Bcontent:'';position:absolute;bottom:50%25;left:50%25;width:400vw;height:400vw;border-radius:45%25 48%25 43%25 47%25;transform:translate(-50%25,0);box-shadow:0 0 0 50vw %2354caff9e;animation:rotate 10s infinite linear;z-index:1%7Dbody::after%7Bborder-radius:43%25 47%25 44%25 48%25;animation:rotate 10s infinite -1s linear%7D%3C/style%3E%3Cbody xmlns='http://www.w3.org/1999/xhtml'/%3E%3C/foreignObject%3E%3C/svg%3E");
}

這樣也是完全沒問題的(如果預覽有誤,有可能是CSS 中的單雙引號問題,比如 content:'')

image-20220224170137009

完整程式碼可以訪問 CSS wave text (codepen.io)

五、優勢和侷限

優勢其實非常明顯,由於是圖片,屬於真正的文字鏤空效果,因此不會像混合模式那樣,容易受到背景圖層的影響(原文實現只能是白色背景),比如這裡換一種顏色

image-20220224161914981

侷限是由於變成了圖片,某些屬性樣式就固定下來了,無法動態修改,hover相關互動也失效了,也無法控制動畫的快慢等等(直接在頁面中使用 SVG 是可以的)

六、其他應用

很多 HTML 嚴格受限的情況下都可以採用這種方式來實現,比如有時候偽元素不夠了怎麼辦,完全可以用 SVG 來生成,這樣你就有無數的偽元素可用了。

另外還有一個非常重要的應用場景,純前端截圖功能,大名鼎鼎的前端截相簿rasterizeHTML就是這個原理

img

七、總結和說明

以上就是本文的全部內容了,一個還算實用的小技巧,稍微藉助了 SVG 的一點點特性,主要是為了解決 HTML 結構限制的問題,剩下的還是傳統 CSS 相關,這裡總結一下

  1. SVG foreignObject 可以嵌入 HTML 標籤
  2. 注意 SVG 和 HTML 的名稱空間,HTML 中會自動識別
  3. 文字背景圖片用 -webkit-background-clip 結合透明文字實現
  4. SVG 本質就是一種圖片
  5. SVG 可以轉義成內聯 base 64,不影響原有動畫
  6. SVG 在用作圖片時,不支援動態修改樣式
  7. SVG foreignObject 可以實現截圖功能

很多時候,如果有圖片相關需求時,都可以朝 SVG 的方向思考,畢竟 SVG 是一門影像語言,關於 SVG 有用的特性還有很多很多,後面會慢慢介紹的。最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤

相關文章