復刻解析一個流光溢彩炫到掉渣的 CSS 動畫按鈕

語言: CN / TW / HK

持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第14天,點選檢視活動詳情

最近在看 next.js 官網是被引流到 conf 頁面,發現上面有一個炫酷的按鈕,按鈕的邊框色彩不斷變動給人感覺是光在隨著按鈕旋轉一般,感覺挺酷的,復刻一下講解下原理。

不想看的可以直接跳到最後看碼上掘金演示。

結構復刻

先復刻下結構,原版有非常多的 DOM 節點,而且按鈕什麼的都是通過定位去放到框裡的,很容易在元素偏移時導致按鈕跑出邊框,咱復刻歸復刻,順路給他優化優化:

```html

```

按鈕中間空空蕩蕩會影響觀感,所以我連內部的頭像、文字、圖示就一起復刻了。原版邊框和按鈕為同級,我這裡改造下將按鈕嵌入進去,所以後續程式碼與原版並不一致,但是自適應程度會好很多,不需要寫死寬高、固定定位。

流光復刻

外層的樣式隨便寫一下,為了讓光彩更漂亮我們背景一樣選擇黑色,畢竟大白天的極光哪有晚上看起來好看。

```css :root, body { height: 100%; background-color: #000; }

.shining-gradient-svg_wrapper { display: flex; align-items: center; justify-content: center; position: relative; height: 100%; } ```

還有按鈕的樣式就隨便抄一下,就一些 border-radiusflex 啥的,這裡不貼了。

研究了下他的邊框流光效果,發現其實並不是 borderoutline 一類的東西,而是 background,然後通過 -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); 使用 mask 將內容區域進行遮擋,導致看起來只剩下一個邊框。

```css .shining-gradient-svg_wrapper { --full-gradient: radial-gradient(#c42d01 0%, #c42d01 10%, #fcf26e 40%, #00e754 60%, #00eef4 70%, #0070f3 100%); }

.shining-gradient-svg_gradient:before { content: ''; position: absolute; z-index: -1; inset: calc(var(--b) * -1); height: 100%; width: 100%; background: var(--full-gradient); background-size: 300% 300%; padding: var(--b); border-radius: 32px; -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); -webkit-mask-composite: xor; mask-composite: exclude; } ```

.shining-gradient-svg_gradient 使用 before 偽元素,將其絕對定位,再通過 insetpadding 將其面積擴大,這裡用了一個變數,方便控制溢位背景的大小。然後使用漸變背景,並將尺寸擴大到 3 倍。

再通過 mask-compositemask 進行背景遮照,就可以將漸變背景變得只剩下邊框。不過這倆屬性相容並不佳,其實有挺多其它方案來實現相同的效果,比如直接設定按鈕區域的背景色,由於按鈕區域層級在偽元素之上,中間的背景色直接就被覆蓋了。

如果去掉背景擷取,我們可以看到效果是這樣:

blog-cool-gradient-next-js-conf-btn-2.png

感覺還不錯的樣子。核心樣式我們已經復刻完成了,我們再來加上動畫。要製造出光線移動的效果,我們只需要慢慢的移動背景,由於漸變色顏色過渡都是循序漸進的,這樣在只剩下邊框時,就會有一種邊框色在流動的感覺。

```css @keyframes shining-gradient-svg_translateGradient { 0% { background-position: -20% -20%; }

25% {
    background-position: 30% 80%;
}

50% {
    background-position: 110% 110%;
}

75% {
    background-position: 80% 30%;
}

to {
    background-position: -20% -20%;
}

} .shining-gradient-svg_gradient:before { animation: shining-gradient-svg_translateGradient var(--animation-speed) linear infinite; will-change: background-position; } ```

定義一下 keyframes,之前寫過很多篇關於動畫內容的,這裡就不解釋 keyframesanimation 中的屬性了,此處可以加上 will-change,可以讓瀏覽器提前知道哪些屬性會變動,能有有利於瀏覽器效能。

演示

某些螢幕邊框圓角顯示效果較差,為了效果更明顯,將邊框設定為了 2px。

https://code.juejin.cn/pen/7153240601821970463

總結

藉助漸變背景色和背景移動動畫,我們可以製造出這種邊框光在邊框中流動的感覺,感覺還是蠻有趣的。果然動畫創意才是精髓。