背景大小不够玩,咋整出会动的条纹悬停效果?

3,514字
15–22 分钟
in

搞前端的朋友都晓得,平时切图摸鱼的时候,最多用到的就是 background-size: cover 把背景图撑满整个盒子,妥妥的省事。但有一天碰到个需求,要在鼠标划上去的时候让背景条纹像吃了摇头丸一样横向展开,光靠 cover 那点本事根本搞不定。这玩意儿到底咋弄?下面直接上硬核操作。

目录

基础搭建

整个效果依赖 background-size 这个属性的二值语法,它能单独控制背景图片的宽和高。先整个最简单的 HTML 结构,一个 <div> 就够:

<div class="box"></div>
.box {
  width: 500px;
  height: 500px;
  background: palegreen;
}

这里用 palegreen 只是临时打个底,后面会换掉。注意 background-size 如果只写一个值,第二个值默认是 auto,很容易让条纹叠成一坨。所以后续必须把高度也锁死。

条纹制作

要做出五条横向条纹,不能直接用 repeating-linear-gradient,因为每条条纹的宽度不一样,而且还要做过渡动画。换一种思路:用五个独立的 linear-gradient 堆叠在一起,每个渐变都是纯黑色到纯黑色,分别放在容器右上角的不同垂直偏移位置。

.box {
  --gt: linear-gradient(black, black);
  --n: 100px;

  width: 500px;
  height: 500px;
  background: 
    var(--gt) top right,
    var(--gt) top var(--n) right,
    var(--gt) top calc(var(--n) * 2) right,
    var(--gt) top calc(var(--n) * 3) right,
    var(--gt) top calc(var(--n) * 4) right,
    palegreen;
  background-repeat: no-repeat;
}

这里有个容易翻车的地方:如果不写 background-repeat: no-repeat,那些背景图会默认平铺,整个画面直接黑成煤球。每个黑色渐变块现在还没有尺寸,看起来就是一块实心黑。接下来用 background-size 给每个条纹单独设置宽度和高度:

.box {
  /* 接上面代码 */
  background-size: 60% var(--n), 90% var(--n), 70% var(--n), 40% var(--n), 10% var(--n);
}

这样每条条纹的高度都是 100px,宽度分别是容器宽度的 60%、90%、70%、40%、10%。如果希望条纹之间留出空隙,把高度稍微缩小一点就行:

.box {
  --h: calc(var(--n) - 5px);
  background-size: 60% var(--h), 90% var(--h), 70% var(--h), 40% var(--h), 10% var(--h);
}

减掉 5px 之后,每条条纹下方就会露出一小截底色,变成间隙效果。

混合模式

光有黑白条纹还不够,最终要上真正的彩色渐变。这里用两层 <div> 叠罗汉,外层父容器用 Grid 把它们重合在一起:

<section class="stripes-wrapper">
  <div class="gradient-layer"></div>
  <div class="mask-layer"></div>
</section>
.stripes-wrapper {
  display: grid;
  align-items: center;
  justify-items: center;
  width: 500px;
  height: 500px;
}

.stripes-wrapper > div {
  width: inherit;
  height: inherit;
  grid-area: 1 / 1;
}

第一层放真正的渐变色,比如从左到右的红橙渐变:

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

第二层放之前做好的黑白条纹,背景底色换成白色:

.mask-layer {
  --gt: linear-gradient(black, black);
  --n: 100px;
  --h: calc(var(--n) - 5px);
  background: 
    var(--gt) top right,
    var(--gt) top var(--n) right,
    var(--gt) top calc(var(--n) * 2) right,
    var(--gt) top calc(var(--n) * 3) right,
    var(--gt) top calc(var(--n) * 4) right,
    white;
  background-repeat: no-repeat;
  background-size: 60% var(--h), 90% var(--h), 70% var(--h), 40% var(--h), 10% var(--h);
  mix-blend-mode: screen;
}

mix-blend-mode: screen 这个属性很关键:黑色区域会让底下的渐变漏出来,白色区域保持白色。最终呈现出来就是彩色条纹叠在白色底上。如果底色不是纯白,比如带一点米黄,那么整个背景会透出那种奶油质感。

悬停动画

鼠标划过外层容器时,让所有条纹的宽度瞬间变成 100%,并且加上平滑过渡。选中第二层 .mask-layer 来改:

.stripes-wrapper:hover > .mask-layer {
  background-size: 100% var(--h);
  transition: background-size 1s;
}

这样每条条纹就会像拉面一样从左边弹到右边,宽度占满整个容器。注意 transition 只能作用在 background-size 上,其他属性比如颜色、位置都不会动。如果要让过渡更丝滑,可以给 .mask-layer 预先加上 transition: background-size 0.3s ease-out

完整方案

下面是一份可以直接复制粘贴的成品代码,包含所有必要样式和一个简单的文字示例:

<section style="
  display: grid;
  align-items: center;
  justify-items: center;
  width: 500px;
  height: 500px;
  background: #faf0e6;  /* 米色底 */
">
  <div style="
    width: inherit;
    height: inherit;
    grid-area: 1 / 1;
    background: linear-gradient(135deg, #ff7e5e, #feb47b);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2rem;
    font-weight: bold;
    color: white;
    text-shadow: 0 0 5px black;
  ">
    划我
  </div>
  <div style="
    width: inherit;
    height: inherit;
    grid-area: 1 / 1;
    --gt: linear-gradient(black, black);
    --n: 100px;
    --h: calc(var(--n) - 5px);
    background: 
      var(--gt) top right,
      var(--gt) top var(--n) right,
      var(--gt) top calc(var(--n) * 2) right,
      var(--gt) top calc(var(--n) * 3) right,
      var(--gt) top calc(var(--n) * 4) right,
      white;
    background-repeat: no-repeat;
    background-size: 60% var(--h), 90% var(--h), 70% var(--h), 40% var(--h), 10% var(--h);
    mix-blend-mode: screen;
    transition: background-size 0.8s cubic-bezier(0.2, 0.9, 0.4, 1.1);
  "></div>
</section>

<style>
  section:hover > div:last-child {
    background-size: 100% var(--h);
  }
</style>

把上面这段丢进任意 HTML 文件里,鼠标划上去就能看到条纹像扯桌布一样铺满整个盒子。如果要调整条纹数量、方向或者颜色,只需要改 --n 的值(控制间距)和第一层的 linear-gradient 参数。水平条纹变垂直条纹?把所有 top right 换成 left top,然后把宽度值改成高度值即可。