美文网首页简书技术团队
渐变边框的实现方式

渐变边框的实现方式

作者: z4d | 来源:发表于2019-01-18 20:04 被阅读109次

    前言

    我们在 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;
    }
    

    效果如下图所示:

    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

    效果如下:

    background-image

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

    动态的渐变边框

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

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

    效果如下:

    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));
          }
        }
      }
    }
    

    效果如下:

    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%;
    }
    

    效果如下:

    animation

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

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

    技术总结

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

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

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

    相关文章

      网友评论

        本文标题:渐变边框的实现方式

        本文链接:https://www.haomeiwen.com/subject/zfvurqtx.html