渐变边框的实现方式

前言

我们在 css 里可以直接使用 border 属性指定元素的边框,但这样的方法具有局限性,就是只能添加单色的边框,如果需要给元素添加渐变的边框,又该如何实现呢?

利用 border-image

我们可以使用 border-imagelinear-gradient() 实现渐变的边框。

<template>
  <div :class="$style.btn">
    <slot>button</slot>
  </div>
</template>
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  user-select: none;
  border: 4px solid transparent;
  border-image: linear-gradient(#e66465, #9198e5);
  padding: 8px 16px;
  font-size: 32px;
}

效果如下图所示:

6160930-95c1457bc6a5f461.png
border-image

这样就完成了基本的渐变边框,但这种方法并不能支持圆角属性,除非我们直接使用带圆角的图片。

利用 background

CSS SECRETS 中第二章第八节提到,可以利用叠加的背景图片去模拟边框效果,利用这一思想,我们在渐变的背景之上叠加一个原有的背景,就能达到我们想要的效果,而且因为利用的是 background,所以圆角属性也能得到支持,一举两得。

.btn {
  border-radius: 8px; /* 圆角属性测试 */
  background-image: linear-gradient(#eee, #eee), /* 底色,即原有的背景 */
  linear-gradient(#e66465, #9198e5); /* 模拟渐变边框 */
  background-clip: padding-box, border-box;
  background-origin: border-box;
}

说明:

  • 因为我们需要将底色覆盖在渐变背景之上,层级最高,所以底色背景是 background-image 的第一项,渐变背景为第二项。
  • 由于是模拟边框效果,所以底色的绘制区域为 padding-box,渐变背景的绘制区域为 border-box
  • 上面所说的覆盖背景,其实指的是覆盖背景区域,而元素的背景区域是由 background-origin 属性来决定的,默认值是 padding-box,故默认情况下,渐变背景是填不满整个元素的,需要改为 border-box

效果如下:

6160930-8a7ad3fad464bf71.png
background-image

这样我们就实现了渐变边框的效果,且支持圆角属性。

动态的渐变边框

既然渐变边框是背景模拟出来的,那我们也可以将线性渐变改为圆锥渐变(conic-gradient),这样就有了彩虹色的边框,当然这需要浏览器的支持。

.btn {
  background-image: linear-gradient(#eee, #eee),
  conic-gradient(#ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000);
}

效果如下:

6160930-33ddab1c78cc149c.png
conic-gradient

如果我们想让边框旋转起来,我们可以先舍弃上面的解决方案,而是用两个元素,一个元素充当渐变背景,一个元素覆盖到背景上面,然后两者做反向的旋转动画即可,但我们偏不这么做,就是要用一个元素做到旋转效果,该如何做呢?

其实我们可以利用 CSS 自定义属性及 @keyframe 去实现,首先定义6个基本颜色构成一个数组,然后在动画的每一帧,进行颜色数组的左移操作,就能实现旋转效果。

scss 代码如下:

$colors: (#ff0000), (#ffff00), (#00ff00), (#00ffff), (#0000ff), (#ff00ff);
$colorSize: length($colors);

@function getIndex($len, $index) {
  @if $index > $len {
    @return $index - $len;
  }
  @return $index;
}

.btn {
  @for $i from 1 through $colorSize {
    #{--color + $i}: nth($colors, $i);
  }
  background-image: linear-gradient(#eee, #eee),
  conic-gradient(var(--color1), var(--color2), var(--color3), var(--color4), var(--color5), var(--color6), var(--color1));
  animation: rotate 5s linear infinite;
}

@keyframes rotate {
  @for $i from 1 through $colorSize {
    #{$i * 100% / $colorSize} {
      @for $j from 1 through $colorSize {
        #{--color + $j}: nth($colors, getIndex($colorSize, $i + $j));
      }
    }
  }
}

效果如下:

6160930-6376c93f63080e88.gif
animation

可以看到动画是一帧一帧的而不是平滑的,这是因为自定义属性本身是不能动画化的,不过现在有了css houdini,这样我们就能手动注册自定义属性,使得自定义属性可以动画化。

增加 js:

  const colors = [
    '#ff0000',
    '#ffff00',
    '#00ff00',
    '#00ffff',
    '#0000ff',
    '#ff00ff',
  ];
  colors.forEach((color, index) => {
    CSS.registerProperty({
      name: `--color${index + 1}`,
      syntax: '<color>',
      initialValue: color,
      inherits: true,
    });
  });

增加 scss:

.btn {
  background-size: 200% 200%;
}

效果如下:

6160930-d400479e0ca5992c.gif
animation

这样就由一帧一帧的动画变成了平滑的动画,把 background-size 设置成 200% 200%,可以使动画看起来更舒服。

注:conic-gradientcss houdini 都需要浏览器支持,本文代码在 chrome 71 可完美运行,代码可至 github 查看。

技术总结

我们可以利用 background-clipbackground-origin 去完成渐变边框的需求,利用 CSS.registerProperty 使得自定义属性也能够和其他属性一样运行在动画中。

其实我们还可以使用 clip-path 对渐变背景的显示区域进行裁剪,从而也模拟出渐变边框的效果,但这种方案对圆角属性的支持也不算太好,故本文暂不讨论该方案。

本文如有纰漏之处,还请各位大神指出,有其他问题也可以在评论中提出,谢谢大家。

猜你喜欢

转载自blog.csdn.net/weixin_33753845/article/details/87317800