之前看過一篇文章:CSS 奇技淫巧 | 妙用混合模式實現文字鏤空波浪效果,非常巧妙,將 CSS 動畫融入文字內部,主要利用了混合模式,效果是這樣的
為什麼要用到混合模式呢?因為這是文字,文字內部不可能放入 HTML 節點,所以下面介紹將另一種方式,在 HTML 完全受限的情況下,藉助 SVG foreignObject 也能很輕鬆的實現這一效果,而且會有更好的效果,一起看看吧
一、SVG foreignObject 是什麼
首先, foreignObject 是 SVG
中的一個元素,允許包含來自不同的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,然後放到瀏覽器中
注意到SVG
有個屬性xmlns
,這個就是名稱空間,它規定這段內容在瀏覽器中是如何渲染的,這裡的http://www.w3.org/2000/svg
就是 SVG
的名稱空間。假如沒有這個屬性,瀏覽器中就直接以普通 xml
文件渲染了,如下
不過有一種情況可以不用指定這個名稱空間:如果我們的SVG檔案是直接內聯在XHTML頁面中或者直接以.html
作為字尾名,是可以不指定名稱空間的,瀏覽器會自動識別。
那麼,回到這裡,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
的名稱空間,渲染結果如下
和普通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
一般情況下,我們都可以把 SVG 當做一種圖片,可以很方便地用在網頁中的各種地方,比如 img
的src
屬性,或者直接當做 CSS 背景圖片。簡單一句話,藉助 foreignObject
,可以很輕易地將一段 HTML 轉換為圖片,包括 CSS 動畫。
這樣就有趣了,很多受 HTML 結構限制的場景就可以用這種方式解決了,比如文章開頭提到的文字鏤空波浪動畫
二、文字背景圖片
在文字中嵌入背景圖是有固定套路的,藉助-webkit-background-clip
裁切和透明文字,可以輕易的將任意背景置入文字中,例如
<p>CSS TEXT</p>
p{
-webkit-background-clip: text;
color: transparent;
background: linear-gradient( #f44336, #ffc107);
}
效果如下
試想一下,如果上面的波浪動畫做成了一張圖片,是不是可以直接用在這裡?
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);
}
}
可以得到這樣的效果
然後將這個放入一個 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
,在瀏覽器中就可以直接開啟預覽了!
這樣就得到了一張“動圖”
四、SVG 圖片的使用
回到前面的文字,直接將漸變改為這張圖就可以了
p{
-webkit-text-stroke: 1px #333;
-webkit-background-clip: text;
color: transparent;
background: url(wave.svg);
}
效果如下
當然,你也可以將這段 SVG 直接轉成 base 64 格式(推薦大家使用張鑫旭老師的 SVG 轉換工具 ,非常贊)
轉換完就是這樣,完全沒有任何依賴了(依稀還能看得明白)
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:'')
完整程式碼可以訪問 CSS wave text (codepen.io)
五、優勢和侷限
優勢其實非常明顯,由於是圖片,屬於真正的文字鏤空效果,因此不會像混合模式那樣,容易受到背景圖層的影響(原文實現只能是白色背景),比如這裡換一種顏色
侷限是由於變成了圖片,某些屬性樣式就固定下來了,無法動態修改,hover
相關互動也失效了,也無法控制動畫的快慢等等(直接在頁面中使用 SVG 是可以的)
六、其他應用
很多 HTML 嚴格受限的情況下都可以採用這種方式來實現,比如有時候偽元素不夠了怎麼辦,完全可以用 SVG 來生成,這樣你就有無數的偽元素可用了。
另外還有一個非常重要的應用場景,純前端截圖功能,大名鼎鼎的前端截相簿rasterizeHTML就是這個原理
七、總結和說明
以上就是本文的全部內容了,一個還算實用的小技巧,稍微藉助了 SVG 的一點點特性,主要是為了解決 HTML 結構限制的問題,剩下的還是傳統 CSS 相關,這裡總結一下
- SVG foreignObject 可以嵌入 HTML 標籤
- 注意 SVG 和 HTML 的名稱空間,HTML 中會自動識別
- 文字背景圖片用 -webkit-background-clip 結合透明文字實現
- SVG 本質就是一種圖片
- SVG 可以轉義成內聯 base 64,不影響原有動畫
- SVG 在用作圖片時,不支援動態修改樣式
- SVG foreignObject 可以實現截圖功能
很多時候,如果有圖片相關需求時,都可以朝 SVG 的方向思考,畢竟 SVG 是一門影像語言,關於 SVG 有用的特性還有很多很多,後面會慢慢介紹的。最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤