背景条纹过渡效果做不出,background-size加混合模式能搞定?

2,657字
11–17 分钟
in

很多前端老铁在切页面时,都碰到过这种需求:想让背景条纹在鼠标悬停时平滑变宽,但又不想用图片或者SVG。纯CSS能不能实现?当然能,核心就是background-size配合混合模式。下面直接上一套能跑通、能改、能复用的完整流程。

目录

搞背景条纹悬停动画,光设背景图可不行。关键是理解background-size支持双值控制宽高,配合linear-gradient生成纯色块,再用mix-blend-mode: screen让下层渐变色透出来。整个过程不用额外图片,通过background-position错开条纹位置,悬停时把background-size宽度改到100%加过渡动画,就能丝滑扩展。注意控制好间隙和重复平铺,避免条纹堆叠。

background-size双值用法

background-size平时用covercontain比较多,但它的双值语法才是做条纹动画的法宝。第一个值控制宽度,第二个值控制高度。比如background-size: 60% 100px表示背景图片占容器宽度的60%,高度固定100像素。如果只写一个值,第二个默认auto,高度会按比例缩放,容易翻车。

制作五条错位横条纹

先把容器架子搭起来。用一个<section>当父层,里面塞两个<div>,后面用来做混合叠加。

<section class="stripe-stage">
  <div class="stripe-bg"></div>
  <div class="stripe-mask"></div>
</section>
.stripe-stage {
  width: 500px;
  height: 500px;
  display: grid;
  align-items: center;
  justify-items: center;
}

.stripe-stage > div {
  width: inherit;
  height: inherit;
  grid-area: 1 / 1;
}

父级固定宽高,用网格让两个子元素重叠在同一块区域。接下来做条纹层。目标是五条黑色横条,从上往下每隔100像素放一条。用linear-gradient(black, black)生成纯黑块——这里没有渐变,就是从黑到黑,看起来就是个实心方块。然后用background-position把每条分别钉到不同的垂直位置。

.stripe-mask {
  --stripe-solid: linear-gradient(black, black);
  --offset: 100px;
  background: 
    var(--stripe-solid) top right,
    var(--stripe-solid) top var(--offset) right,
    var(--stripe-solid) top calc(var(--offset) * 2) right,
    var(--stripe-solid) top calc(var(--offset) * 3) right,
    var(--stripe-solid) top calc(var(--offset) * 4) right,
    white;
  background-repeat: no-repeat;
}

注意这里背景色设成了白色,底层叠了五条黑色条纹。background-repeat: no-repeat必须加上,不然条纹会重复平铺满整个容器,变成一坨黑。

控制条纹宽高和间隙

光有位置还不够,每条条纹的宽高得单独设置。用background-size分别指定每条的黑块尺寸。宽度用百分比(不同条纹不一样宽),高度统一用--offset减掉5像素,这样每条下方会留出5像素空隙。

.stripe-mask {
  /* ... 上面代码不变 ... */
  --gap: calc(var(--offset) - 5px);
  background-size: 60% var(--gap), 90% var(--gap), 70% var(--gap), 40% var(--gap), 10% var(--gap);
}

这时候看效果,应该能看到五条不同宽度的黑色横条,从上往下排列,每条之间有5像素的白色缝隙。如果条纹没有显示全,检查一下background-position里面的top right是否写对——right让条纹靠右对齐,避免偏移出视野。

混合模式让颜色透出来

把上面.stripe-mask的背景颜色从白色换成白色(本来就是白色),然后把真正想展示的渐变色放到第一个<div>上。

.stripe-bg {
  background: linear-gradient(to right, red, orange);
}

现在两个div叠在一起,下层是红橙渐变,上层是黑条加白底的图案。要想让下层渐变只从黑色区域透出来,白色区域保持白色,需要用mix-blend-mode: screen

.stripe-mask {
  mix-blend-mode: screen;
}

screen混合模式会把黑色部分变透明,白色保持不变。这样一来,红橙渐变就从黑色条纹的位置露出来了,白色背景区域还是白色。如果希望背景色稍微暗一点(比如米色),把.stripe-mask的白色背景改成#f5f0e6,透出的颜色会更柔和。

混合模式效果描述
screen黑色消失
multiply白色消失
overlay对比增强

悬停扩展动画

最后加悬停效果:鼠标放到父容器上时,所有条纹的宽度变成100%,并且带过渡动画。

.stripe-stage:hover > .stripe-mask {
  background-size: 100% var(--gap), 100% var(--gap), 100% var(--gap), 100% var(--gap), 100% var(--gap);
  transition: background-size 1s ease;
}

注意这里background-size的多个值必须全部改成100%,数量要对齐。如果只改第一个,其他条纹不会动。过渡动画加在.stripe-mask本身上也行,但为了性能,直接加在悬停状态里。

整个流程跑下来,悬停时条纹从左向右(或从右向左,取决于之前设的right位置)平滑展开。如果想改方向,把background-position里的right换成left,条纹就会从左边缘伸出来。

有强迫症的小伙伴可能发现,background-size过渡在部分浏览器上会有点卡顿,尤其是多个背景层同时动画的时候。可以加will-change: background-size稍微优化一下,不过大部分现代浏览器已经处理得很丝滑了。