一個炫酷的頭像懸停效果

南城FE發表於2023-02-06
本文翻譯自 A Fancy Hover Effect For Your Avatar,略有刪改,有興趣可以看看原文。

你知道當一個人的頭像從一個圓圈或洞裡伸出來時的那種效果嗎?本文將使用一種很簡潔的方式實現該懸停效果,可以用在你的頭像互動上面。

看到了嗎?我們將製作一個縮放動畫,其中頭像部分似乎從它所在的圓圈中鑽出來了。是不是很酷呢?接下來讓我們一起一步一步地構建這個動畫互動效果。

HTML:只需要一個元素

是的,只需要一個img圖片標籤即可,本次練習的挑戰性部分是使用盡可能少的程式碼。如果你已經關注我一段時間了,你應該習慣了。我努力尋找能夠用最小、最易維護的程式碼實現的CSS解決方案。

<img src="" alt="">

首先我們需要一個帶有透明背景的正方形影像檔案,以下是本次案例使用的影像。

在開始CSS之前,讓我們先分析一下效果。懸停時影像會變大,所以我們肯定會在這裡使用transform:scale。頭像後面有一個圓圈,徑向漸變應該可以達到這個效果。最後我們需要一種在圓圈底部建立邊框的方法,該邊框將不受整體放大的影響且是在視覺頂層。

放大效果

放大的效果,增加transform:scale,這個比較簡單。

img:hover {
  transform: scale(1.35);
}

上面說過背景是一個徑向漸變。我們建立一個徑向漸變,但是兩個顏色之間不要有過渡效果,這樣使得它看起來像我們畫了一個有實線邊框的圓。

img {
  --b: 5px; /* border width */

  background:
    radial-gradient(
      circle closest-side,
      #ECD078 calc(99% - var(--b)),
      #C02942 calc(100% - var(--b)) 99%,
      #0000
    );
}

注意CSS變數,--b,在這裡它表示“邊框”的寬度,實際上只是用於定義徑向漸變紅色部分的位置。

下一步是在懸停時調整漸變大小,隨著影像的放大,圓需要保持大小不變。由於我們正在應用scale變換,因此實際上需要減小圓圈的大小,否則它會隨著化身的大小而增大。

讓我們首先定義一個CSS變數--f,它定義了“比例因子”,並使用它來設定圓的大小。我使用1作為預設值,因為這是影像和圓的初始比例,我們從圓轉換。

現在我們必須將背景定位在圓的中心,並確保它佔據整個高度。我喜歡把所有東西都直接簡寫在 background 屬性,程式碼如下:

background: radial-gradient() 50% / calc(100% / var(--f)) 100% no-repeat;

背景放置在中心( 50%),寬度等於calc(100%/var(--f)),高度等於100%。

當 --f 等於 1 時是我們最初的比例。同時,漸變佔據容器的整個寬度。當我們增加 --f,元素的大小會增長但是漸變的大小將減小。

越來越接近了!我們在頂部新增了溢位效果,但我們仍然需要隱藏影像的底部,這樣它看起來就像是跳出了圓圈,而不是整體浮在圓圈前面。這是整個過程中比較複雜的部分,也是我們接下來要做的。

下邊框

第一次嘗試使用border-bottom屬性,但無法找到一種方法來匹配邊框的大小與圓的大小。如圖所示,相信你能看出來無法實現我們想要的效果:

實際的解決方案是使用outline屬性。不是borderoutline可以讓我們創造出很酷的懸停效果。結合 outline-offset 偏移量,我們就可以實現所需要的效果。

其核心是在影像上設定一個outline輪廓並調整其偏移量以建立下邊框。偏移量將取決於比例因子,與漸變大小相同。outline-offset 偏移量看起來相對比較複雜,這裡對計算方式進行了精簡,有興趣的可以看看原文。

img {
  --s: 280px; /* image size */
  --b: 5px; /* border thickness */
  --c: #C02942; /* border color */
  --f: 1; /* initial scale */
  
  border-radius: 0 0 999px 999px;
  outline: var(--b) solid var(--c);
  outline-offset: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b));

}

因為我們需要一個圓形的底部邊框,所以在底部新增了一個邊框圓角,使輪廓與漸變的彎曲程度相匹配。

現在我們需要找到如何從輪廓中刪除頂部,也就是上圖中擋住頭像的那根線。換句話說,我們只需要影像的底部輪廓。首先,在頂部新增空白和填充,以幫助避免頂部頭像的重疊,這透過增加padding即可實現:

padding-top: calc(var(--s)/5)

這裡還有一個注意點,需要新增 content-box 值新增到 background

background:
  radial-gradient(
    circle closest-side,
    #ECD078 calc(99% - var(--b)),
    var(--c) calc(100% - var(--b)) 99%,
    #0000
  ) 50%/calc(100%/var(--f)) 100% no-repeat content-box;

這樣做是因為我們新增了padding填充,並且我們只希望將背景設定為內容框,因此我們必須顯式地定義出來。

CSS mask

到了最後一部分!我們要做的就是藏起一些碎片。為此,我們將依賴於 CSS mask 屬性,當然還有漸變。

下面的圖說明了我們需要隱藏的內容或需要顯示的內容,以便更加準確。左圖是我們目前擁有的,右圖是我們想要的。綠色部分說明了我們必須應用於原始影像以獲得最終結果的遮罩內容。

我們可以識別mask的兩個部分:

  • 底部的圓形部分,與我們用來建立化身後面的圓的徑向漸變具有相同的維度和曲率
  • 頂部的矩形,覆蓋輪廓內部的區域。請注意輪廓是如何位於頂部的綠色區域之外的-這是最重要的部分,因為它允許剪下輪廓,以便只有底部部分可見

最終的完整css如下,對有重複的程式碼進行抽離,如--_g,--_o:

img {
  --s: 280px; /* image size */
  --b: 5px; /* border thickness */
  --c: #C02942; /* border color */
  --f: 1; /* initial scale */

  --_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box;
  --_o: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b));

  width: var(--s);
  aspect-ratio: 1;
  padding-top: calc(var(--s)/5);
  cursor: pointer;
  border-radius: 0 0 999px 999px;
  outline: var(--b) solid var(--c);
  outline-offset: var(--_o);
  background: 
    radial-gradient(
      circle closest-side,
      #ECD078 calc(99% - var(--b)),
      var(--c) calc(100% - var(--b)) 99%,
      #0000) var(--_g);
  mask:
    linear-gradient(#000 0 0) no-repeat
    50% calc(-1 * var(--_o)) / calc(100% / var(--f) - 2 * var(--b)) 50%,
    radial-gradient(
      circle closest-side,
      #000 99%,
      #0000) var(--_g);
  transform: scale(var(--f));
  transition: .5s;
}
img:hover {
  --f: 1.35; /* hover scale */
}

下面的一個演示,直觀的說明mask的使用區域。中間的框說明了由兩個漸變組成的遮罩層。把它想象成左邊影像的可見部分,你就會得到右邊的最終結果:

最後

搞定!我們不僅完成了一個流暢的懸停動畫,而且只用了一個<img>元素和不到20行的CSS技巧!如果我們允許自己使用更多的HTML,我們能簡化CSS嗎?當然可以。但我們是來學習CSS新技巧的!這是一個很好的練習,可以探索CSS漸變、遮罩、outline屬性的行為、轉換以及其他許多內容。

線上效果

例項裡面是流行的CSS開發人員的照片。有興趣的同學可以展示一下自己的頭像效果。

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

看完本文如果覺得有用,記得點個贊支援,收藏起來說不定哪天就用上啦~

專注前端開發,分享前端相關技術乾貨,公眾號:南城大前端(ID: nanchengfe)

相關文章