1. 程式人生 > 其它 >【轉】CSS 實現按鈕點選動效的套路

【轉】CSS 實現按鈕點選動效的套路

前言

在 Web 中,大部分按鈕可能都是平平無奇的,有時候為了強調品牌特殊或者滿足特殊功能,可能需要給按鈕新增一點點選動效。比如,用過 Ant Design 的小夥伴應該都能發現,在點選按鈕的時候會有一個很微妙的水波動畫

這就非常有特色了,看到這樣的按鈕自然會聯絡上 Ant Design 。

動畫過程其實不復雜,看了一下官方的實現,是通過 js 動態更改屬性實現的,在點選的時候,改變屬性,觸發動畫,當動畫結束之後,再將該屬性還原(還原是為了保證下次點選仍然有動畫),如下

看著好像有點麻煩?其實,這種效果也是可以純 CSS 實現的,而且還能實現其他更多有趣的效果

一起看看吧~

一、CSS 過渡動畫

通常 CSS 中實現動畫有兩種思路,transitionanimation。一般而言,簡單的、需要主動觸發(:hover:active或者動態切換類名等)的可以用transition實現,其他的都可以用animation

回到這個例子,動畫足夠簡單了,就兩個變化,而且需要主動觸發(這裡是點選,可以想到:active),所以優先考慮用transition來實現。

觀察整個動畫,其實就是兩個效果疊加而成

  1. 陰影不斷擴大
  2. 透明度不斷降低

那麼,這個動畫(過渡)的兩種狀態可以這樣來表示

/* 初始狀態 */
button{
  opacity: .4;
  transition: .3s;
}
/* 擴散狀態 */
button{
  box-shadow: 0 0 0 6px var(--primary-color);
  opacity: 0;
}

嗯,兩種狀態的樣式都寫好了,怎麼觸發點選呢?

二、CSS 點選動畫

先完善一下基本樣式,假設 HTML 結構如下

<button class="button">Default</button>

簡單美化一下

:root{
  --primary-color: royalblue;
}
.button{
  padding: 5px 16px;
  color: #000000d9;
  border: 1px solid #d9d9d9;
  background-color: transparent;
  border-radius: 2px;
  line-height: 1.4;
  box-shadow: 0 2px #00000004;
  cursor: pointer;
  transition: .3s;
}
.button:hover{
  color: var(--primary-color);
  border-color: currentColor;
}

然後新增陰影擴散動畫,為了方便透明度的控制,這裡用::after偽類單獨渲染

.button::after{
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  opacity: 0.4;
  transition: .3s;
}

如果按照正常的思路通過:active來觸發過渡動畫,可能會這樣來實現

.button:active::after{
  box-shadow: 0 0 0 6px var(--primary-color);
  opacity: 0;
}

效果如下:

嗯,好像不大對勁?接著往下看

三、CSS 過渡重置

為什麼會有上面這種現象呢?這裡提一下:active:active只有在滑鼠按下時才會起作用,通常在點選一個按鈕時,都是輕輕地點選,而不是長按,如果在:active上新增動畫,那麼在滑鼠抬起的時候,動畫一般都沒有結束,所以會導致在滑鼠抬起的時候,動畫馬上就停止了,如果是transition,還會有一個“回退”的過渡效果。

那麼,有沒有什麼方法可以只在滑鼠抬起的時候產生動畫呢?

我的實現是這樣的,假設預設就是有陰影(透明度為0)的狀態,在:active的時候迅速去除陰影(這裡的“迅速”,是指取消按下去的過渡動畫),然後由於預設是有過渡的,所以滑鼠抬起的時候陰影就回退到有陰影的狀態了,這樣可以保證按下去是沒有動畫的,抬起來觸發過渡動畫

整個流程其實是這樣:

取消過渡動畫也很簡單,設定時長為 0 就行了,程式碼實現就是這樣

.button::after{
  /*其他樣式*/
  opacity: 0;
  box-shadow: 0 0 0 6px var(--primary-color);
  transition: .3s;
}
/*點選*/
.button:active::after{
  box-shadow: none;
  opacity: 0.4;
  transition: 0s; /*取消過渡*/
}

然後,神奇的效果就出來了!

這樣就實現了和 Ant Design 幾乎相同的點選效果

四、其他動效案例

上面其實提供了一種思路,只要是這種點選動畫,都可以採用這種方式來實現。比如這樣一個重新整理按鈕,需要點選的時候轉一下

用這種思路就很容易了,這個例子比上面那個要簡單一些,畢竟只有旋轉變化,沒有透明度變化,核心程式碼如下

.icon{
    transform: rotate(360deg);
    transition: .5s;
}
.button:active .icon{
    transform: rotate(0);
    transition: 0s;
}

完整程式碼可以訪問 ant design button (codepen.io),整合了更多的 demo

再比如這樣的點選粒子動效,原理也是相同的

在之前文章CSS實現一個粒子動效的按鈕中已經有講到,這裡就不多說了,完整程式碼可以訪問 button-active (codepen.io)

五、更復雜的動畫

前面提到過,簡單的動畫可以用過渡transition來實現,那麼稍微複雜點的,比如下面這種 “Q彈Q彈” 的按鈕

這類動畫,單純的transition就無能為力了,必須藉助animation來實現,原理還是類似

先定義一個動畫關鍵幀

@keyframes tada {
    from {
        transform: scale3d(1, 1, 1)
    }
    10%, 20% {
        transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)
    }
    30%, 50%, 70%, 90% {
        transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)
    }
    40%, 60%, 80% {
        transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)
    }
    to {
        transform: scale3d(1, 1, 1)
    }
}

這個動畫來自於 Animate.css 中的 tada,直接 copy 過來就行

然後讓按鈕動起來

.button{
  animation: tada 1s;
}

在點選的時候重置動畫,直接重置動畫,animation可能會更好理解一些,這樣在抬起的時候會重新執行動畫

.button:active{
  animation: none;
}

這樣就實現了,是不是出乎意料的容易?

不過有一點小瑕疵,每次頁面重新整理,按鈕會主動進行一次動畫(因為動畫是自動執行的),如下

那麼,如何避免首次進來時動畫不執行呢?

這裡有一個小技巧,可以在預設情況下設定動畫時長為 0 ,這樣在首次動畫執行後,馬上就結束了,然後在 hover時恢復預設的動畫時長,由於動畫已經結束,改變動畫時長也不會觸發動畫再次執行,所以實現就是

.button{
  animation: jump 0s;
}
.button:hover{
  animation-duration: 1s;
}
.button:active{
  animation: none;
}

這樣重新整理頁面就不會再有動畫了

接下來,藉助 animate.css 你可以更換任意的動畫,比如

完整程式碼可以訪問button-jump (codepen.io),整合了更多的 demo

六、總結和說明

以上就是關於 CSS 點選動畫的幾個套路和一些案例,其實就是預設執行動畫,點選時重置一下就行了。整體來說程式碼很簡單,只是理解起來可能不是特別順暢,下面總結一下實現要點:

  1. 簡單動畫用transition,其他用 animation
  2. transition 可以通過設定時長為 0 來重置
  3. animation 可以通過設定 none 來重置
  4. 在 :active 時重置動畫,點選後會再次執行動畫
  5. 複雜的動畫可以藉助現有的動畫庫,例如 anmate.css
  6. 設定動畫時長為 0 可以避免首次渲染出現動畫

相比 js 實現,CSS 實現程式碼更少,載入更快,無需等待 js 載入完成,體驗更優(比如天然支援敲空格鍵觸發),同時也更容易維護和使用,直接複製一個類名就行了。最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤
原文:https://juejin.cn/post/7064404257436336135