欢迎关注我的公众号:前端侦探
今天来分享一个比较有意思的图片 hover 效果,如下
案例来源于https://codepen.io/t_afif/details/abRWELR,略有修改
仔细观察,这个效果主要有两个要点
- 图片被切割成多个矩形
- 每个矩形会旋转 90 度
那么,这个是如何实现的呢?花几分钟时间一起看看吧
一、分割的矩形
假设HTML
是这样的,很简单,就一个图片
<img src="xxx.jpg" alt="xxx">
然后,我们需要一个变量,来控制分割的数量,比如2
表示2*2
,这里可以用 CSS 变量
img{
--n: 4; /*横纵分割的数量*/
}
那么,如何来切割呢?
提到切割,可以想到镂空,进而可以想到遮罩(CSS Mask)。关于遮罩,这个技巧非常实用,之前在多篇文章中都有用到
原理很简单,最终效果只显示不透明的部分,透明部分将不可见,半透明类推,例如
在这里,我们可以通过类似背景平铺的方式,来将一个完整的图片切割成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)
/* Keyword values */
mask-composite: add; /* 叠加(默认) */
mask-composite: subtract; /* 减去,排除掉上层的区域 */
mask-composite: intersect; /* 相交,只显示重合的地方 */
mask-composite: exclude; /* 排除,只显示不重合的地方 */
相信在很多图形设计软件中都见到类似的操作(下面是 photoshop)
这些是标准属性,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;
效果如下
三、动画
最后就是动画了。
我们需要在hover
的时候,将矩形旋转90deg
,可以直接改变--r
这个变量
img{
--r: 0deg;
}
img:hover{
--r: 90deg;
transition: 0.5s;
}
但是,仅仅这样是没有动画的,因为--r
并不是一个合法的、可以过渡的属性
这时可以用到 CSS @property。可以让任意变量像颜色一样进行支持过渡和动画
@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">
完整代码可以查看以下任意链接:
四、总结和说明
以上就是实现的全部过程了,代码其实不多,其实主要难点在于旋转矩形的绘制,整体实现其实并不困难,难点其实是创意,可惜的是平时接触的还是太少😥。下面总结一下实现要点:
- 提到切割,可以想到镂空,进而可以想到遮罩
- 分割成
n*n
块,其实就是遮罩背景的平铺 - 旋转的矩形其实就是两个互相垂直的线性渐变重叠而成
- CSS 变量的过渡动画需要用到
CSS @property
特性
兼容性其实就取决于CSS @property
了,这是CSS Houdini
的一部分,目前只有 Chrome 和 Safari支持。
最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发❤❤❤
欢迎关注我的公众号:前端侦探
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。