一個有意思的CSS圖片Hover效果
今天來分享一個比較有意思的圖片 hover 效果,如下:
案例來源于https://codepen.io/t_afif/details/abRWELR[1],略有修改。
仔細觀察,這個效果主要有兩個要點。
- 圖片被切割成多個矩形。
- 每個矩形會旋轉 90 度。
那么,這個是如何實現的呢?花幾分鐘時間一起看看吧。
一、分割的矩形
假設HTML是這樣的,很簡單,就一個圖片。
<img src="xxx.jpg" alt="xxx">
然后,我們需要一個變量,來控制分割的數量,比如2表示2*2,這里可以用 CSS 變量。
img{
--n: 4; /*橫縱分割的數量*/
}
那么,如何來切割呢?
提到切割,可以想到鏤空,進而可以想到遮罩(CSS Mask)。關于遮罩,這個技巧非常實用,之前在多篇文章中都有用到
- CSS 如何實現羽化效果?[2]
- 別用圖片了,CSS 遮罩合成實現帶圓角的環形 loading 動畫[3]
- CSS mask 實現鼠標跟隨鏤空效果[4]
- CSS 實現Chrome標簽欄的技巧[5]
- CSS 實現優惠券的技巧[6]
原理很簡單,最終效果只顯示不透明的部分,透明部分將不可見,半透明類推,例如:
在這里,我們可以通過類似背景平鋪的方式,來將一個完整的圖片切割成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*2和4*4的效果。
這就是分割的原理了。
二、旋轉的矩形
那么,問題來了,這里是背景層,并沒有rotate這樣的屬性,如何讓一個矩形旋轉呢?或者說,如何繪制一個傾斜的矩形呢?
下面就來一步一步實現。
由于遮罩和背景的語法基本一致,為了方便調試,可以先用背景代替。
大家都知道,線性漸變是可以設置角度的,為了計算方便,我們可以用 CSS 變量來表示。
div{
--r: 45deg;
background: linear-gradient(var(--r), red, orange)
}
這樣可以得到一個45deg的漸變。
然后,我們可以將這個漸變改成透明→純色→透明的漸變。
div{
--r: 45deg;
background: linear-gradient(var(--r), transparent 5%, orange 0 95%, transparent 0)
}
效果如下:
為了計算方便,可以將透明的比例用 CSS 變量來表示。
div{
--r: 45deg;
--d: 30%;
background: linear-gradient(var(--r), transparent var(--d), orange 0 calc(100% - var(--d)), transparent 0)
}
下面是30%的效果。
接下來,用同樣的方式繪制和這個垂直的圖形,也就是角度相差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),
}
效果如下:
注意觀察,兩個重疊的部分不就是一個旋轉45deg的矩形嗎?如下:
可以任意改變角度。
div{
--deg: 15deg
}
下面改變背景尺寸,變成4*4的效果:
div{
background-size: 50% 50%
}
是不是和我們想要的效果有點相似呢?下面將背景用做遮罩。
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));
}
變成了這樣。
是不是很凌亂?這是因為現在的遮罩還是直接疊加的,并不是只顯示重疊部分,可以設置遮罩合成mask-composite,也就是將圖形進行布爾運算,得出我們想要的圖形,這里簡單介紹一下。
mask-composite - CSS: Cascading Style Sheets | MDN (mozilla.org)[7]
/* Keyword values */
mask-composite: add; /* 疊加(默認) */
mask-composite: subtract; /* 減去,排除掉上層的區域 */
mask-composite: intersect; /* 相交,只顯示重合的地方 */
mask-composite: exclude; /* 排除,只顯示不重合的地方 */
相信在很多圖形設計軟件中都見到類似的操作(下面是 photoshop)。
這些是標準屬性,Chrome 還不支持,可以用帶前綴的屬性-webkit-mask-composite[8] ,但是值和上面這些不同,非常多,主要有這些。
-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;
效果如下:
三、動畫
最后就是動畫了。
我們需要在hover的時候,將矩形旋轉90deg,可以直接改變--r這個變量。
img{
--r: 0deg;
}
img:hover{
--r: 90deg;
transition: 0.5s;
}
但是,僅僅這樣是沒有動畫的,因為--r并不是一個合法的、可以過渡的屬性。
這時可以用到 CSS @property[9]。可以讓任意變量像顏色一樣進行支持過渡和動畫
@property --r {
syntax: "<angle>";
initial-value: 0deg;
inherits: false;
}
現在就有過渡效果了。
現在還有一個問題,空隙太大了,還需要改變--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;
}
效果如下:
當然還可以將這個過渡和動畫寫在一個動畫里。
@keyframes r {
0%{
--d: 0%
}
100%{
--d: 0%;
--r: 90deg
}
50%{
--d: 20%
}
}
img:hover{
animation: r .5s;
}
這樣也能實現相同的效果,下面分別是2*2、4*4、6*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">
完整代碼可以查看以下任意鏈接:
- CSS img hover (juejin.cn)[10]
- CSS img hover (runjs.work)[11]
- CSS img hover (codepen.io)[12]
四、總結和說明
以上就是實現的全部過程了,代碼其實不多,其實主要難點在于旋轉矩形的繪制,整體實現其實并不困難,難點其實是創意,可惜的是平時接觸的還是太少??。下面總結一下實現要點:
- 提到切割,可以想到鏤空,進而可以想到遮罩。
- 分割成n*n塊,其實就是遮罩背景的平鋪。
- 旋轉的矩形其實就是兩個互相垂直的線性漸變重疊而成。
- CSS 變量的過渡動畫需要用到CSS @property 特性。
兼容性其實就取決于CSS @property了,這是CSS Houdini的一部分,目前只有 Chrome 和 Safari支持。
參考資料
[1]https://codepen.io/t_afif/details/abRWELR: https://codepen.io/t_afif/details/abRWELR。
[2]CSS 如何實現羽化效果?: https://juejin.cn/post/7176094306124431421。
[3]別用圖片了,CSS 遮罩合成實現帶圓角的環形 loading 動畫: https://juejin.cn/post/7217731969307328571。
[4]CSS mask 實現鼠標跟隨鏤空效果: https://juejin.cn/post/7033188994641100831。
[5]CSS 實現Chrome標簽欄的技巧: https://juejin.cn/post/6986827061461516324。
[6]CSS 實現優惠券的技巧 : https://juejin.cn/post/6945023989555134494。
[7]mask-composite - CSS: Cascading Style Sheets | MDN (mozilla.org): https://link.juejin.cn/?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FCSS%2Fmask-composite。
[8]-webkit-mask-composite: https://link.juejin.cn/?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FCSS%2F-webkit-mask-composite。
[9]CSS @property: https://developer.mozilla.org/zh-CN/docs/Web/CSS/@property。
[10]CSS img hover (juejin.cn): https://code.juejin.cn/pen/7232884497778704440。
[11]CSS img hover (runjs.work): https://runjs.work/projects/a1f43973537d4e05。
[12]CSS img hover (codepen.io): https://codepen.io/xboxyan/pen/vYVaNNp。