歡迎關注我的公眾號:前端偵探
再次介紹一些你可能沒用過的SVG小技巧。
有時候會遇到一些完全相同的圖形,如果能用上 CSS背景平鋪,那就再合適不過了。舉個例子,有這樣一個按鈕
相比普通的按鈕,多個左右兩個小裝飾,如果是你,會怎樣實現呢?
假設這個小圖標是a.svg,想了一下,應該有以下幾種方式
1.偽元素
剛好用上::before和::after,設置相同的背景就行了,示意如下
button::before,
button::after{
content:'';
background: url(a.svg)
}
2. 多重背景
利用 CSS背景可以疊加的特性,設置兩個背景就可以了,分別定位,示意如下
button{
background: url(a.svg) 10px center no-repeat,url(a.svg) right 10px top center no-repeat
}
這兩種方式都是不錯的方式,但是感覺還是有些浪費,畢竟把兩個相同的圖案重複寫了兩遍。
為啥不能直接用背景平鋪呢?因為無法直接設置平鋪的間隔,就像這樣
這時,如果能充分發揮SVG的特性,就可以僅僅使用平鋪的方式來實現我們想要的效果了,一起看看吧~
一、SVG的自適應和viewBox
就以上面的小圖標為例,從 Figma中可以複製這段svg,如下
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.021 8.028L15 8.027c-.262-.26-.876-.747-2.008-.701.13-.314.027-1.204-.676-1.227.179.574-.803.974-1.361 1.155-.484.156-1.025.344-1.16.622l-.437-.003.002-.43-.276-.001-.004.428-.25-.002c-.028-.022-.058-.047-.091-.07a.643.643 0 0 0-.316-.29.663.663 0 0 0-.21-.22 3.071 3.071 0 0 0-.109-.157l.002-.21.43.004.003-.277-.43-.004.002-.436c.283-.127.478-.667.644-1.152.19-.555.606-1.53 1.18-1.341-.013-.704-.902-.822-1.216-.695.063-1.135-.417-1.754-.67-2.02V.977L8.036.99 8.028.977l-.001.02c-.26.263-.747.877-.701 2.009-.314-.13-1.204-.027-1.228.676.574-.179.974.803 1.155 1.362.157.484.345 1.025.622 1.159l-.003.437-.43-.002v.277l.427.003-.002.25c-.022.028-.046.058-.069.091a.643.643 0 0 0-.29.317.664.664 0 0 0-.222.21 3.07 3.07 0 0 0-.156.108l-.21-.002.004-.43-.276-.002-.004.43-.436-.002c-.127-.283-.667-.478-1.152-.644-.555-.19-1.53-.606-1.341-1.18-.704.013-.822.902-.695 1.216-1.135-.063-1.754.417-2.02.67H.977l.012.012-.012.009.02.001c.263.26.877.747 2.009.702-.13.314-.027 1.203.676 1.227-.179-.574.803-.974 1.362-1.155.484-.157 1.024-.345 1.159-.622l.437.003-.002.43h.277l.003-.427.25.002c.028.022.058.046.091.069a.643.643 0 0 0 .317.29c.058.101.135.172.21.222.04.06.077.114.108.156l-.002.21-.43-.004-.002.276.43.004-.002.437c-.283.127-.478.666-.644 1.15-.19.556-.606 1.532-1.18 1.342.013.705.902.822 1.216.696-.063 1.134.417 1.753.67 2.02v.022l.012-.011c.004.003.006.007.009.012l.001-.021c.26-.263.747-.877.702-2.009.314.13 1.203.027 1.227-.676-.574.179-.974-.803-1.155-1.362-.157-.483-.345-1.024-.622-1.159l.003-.437.43.002v-.277l-.427-.003.002-.25c.022-.028.046-.058.069-.091a.643.643 0 0 0 .29-.317.664.664 0 0 0 .222-.21c.06-.04.114-.077.156-.108l.21.002-.004.43.276.002.004-.43.437.002c.127.283.666.478 1.15.644.556.19 1.532.606 1.342 1.18.705-.013.823-.902.696-1.216 1.134.063 1.753-.417 2.02-.67h.022a1.093 1.093 0 0 1-.011-.012c.003-.004.007-.006.012-.009h-.002z" fill="#000"/>
</svg>
嗯,看着非常亂,沒關係,我們不必關注裏面的細節。
我們直接放在 html 中來展示這段svg,效果是這樣的
因為這段 svg有自帶的尺寸,最後展示的就是16 * 16的大小。
如果我們手動改變這個svg的尺寸呢?為了方便觀察,我們給svg添加一個邊框,如下
svg{
width: 200px;
height: 100px;
outline: 1px dashed;
}
你猜會是什麼樣的?
下面有 3 個選項
思考一分鐘...
🤔
🤔
🤔
🤔
🤔
🤔
🤔
🤔
🤔
思考完成,答案是A,你猜對了嗎
為什麼會這樣呢?有點類似於object-fit:contain的效果。其實這是viewBox造成的,viewBox會按照尺寸等比放大從而鋪滿整個svg。
有關 viewBox的更多介紹,可以參考張鑫旭老師的這篇文章:理解SVG viewport,viewBox,preserveAspectRatio縮放
如果去除viewBox,svg內部該是什麼樣就是什麼樣,也就是仍然是16*16的大小
<!--去除viewBox屬性-->
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg">
...
</svg>
結果如下
也就是説,在去除viewBox之後,無論svg尺寸是多少,裏面的圖標都不會變化。
看似好像沒什麼用?🤔
其實用處可大了,可以讓背景以我們想要的方式平鋪,那就來看接下來的應用
二、自適應尺寸與背景平鋪
現在來簡單實現文章開頭所示的按鈕效果。html很簡單,就一個標籤
<button>召喚卡牌</button>
然後簡單裝飾一下,繪製圓角和背景
button{
border: 0;
outline: 0;
padding: 8px 36px;
font-size: 12px;
line-height: 16px;
border-radius: 30px;
color: #FFEFDB;
background: #FF2A2A;
}
效果如下
接下來,我們要用上前面的那段svg,先去除viewBox屬性,為了能夠通過背景尺寸控制svg大小,我們可以將svg的尺寸改成100%
這個技巧在上一篇也有提到過:不一樣的SVG!SVG 漸變邊框在 CSS 中的應用
示意如下
<svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg">
...
</svg>
然後將這段svg轉換成css內聯格式,推薦用張鑫旭老師的在線轉換工具
SVG在線壓縮合並工具
可以得到這樣一段
--icon: url("data:image/svg+xml,%3Csvg width='16' height='16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.021 8.028L15 8.027c-.262-.26-.876-.747-2.008-.701.13-.314.027-1.204-.676-1.227.179.574-.803.974-1.361 1.155-.484.156-1.025.344-1.16.622l-.437-.003.002-.43-.276-.001-.004.428-.25-.002c-.028-.022-.058-.047-.091-.07a.643.643 0 0 0-.316-.29.663.663 0 0 0-.21-.22 3.071 3.071 0 0 0-.109-.157l.002-.21.43.004.003-.277-.43-.004.002-.436c.283-.127.478-.667.644-1.152.19-.555.606-1.53 1.18-1.341-.013-.704-.902-.822-1.216-.695.063-1.135-.417-1.754-.67-2.02V.977L8.036.99 8.028.977l-.001.02c-.26.263-.747.877-.701 2.009-.314-.13-1.204-.027-1.228.676.574-.179.974.803 1.155 1.362.157.484.345 1.025.622 1.159l-.003.437-.43-.002v.277l.427.003-.002.25c-.022.028-.046.058-.069.091a.643.643 0 0 0-.29.317.664.664 0 0 0-.222.21 3.07 3.07 0 0 0-.156.108l-.21-.002.004-.43-.276-.002-.004.43-.436-.002c-.127-.283-.667-.478-1.152-.644-.555-.19-1.53-.606-1.341-1.18-.704.013-.822.902-.695 1.216-1.135-.063-1.754.417-2.02.67H.977l.012.012-.012.009.02.001c.263.26.877.747 2.009.702-.13.314-.027 1.203.676 1.227-.179-.574.803-.974 1.362-1.155.484-.157 1.024-.345 1.159-.622l.437.003-.002.43h.277l.003-.427.25.002c.028.022.058.046.091.069a.643.643 0 0 0 .317.29c.058.101.135.172.21.222.04.06.077.114.108.156l-.002.21-.43-.004-.002.276.43.004-.002.437c-.283.127-.478.666-.644 1.15-.19.556-.606 1.532-1.18 1.342.013.705.902.822 1.216.696-.063 1.134.417 1.753.67 2.02v.022l.012-.011c.004.003.006.007.009.012l.001-.021c.26-.263.747-.877.702-2.009.314.13 1.203.027 1.227-.676-.574.179-.974-.803-1.155-1.362-.157-.483-.345-1.024-.622-1.159l.003-.437.43.002v-.277l-.427-.003.002-.25c.022-.028.046-.058.069-.091a.643.643 0 0 0 .29-.317.664.664 0 0 0 .222-.21c.06-.04.114-.077.156-.108l.21.002-.004.43.276.002.004-.43.437.002c.127.283.666.478 1.15.644.556.19 1.532.606 1.342 1.18.705-.013.823-.902.696-1.216 1.134.063 1.753-.417 2.02-.67h.022a1.093 1.093 0 0 1-.011-.012c.003-.004.007-.006.012-.009h-.002z' fill='%23FFEFDB'/%3E%3C/svg%3E")
接着,將這段svg背景放到按鈕中
button{
background: var(--icon) #FF2A2A;
}
效果如下
很明顯,此時這個svg圖標背景默認尺寸是充滿整個容器的,也就是100% * 100%。如果希望右邊也出現平鋪一個圖標,可以減小背景尺寸,比如
button{
background: var(--icon) #FF2A2A;
background-size: calc(100% - 48px)
}
效果如下
然後改變水平位置
button{
background: var(--icon) #FF2A2A;
background-size: calc(100% - 48px);
background-position: 16px;
}
效果如下
左右已經出現圖標了,現在只需要垂直居中就可以了,這個也很好實現,設置尺寸為圖標本身大小,然後改變背景位置就行了
button{
background: var(--icon) #FF2A2A;
background-size: calc(100% - 48px) 16px;
background-position: 16px center;
}
效果如下
垂直方向也平鋪了,所以還需要改變一下平鋪方式,僅限水平方向
button{
background: var(--icon) #FF2A2A;
background-size: calc(100% - 48px) 16px;
background-position: 16px center;
background-repeat: repeat-x;
}
這樣就僅僅使用平鋪完成了想要的效果!
是不是有些不可思議?下面是一個示意圖,紫色圓圈代表圖標,紫色邊框代表svg尺寸,也就是背景尺寸,設置為100% - 36px後,水平方向的平鋪就可以顯示兩個圓圈了,然後適當移動背景位置,就可以看到左右兩邊的圖標了。
當然這種實現也是完全是自適應的,無論什麼尺寸都可以完美適配
完整代碼可以查看以下鏈接:
- CSS & SVG repeat button (codepen.io)')
三、有時候可替代徑向漸變
之前寫過這樣一篇文章:CSS 實現優惠券的技巧 ,講述了非常多的優惠券繪製技巧,非常巧妙。
裏面有提到用徑向漸變的方式來繪製內凹圓角,但是漸變一直都是一個非常難學的技巧,語法非常多,一般同學表示接受不了。
這裏就採用 SVG來代替徑向漸變的方式來實現這樣的優惠券效果
首先,我們用設計軟件隨便畫個圓,這裏以Figma為例,然後複製出SVG
其實就是這樣一段代碼
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="20" cy="20" r="20" fill="#FF336F"/>
</svg>
非常簡單吧
然後去除viewBox,並且設置寬高為100%,這是為了讓SVG畫布尺寸撐滿整個容器
<svg width="100%" height="100%" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="20" cy="20" r="20" fill="#FF336F"/>
</svg>
接着轉換成CSS內聯格式
--icon: url("data:image/svg+xml,%3Csvg width='100%' height='100%' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='20' cy='20' r='20' fill='%23FF336F'/%3E%3C/svg%3E")
然後用在 CSS 背景中
div{
background: var(--icon);
}
目前效果是這樣的
我們需要將這個圓分割到4個角落上,可以直接用背景平移的方式
div{
background: var(--icon);
background-position: -20px -20px;
}
這樣就可以了,演示如下
不過這樣還沒完,我們需要實現的反向圓角,所以這裏需要用遮罩的方式,減去 4 個角落
關於遮罩,之前在多篇文章中都有提到,有興趣可以回顧一下
- 一個有意思的CSS圖片hover效果
- CSS 如何實現羽化效果?
- 別用圖片了,CSS 遮罩合成實現帶圓角的環形 loading 動畫
- CSS mask 實現鼠標跟隨鏤空效果
- CSS 實現Chrome標籤欄的技巧
- CSS 實現優惠券的技巧
回到這裏,我們只需要一個完整的背景,減去剛才的圓角就可以了,具體實現如下
.coupon{
background: linear-gradient(red,orange);
-webkit-mask:linear-gradient(red,red), var(--icon);
-webkit-mask-position: -20px -20px;
-webkit-mask-composite: xor;
}
效果如下,其實和前面幾乎一致
是不是沒有用到徑向漸變?
完整代碼可以查看以下鏈接:
- CSS & SVG coupon (codepen.io)')
四、SVG還可以更靈活
有時候徑向漸變還是有很多侷限的,複雜的圖形繪製不了或者成本很高。而 SVG就沒有這樣的限制了,如果能有一定的自適應特性,相信可以更方便的解決問題。
比如這樣一個帶圓角的自適應聚焦框,可能在大屏可視化比較常見,如下(CSS表示無能為力😭)
除了使用border-image實現以外,還可以採用背景平鋪來實現
要實現背景平鋪,首先要考慮,哪個是平鋪最小單元?
思考一分鐘...
🤔
🤔
🤔
🤔
🤔
🤔
🤔
🤔
🤔
思考完成,答案還是A,你猜對了嗎?
為什麼是A呢?其實要從全局視野來觀察,從全局來看,其實是由4個半圓弧組合而成,示意如下
那麼,如何用 CSS背景平鋪來實現呢?
思路是一致的,首先從設計稿把這段svg複製下來
得到這樣一個片段
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M30 2C30 0.895431 29.1046 0 28 0C26.8954 0 26 0.895431 26 2V10C26 18.8366 18.8366 26 10 26H2C0.895431 26 0 26.8954 0 28C0 29.1046 0.895431 30 2 30C0.895431 30 0 30.8954 0 32C0 33.1046 0.895431 34 2 34H10C18.8366 34 26 41.1634 26 50V58C26 59.1046 26.8954 60 28 60C29.1046 60 30 59.1046 30 58C30 59.1046 30.8954 60 32 60C33.1046 60 34 59.1046 34 58V50C34 41.1634 41.1634 34 50 34H58C59.1046 34 60 33.1046 60 32C60 30.8954 59.1046 30 58 30C59.1046 30 60 29.1046 60 28C60 26.8954 59.1046 26 58 26H50C41.1634 26 34 18.8366 34 10V2C34 0.895431 33.1046 0 32 0C30.8954 0 30 0.895431 30 2ZM50 30C38.9543 30 30 38.9543 30 50C30 38.9543 21.0457 30 10 30C21.0457 30 30 21.0457 30 10C30 21.0457 38.9543 30 50 30Z" fill="#FF336F"/>
</svg>
然後去除viewBox,並且設置寬高為100%
<svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg">
...
</svg>
接着轉換成CSS內聯格式
--icon: url("data:image/svg+xml,%3Csvg width='100%' height='100%' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M30 2a2 2 0 1 0-4 0v8c0 8.837-7.163 16-16 16H2a2 2 0 1 0 0 4 2 2 0 1 0 0 4h8c8.837 0 16 7.163 16 16v8a2 2 0 1 0 4 0 2 2 0 1 0 4 0v-8c0-8.837 7.163-16 16-16h8a2 2 0 1 0 0-4 2 2 0 1 0 0-4h-8c-8.837 0-16-7.163-16-16V2a2 2 0 1 0-4 0zm20 28c-11.046 0-20 8.954-20 20 0-11.046-8.954-20-20-20 11.046 0 20-8.954 20-20 0 11.046 8.954 20 20 20z' fill='%23FF336F'/%3E%3C/svg%3E")
最後設置為CSS背景
div{
background: var(--icon);
}
效果如下
此時無論容器尺寸是多少,這個背景位於左上角。
最後只需要改變一下背景的位置,設置負的偏移量,就可以平鋪到 4 個角落了
div{
background: var(--icon) #eee;
background-position: -30px -30px;/*圖案的一半*/
}
動態示意如下
是不是非常簡單的實現?
完整代碼可以查看以下鏈接:
- CSS & SVG coner (codepen.io)')
五、總結一下
以上就是本文的全部內容了,一個成本非常小的 SVG小技巧,僅僅需要小小的改動,就能讓SVG自適應背景平鋪,如下
<!--去除viewBox屬性,並設置寬高100%-->
<svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg">
...
</svg>
你學到了嗎?下面總結一下實現要點
SVG默認會根據viewBox填充整個畫布,有點類似於object-fit:contain的效果- 去除
viewBox後,無論svg尺寸是多少,裏面的內容大小都不會變化 - 利用這個特性,可以通過設置背景尺寸的方式,讓背景以我們想要的方式平鋪
- 有時候可替代徑向漸變,畢竟
SVG比漸變還是容易很多 - 複雜的圖形漸變繪製不了或者成本很高,SVG還能更靈活
最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發 ❤❤❤