渐变边框的实现方式

前言

我们在 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 对渐变背景的显示区域进行裁剪,从而也模拟出渐变边框的效果,但这种方案对圆角属性的支持也不算太好,故本文暂不讨论该方案。

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容