前言
我们在 css 里可以直接使用 border
属性指定元素的边框,但这样的方法具有局限性,就是只能添加单色的边框,如果需要给元素添加渐变的边框,又该如何实现呢?
利用 border-image
我们可以使用 border-image 和 linear-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-gradient
和css houdini
都需要浏览器支持,本文代码在 chrome 71 可完美运行,代码可至 github 查看。
技术总结
我们可以利用 background-clip
和 background-origin
去完成渐变边框的需求,利用 CSS.registerProperty
使得自定义属性也能够和其他属性一样运行在动画中。
其实我们还可以使用 clip-path
对渐变背景的显示区域进行裁剪,从而也模拟出渐变边框的效果,但这种方案对圆角属性的支持也不算太好,故本文暂不讨论该方案。
本文如有纰漏之处,还请各位大神指出,有其他问题也可以在评论中提出,谢谢大家。