很多小伙伴以为CSS的outline属性就是个画边框的替补队员,平时顶多用来描个边、标个焦点啥的。但最近捣鼓图片装饰的时候发现,这玩意儿配合负偏移和半透明色值,居然能整出各种花里胡哨的悬停动画和形状遮罩,而且全程只用一张<img>标签,连个多余的div都不用加。下面直接上实操,看看咋用outline把图片玩出新高度。
啥是outline
outline跟border长得像,但骨子里完全不一样。border会挤占盒模型的空间,增加元素的实际占地大小;而outline就像给元素套了个“光晕”,不占布局位置,而且默认四个边一起画,没法单独控制某一边。最关键的一个骚操作是:outline支持负偏移,用outline-offset可以往元素内部缩,甚至完全盖在图片上面。拿这个特性搞覆盖层动画,简直绝绝子。
| 属性 | 占盒模型 | 支持单边 | 可负偏移 |
|---|---|---|---|
| border | 是 | 是 | 否 |
| outline | 否 | 否 | 是 |
搞个悬停覆盖层
先来个最常用的效果:鼠标一放上去,图片上慢慢“擦”出一个带边框的干净区域,原本的半透明遮罩自动消失。整个过程只用outline的厚度和偏移值变化就能实现。
第一步:固定图片尺寸
给<img>一个固定宽度,比如250像素,再用aspect-ratio: 1强制变成正方形。这样计算偏移量时心里有数。
第二步:叠上半透明遮罩
把outline厚度设成图片宽度的一半(125px),颜色用半透明黑(#0009)。然后outline-offset设为负的一半(-125px),整个轮廓线就会从图片中心往外炸开,但因为厚度够大,最终盖住整张图片。效果就是一个均匀的深色蒙层。
第三步:定义悬停样式
鼠标悬停时,把outline改成一个细边框(比如8px实色),outline-offset改成一个小正数(比如14px)。这时轮廓线缩到图片外围,之前那个半透明大厚层就消失了。配合transition: 0.3s,就能看到蒙层“收缩”成边框的丝滑动画。
img {
--size: 250px;
--border: 8px;
--gap: 14px;
--color: #4ECDC4;
width: var(--size);
aspect-ratio: 1;
outline: calc(var(--size) / 2) solid #0009;
outline-offset: calc(var(--size) / -2);
cursor: pointer;
transition: 0.3s;
}
img:hover {
outline: var(--border) solid var(--color);
outline-offset: var(--gap);
}这里有个坑:outline-offset负值不能超过元素尺寸的一半,否则轮廓线会直接跑出元素范围,导致覆盖不全。另外半透明颜色建议用#0009这种四位数简写,透明度刚刚好,既能看到底层图片,又不至于太刺眼。
弄个淡入淡出效果
有时候不想移动轮廓线,只希望悬停时蒙层慢慢变透明。这种场景用超大outline厚度配合mask就能搞定,而且不需要提前知道图片尺寸。
第一步:设置一个巨大厚度
用100vmax作为outline的厚度。vmax是视口宽高里较大的那个百分比,随便屏幕多大,这个值都能保证轮廓线完全覆盖元素。同时把outline颜色设成想要的蒙层色(比如带透明度的深色)。
第二步:用mask剪掉多余部分
不加mask的话,整个屏幕都会被那个超大轮廓线盖住。所以需要给img加一个mask,用linear-gradient或者纯色裁剪,只保留图片区域内的部分。这样outline虽然物理上巨大,但被mask一挡,露出来的还是图片范围内的蒙层。
第三步:悬停时让蒙层透明
鼠标悬停时,把outline颜色改成完全透明(transparent)。因为transition的存在,透明度会从半透明慢慢变到全透明,形成淡出动画。注意outline厚度和偏移值都不用改,只动颜色。
img {
width: 200px;
aspect-ratio: 1;
outline: 100vmax solid #4ECDC480;
outline-offset: 0;
mask: linear-gradient(#000, #000);
transition: 0.3s;
}
img:hover {
outline-color: transparent;
}用100vmax在Safari里可能会抽风。如果碰到这种情况,可以换回第一种方法:把厚度改成图片宽度的一半,偏移量设成负的一半,效果一样稳。
结合clip-path做形状动画
想要蒙层不是矩形,而是星形、心形或者任意多边形?把clip-path拉进来一起玩。流程分三步:先画形状,再叠轮廓,最后动颜色。
第一步:用clip-path切出形状
比如想要一个五角星覆盖在图片上,直接写clip-path: polygon(...)或者用现成的星形路径。这样图片只显示星形区域,外部被裁剪掉。
第二步:加上半透明轮廓
跟第一个例子类似,给img一个厚outline并负偏移,让轮廓铺满整个元素。但因为clip-path已经切出了星形,轮廓也只会在星形区域内可见。
第三步:悬停让轮廓透明
鼠标放上去时,把outline颜色改成透明,之前那个半透明星形蒙层就会慢慢消失。反过来也可以:初始状态轮廓透明,悬停时给个实色,形成从无到有的星形浮现动画。
img {
width: 300px;
aspect-ratio: 1;
outline: 150px solid #FFD166;
outline-offset: -150px;
clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
transition: 0.3s;
}
img:hover {
outline-color: transparent;
}换心形也是同理,只要改clip-path的值就行。不过要留意:clip-path的动画兼容性没那么丝滑,尤其是多边形点数不同的时候,过渡可能会跳帧。所以这种用法更适合颜色或透明度的过渡,而不是路径形状的变形。想要更流畅的形状变化,可以换成mask配合渐变,具体玩法在之前的两篇文章里有详细拆解。
配合mask玩出渐变轮廓
mask比clip-path更灵活,因为它支持渐变透明度。比如做一个从实色到透明的环形渐变轮廓,悬停时慢慢扩散开。
第一步:用mask定义渐变区域
写一个radial-gradient,中心不透明,边缘透明,作为mask的值。这样图片上只会显示渐变环以内的部分。
第二步:叠超大outline
跟淡入淡出例子一样,用100vmax厚度的半透明outline,配合负偏移让它覆盖图片。因为mask已经切了个环,所以轮廓只出现在环内。
第三步:悬停改变mask尺寸
鼠标悬停时,把径向渐变的范围变大(比如从30%变成100%),同时让outline颜色逐渐透明。这样就会出现一个光环从图片中间向外“融化”的效果,非常带感。
img {
width: 260px;
aspect-ratio: 1;
outline: 100vmax solid #E84855;
outline-offset: -100vmax;
mask: radial-gradient(circle at center, #000 20%, #0000 50%);
transition: 0.4s;
}
img:hover {
outline-color: transparent;
mask: radial-gradient(circle at center, #000 80%, #0000 100%);
}这里有个细节:mask的渐变终点颜色#0000是全透明,起点#000是不透明,中间过渡区域会产生半透明羽化。所以光环的边缘不是硬切,而是柔和的虚化效果。另外outline-offset用负的100vmax是为了让轮廓线中心对齐图片中心,否则可能会偏。
上面的所有技巧都只用了一张<img>,没有套娃似的div包装,也没有伪元素。outline这个平时被忽略的属性,配合负偏移、半透明色值,再加上clip-path或mask,就能把图片装饰玩出各种新花样。下次写图片悬停效果的时候,可以先想想能不能用轮廓线搞定,省掉一堆冗余标签。
