一個有意思的CSS圖片hover效果

XboxYan發表於2023-05-15
歡迎關注我的公眾號:前端偵探

今天來分享一個比較有意思的圖片 hover 效果,如下

Kapture 2023-05-13 at 23.51.16.gif

案例來源於https://codepen.io/t_afif/details/abRWELR,略有修改

仔細觀察,這個效果主要有兩個要點

  1. 圖片被切割成多個矩形
  2. 每個矩形會旋轉 90 度

那麼,這個是如何實現的呢?花幾分鐘時間一起看看吧

一、分割的矩形

假設HTML是這樣的,很簡單,就一個圖片

<img src="xxx.jpg" alt="xxx">

然後,我們需要一個變數,來控制分割的數量,比如2表示2*2,這裡可以用 CSS 變數

img{
  --n: 4; /*橫縱分割的數量*/
}

那麼,如何來切割呢?

提到切割,可以想到鏤空,進而可以想到遮罩(CSS Mask)。關於遮罩,這個技巧非常實用,之前在多篇文章中都有用到

原理很簡單,最終效果只顯示不透明的部分,透明部分將不可見,半透明類推,例如

image.png

在這裡,我們可以透過類似背景平鋪的方式,來將一個完整的圖片切割成n*n個矩形,如下

img{
  --n: 4;
  -webkit-mask: radial-gradient(black, transparent);
  -webkit-mask-size: calc(100% / var(--n)) calc(100% / var(--n));
}

這裡用了一個徑向漸變做了遮罩圖片,遮罩尺寸是100% / var(--n),剛好將完整的圖片分成了n*n份,效果如下,分別是2*24*4的效果

image.png

這就是分割的原理了

二、旋轉的矩形

那麼,問題來了,這裡是背景層,並沒有rotate這樣的屬性,如何讓一個矩形旋轉呢?或者說,如何繪製一個傾斜的矩形呢?

下面就來一步一步實現。

由於遮罩和背景的語法基本一致,為了方便除錯,可以先用背景代替

大家都知道,線性漸變是可以設定角度的,為了計算方便,我們可以用 CSS 變數來表示

div{
  --r: 45deg;
  background: linear-gradient(var(--r), red, orange)
}

這樣可以得到一個45deg的漸變

image.png

然後,我們可以將這個漸變改成透明→純色→透明的漸變

div{
  --r: 45deg;
  background: linear-gradient(var(--r), transparent 5%, orange 0 95%, transparent 0)
}

效果如下

image.png

為了計算方便,可以將透明的比例用 CSS 變數來表示

div{
  --r: 45deg;
  --d: 30%;
  background: linear-gradient(var(--r), transparent var(--d), orange 0 calc(100% - var(--d)), transparent 0)
}

下面是30%的效果

image.png

接下來,用同樣的方式繪製和這個垂直的圖形,也就是角度相差90deg

div{
  --r: 45deg;
  --d: 30%;
  background: linear-gradient(var(--r), transparent var(--d), orange 0 calc(100% - var(--d)), transparent 0),
    linear-gradient(calc(var(--r) + 90deg), transparent var(--d), red 0 calc(100% - var(--d)), transparent 0),
}

效果如下

image.png

注意觀察,兩個重疊的部分不就是一個旋轉45deg的矩形嗎?如下

image.png

可以任意改變角度

div{
  --deg: 15deg
}

image.png

下面改變背景尺寸,變成4*4的效果

div{
  background-size: 50% 50%
}

image.png

是不是和我們想要的效果有點相似呢?下面將背景用做遮罩

img{
  --r: 30deg;
  --d: 30%;
  -webkit-mask:
    linear-gradient(var(--r), transparent var(--d),red 0 calc(100% - var(--d)), transparent 0),
    linear-gradient(calc(var(--r) + 90deg), transparent var(--d), red 0 calc(100% - var(--d)), transparent 0);
  -webkit-mask-size: calc(100%/var(--n)) calc(100%/var(--n));
}

變成了這樣

image.png

是不是很凌亂?這是因為現在的遮罩還是直接疊加的,並不是只顯示重疊部分,可以設定遮罩合成mask-composite,也就是將圖形進行布林運算,得出我們想要的圖形,這裡簡單介紹一下

mask-composite - CSS: Cascading Style Sheets | MDN (mozilla.org)
/* Keyword values */
mask-composite: add; /* 疊加(預設) */
mask-composite: subtract; /* 減去,排除掉上層的區域 */
mask-composite: intersect; /* 相交,只顯示重合的地方 */
mask-composite: exclude; /* 排除,只顯示不重合的地方 */

相信在很多圖形設計軟體中都見到類似的操作(下面是 photoshop)

image.png

這些是標準屬性,Chrome 還不支援,可以用帶字首的屬性-webkit-mask-composite ,但是值和上面這些不同,非常多,主要有這些

-webkit-mask-composite: clear; /*清除,不顯示任何遮罩*/
-webkit-mask-composite: copy; /*只顯示上方遮罩,不顯示下方遮罩*/
-webkit-mask-composite: source-over; /*疊加,兩者都顯示*/
-webkit-mask-composite: source-in; /*只顯示重合的地方*/
-webkit-mask-composite: source-out; /*只顯示上方遮罩,重合的地方不顯示*/
-webkit-mask-composite: source-atop;
-webkit-mask-composite: destination-over; /*疊加,兩者都顯示*/
-webkit-mask-composite: destination-in; /*只顯示重合的地方*/
-webkit-mask-composite: destination-out;/*只顯示下方遮罩,重合的地方不顯示*/
-webkit-mask-composite: destination-atop;
-webkit-mask-composite: xor; /*只顯示不重合的地方*/

回到這裡,我們想要得到兩者重疊的部分,所以可以

-webkit-mask-composite: source-in;

效果如下

Kapture 2023-05-14 at 11.22.46.gif

三、動畫

最後就是動畫了。

我們需要在hover的時候,將矩形旋轉90deg,可以直接改變--r這個變數

img{
  --r: 0deg;
}
img:hover{
  --r: 90deg;
  transition: 0.5s;
}

但是,僅僅這樣是沒有動畫的,因為--r並不是一個合法的、可以過渡的屬性

Kapture 2023-05-14 at 11.39.08.gif

這時可以用到 CSS @property。可以讓任意變數像顏色一樣進行支援過渡和動畫

@property --r {
   syntax: "<angle>";
   initial-value: 0deg;
   inherits: false;
}

現在就有過渡效果了

Kapture 2023-05-14 at 11.43.12.gif

現在還有一個問題,空隙太大了,還需要改變--d的大小,起始點應該是0%,在中間45deg時最大,也就是0%→20%→0%,可以用animation實現

@keyframes d {
  0%,100%{
    --d: 0%
  }
  50%{
    --d: 20%
  }
}
img:hover{
  --r: 90deg;
  transition: 0.5s;
  animation: d .5s;
}

效果如下

Kapture 2023-05-14 at 11.47.46.gif

當然還可以將這個過渡和動畫寫在一個動畫裡

@keyframes r {
  0%{
    --d: 0%
  }
  100%{
    --d: 0%;
    --r: 90deg
  }
  50%{
    --d: 20%
  }
}
img:hover{
  animation: r .5s;
}

這樣也能實現相同的效果,下面分別是2*24*46*6的效果

<img src="xxx.jpg" alt="xxx" style="--n:2">
<img src="xxx.jpg" alt="xxx" style="--n:4">
<img src="xxx.jpg" alt="xxx" style="--n:6">

Kapture 2023-05-14 at 11.59.52.gif

完整程式碼可以檢視以下任意連結:

四、總結和說明

以上就是實現的全部過程了,程式碼其實不多,其實主要難點在於旋轉矩形的繪製,整體實現其實並不困難,難點其實是創意,可惜的是平時接觸的還是太少?。下面總結一下實現要點:

  1. 提到切割,可以想到鏤空,進而可以想到遮罩
  2. 分割成n*n塊,其實就是遮罩背景的平鋪
  3. 旋轉的矩形其實就是兩個互相垂直的線性漸變重疊而成
  4. CSS 變數的過渡動畫需要用到CSS @property 特性

相容性其實就取決於CSS @property了,這是CSS Houdini的一部分,目前只有 Chrome 和 Safari支援。

image.png

最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤

歡迎關注我的公眾號:前端偵探

相關文章