漸變邊框的實現方式

前言

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

效果如下圖所示:

這樣就完成了基本的漸變邊框,但這種方法並不能支持圓角屬性,除非我們直接使用帶圓角的圖片。

利用 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

效果如下:

這樣我們就實現了漸變邊框的效果,且支持圓角屬性。

動態的漸變邊框

既然漸變邊框是背景模擬出來的,那我們也可以將線性漸變改爲圓錐漸變(conic-gradient),這樣就有了彩虹色的邊框,當然這需要瀏覽器的支持。

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

效果如下:

如果我們想讓邊框旋轉起來,我們可以先捨棄上面的解決方案,而是用兩個元素,一個元素充當漸變背景,一個元素覆蓋到背景上面,然後兩者做反向的旋轉動畫即可,但我們偏不這麼做,就是要用一個元素做到旋轉效果,該如何做呢?

其實我們可以利用 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));
      }
    }
  }
}

效果如下:

可以看到動畫是一幀一幀的而不是平滑的,這是因爲自定義屬性本身是不能動畫化的,不過現在有了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%;
}

效果如下:

這樣就由一幀一幀的動畫變成了平滑的動畫,把 background-size 設置成 200% 200%,可以使動畫看起來更舒服。

注:conic-gradientcss houdini 都需要瀏覽器支持,本文代碼在 chrome 71 可完美運行,代碼可至 github 查看。

技術總結

我們可以利用 background-clipbackground-origin 去完成漸變邊框的需求,利用 CSS.registerProperty 使得自定義屬性也能夠和其他屬性一樣運行在動畫中。

其實我們還可以使用 clip-path 對漸變背景的顯示區域進行裁剪,從而也模擬出漸變邊框的效果,但這種方案對圓角屬性的支持也不算太好,故本文暫不討論該方案。

本文如有紕漏之處,還請各位大神指出,有其他問題也可以在評論中提出,謝謝大家。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章