chokcoco / iCSS

不止于 CSS
19.64k stars 2.01k forks source link

巧妙实现带圆角的渐变边框 #77

Open chokcoco opened 4 years ago

chokcoco commented 4 years ago

如何实现下面这个渐变的边框效果:

image

这个问题本身不难,实现的方法也有一些,主要是有一些细节需要注意。

border-image

border-image 是 CSS 规范 CSS Backgrounds and Borders Module Level 3 (最新一版的关于 background 和 border 的官方规范) 新增的一个属性值。

顾名思义,我们可以给 border 元素添加 image,类似于 background-image,可以是图片也可以是渐变,不再局限于纯色。

使用 border-image 实现渐变边框

有了 border-image 之后,实现渐变边框变得很方便

不过多介绍 border-image 的语法,读者需要自行了解一下。

实现如下:

<div class="border-image"></div>
.border-image {
    width: 200px;
    height: 100px;
    border-radius: 10px;
    border-image-source: linear-gradient(45deg, gold, deeppink);
    border-image-slice: 1;
    border-image-repeat: stretch;
}

上面关于 border-image 的三个属性可以简写为 border-image: linear-gradient(45deg, gold, deeppink) 1;

得到如下结果:

image

border-radius 失效

仔细看上面的 Demo,设置了 border-radius: 10px 但是实际表现没有圆角。使用 border-image 最大的问题在于,设置的 border-radius 会失效。

我们无法得到一个带圆角的渐变边框。原因,查看官方规范 W3C 解释如下:

A box's backgrounds, but not its border-image, are clipped to the appropriate curve (as determined by ‘background-clip’). Other effects that clip to the border or padding edge (such as ‘overflow’ other than ‘visible’) also must clip to the curve. The content of replaced elements is always trimmed to the content edge curve. Also, the area outside the curve of the border edge does not accept mouse events on behalf of the element.

为此,我们得另辟蹊径或者稍加改进,得到带圆角的渐变边框。

法一:background-image + 伪元素

第一种方法,我们不再使用 border-image ,而是使用 background-image + 伪元素 的方案,这也是在 border-image 规范没有出现最常用的方法。

非常简单,简单的示意图如下:

bgof

利用 background-image 实现一个渐变背景,再通过叠加一个白色背景使之形成一个渐变边框。

CodePen Demo -- bg + overflow 实现渐变边框

缺点

这个方案有两个问题,第一个是多使用了两个元素(当然在这里是 ::before 和 ::after),其次最致命的是,如果要求边框內的背景是透明的,此方案便行不通了。

法二,使用 background-clip 实现

第二种方法,使用 background-clip: content-box 以及 background-clip: border-box 配合使用。

background-clip:background-clip 设置元素的背景(背景图片或颜色)是否延伸到边框下面。它的部分取值和 box-sizing 类似。其中,

这里,我们使用设置两个 background-image,设置两个 background-clip ,并且将 border 设置为透明即可:

核心 CSS:

div {
    width: 200px;
    height: 100px;
    border: solid 10px transparent;
    border-radius: 10px;
    background-image: linear-gradient(#fee, #fee),
        linear-gradient(to right, green, gold);
    background-origin: border-box;
    background-clip: content-box, border-box;
}

image

因为用到了 background-clip: border-box,所以还需要 background-origin: border-box 使图案完整显示,可以尝试下关掉这个属性,即可明白为什么需要这样做。

CodePen Demo -- background-clip 实现渐变边框

缺点

与第一种方法类似,如果要求边框內的背景是透明的,此方案便行不通了。

法三:border-image + overflow: hidden

这个方法也很好理解,既然设置了 background-image 的元素的 border-radius 失效。那么,我们只需要给它加一个父容器,父容器设置 overflow: hidden + border-radius 即可:

<div class="border-image-overflow"></div>
.border-image-pesudo {
    position: relative;
    width: 200px;
    height: 100px;
    border-radius: 10px;
    overflow: hidden;
}

.border-image-pesudo::before {
    content: "";
    position: absolute;
    width: 200px;
    height: 100px;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    border: 10px solid;
    border-image: linear-gradient(45deg, gold, deeppink) 1;
} 

效果如下:

image

当然,这里还是多借助了一个元素实现。还有一种方法,可以不使用多余元素实现:

法四:border-image + clip-path

设置了 background-image 的元素的 border-radius 失效。但是在 CSS 中,还有其它方法可以产生带圆角的容器,那就是借助 clip-path

[clip-path](https://developer.mozilla.org/zh-CN/docs/Web/CSS/clip-path),一个非常有意思的 CSS 属性。

clip-path CSS 属性可以创建一个只有元素的部分区域可以显示的剪切区域。区域内的部分显示,区域外的隐藏。剪切区域是被引用内嵌的URL定义的路径或者外部 SVG 的路径。

简而言之,这里,我们只需要在 border-image 的基础上,再利用 clip-path 裁剪出一个带圆角的矩形容器即可:

<div class="border-image-clip-path"></div>
.border-image-clip-path {
    position: relative;
    width: 200px;
    height: 100px;
    border: 10px solid;
    border-image: linear-gradient(45deg, gold, deeppink) 1;
    clip-path: inset(0 round 10px);
}

解释一下:clip-path: inset(0 round 10px)

非常完美,效果如下:

image

当然,还可以利用 filter: hue-rotate()顺手再加个渐变动画:

clipboder

你可以在我 CSS-Inspiration 看到这个例子:

CSS-Inspiration -- 使用 clip-path 和 border-image 实现圆角渐变边框

最后

好了,本文到此结束,希望对你有帮助 :)

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

更多精彩有趣的 CSS 效果,欢迎来这里看看 CSS 灵感 -- 在这里找到写 CSS 的灵感

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

jun266a commented 4 years ago

边框图像可以使用径向渐变和颜色透明制作圆角边框。再叠加角向渐变,,可以制作渐变的圆角边框,嗯。

helsonxiao commented 2 years ago

clip-path 的兼容性相对弱一些,border-image + overflow: hidden 更简单易用吧。

image

zhen-one commented 1 year ago

边框是虚线呢,如何实现

lnjiatao commented 1 year ago

border-radius 大于 border-width 时 后两种写法 好像有些问题

image

Air-EL commented 3 months ago

image

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box::before {
            content: "";
            position: absolute;
            width: 140px;
            height: 80px;
            z-index: -1;
            border-radius: 100px;
            background: conic-gradient(#ff00fa, #fe3, #0f3, #ff00fa);
            padding: 2px;
            -webkit-mask:
                linear-gradient(#fff 0 100%) content-box,
                linear-gradient(#fff 0 100%);
            -webkit-mask-composite: xor;
            box-sizing: border-box;
        }

        .box {
            position: relative;
            width: 140px;
            height: 80px;
            z-index: 1;
            box-sizing: border-box;
            display: flex;
            justify-content: center;
            align-items: center;
            transform: scale(0.7);
        }

        .box>div {
            text-align: center;
            font-family: Helvetica;
            font-size: 50px;
            height: 80px;
            /* 手动偏移,使字体居中 */
            line-height: 72px;
        }

        /*实现文字颜色从红到黄的线性渐变效果*/
        .gradient-text {
            background: conic-gradient(#ff00fa, #fe3, #0f3, #ff00fa);
            /*设置渐变的方向从左到右 颜色从ff0000到ffff00*/
            -webkit-background-clip: text;
            /*将设置的背景颜色限制在文字中*/
            -webkit-text-fill-color: transparent;
            /*给文字设置成透明*/
        }

    </style>
</head>

<body>
    <div class="box gradient-text">
        <div>css</div>
    </div>
</body>

</html>
Air-EL commented 3 months ago

在尝试实现此文章中的效果, 如评论

但有2处感觉不够优雅

  1. 文字渐变颜色的实现,使用了额外的渐变色
  2. 通过手动偏移使文字 css 垂直居中,若改成大写 CSS,则会偏上

感谢 @chokcoco 的指点