Table of Contents
需求功能
需要实现一个渐变圆角边框的 div,且不仅边框是渐变色,要求 div 内部也是另一种带透明度的渐变色。(注意,下面所有示例都基于 css 属性 box-sizing: border-box)
实现方案
方案 1——双层 div 叠加
能想到最直接的方案就是两层 div 叠加,外层 div 较大,负责边框渐变色,内层 div 较小,负责内部区域颜色。但是由于是两层 div 叠加,内层要实现的渐变色如果没有透明度,是没问题的;若是内层 div 要实现的渐变色带有透明度,外层颜色就会透过透明的内层显示出来,这个方案就不适用了。
内层无透明度,正常显示
内层有透明度,底色透出来了
<div class="test1"> <div class="test1-inner"></div></div>
<style> .test1 { width: 200px; height: 100px; background-image: linear-gradient(to right, #9c20aa, #fb3570); border-radius: 6px; position: relative; } .test1-inner { /* 内比外小,尺寸差为边框宽度的两倍 */ width: 190px; height: 90px; /* 内层居中 */ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); /* 内层渐变色,透明度必需为1,否则外层色露出,故不适用内层有渐变色的渐变 */ background-image: linear-gradient( to right, rgba(128, 128, 128, 1), rgba(0, 0, 0, 1) ); /* 内层圆角 */ border-radius: 6px; }</style>方案 2——边框图片
使用 css 边框图片属性 border-image 实现,不需要嵌套 div 实现,更直观,也支持带有透明度的背景色,但缺点是不支持圆角设置。
<div class="test2"></div>
<style> .test2 { width: 200px; height: 100px; background: linear-gradient( to right, rgba(128, 128, 128, 0.5), rgba(0, 0, 0, 0) ); border: 5px solid; /* 背景图片 */ border-image-source: linear-gradient(to right, #9c20aa, #fb3570); /* 背景图片九宫格切割参数,决定背景图片在边框上的展示范围 */ /* 由于我们是渐变色,切割参数设置为非0值即可,因为切割区域大小,不影响渐变色的展示 */ border-image-slice: 1; /* 圆角设置,仅对纯色边框生效,对边框图片属性无效 */ border-radius: 6px; }</style>方案 3——双层背景叠加
与方案 1 原理一样,只不过不再使用两个 div,而是直接使用 css 背景图的多层叠加特性实现。缺点也同方案 1 一样,不支持内层有透明度。
需要特别注意的是,多层背景图片,后书写的在底层,先书写的在顶层。
内层无透明度,正常显示
内层有透明度,底色透出
<div class="test3"></div>
<style> .test3 { width: 200px; height: 100px; /* 通过 padding 留出边框距离 */ padding: 5px; /* 两层背景图设置,注意两层间用逗号分隔,顶层依然无法支持带透明的颜色 */ background-image: linear-gradient(to right, rgba(128, 128, 128, 1), rgba(0, 0, 0, 1)), linear-gradient(to right, #9c20aa, #fb3570); /* 顶层占用 content-box,底层占用 border-box */ background-clip: content-box, border-box; border-radius: 6px; }</style>方案 4——css mask
使用 css 的 mask 属性,可以说是最佳实现方案了,可以完美支持边框渐变、中间区域背景带有透明度、边框圆角等所有需求。不过由于还处于推荐候选草案(Candidate Recommendation Draft)阶段,兼容性稍差,IE 浏览器不支持,webkit 内核的浏览器需要增加-webkit-前缀,一般脚手架已通过 babel 做自动兼容,无需手动添加前缀。不过下面的示例中,还是都添加了。
实现原理:先建立一个 div,为 div 设置圆角和内边距(内边距即最终的边框宽度),并设置为相对位置position: relative(方便内部伪元素相对该元素定位);再建立一个伪元素使用 mask 来实现圆角渐变的边框,伪元素与外层 div 圆角值及内边距一致即可,伪元素使用绝对定位,这样父元素使用任何布局都不会影响到伪元素。
<div class="test4"></div>
<style> .test4 { width: 200px; height: 100px; padding: 5px; border-radius: 6px; position: relative; background: linear-gradient( to right, rgba(128, 128, 128, 0.5), rgba(0, 0, 0, 0) ); } .test4::before { content: ""; /* 绝对定位,四个方向值都设置为0,不设宽高,相当于宽高100% */ position: absolute; left: 0; top: 0; right: 0; bottom: 0; /* 内边距与圆角值与外部一样,内边距表示边框宽度 */ padding: 5px; border-radius: 6px; /* 背景渐变色为边框色 */ background: linear-gradient(to right, #9c20aa, #fb3570) border-box; /* 设置两层全透明遮罩,上层后绘制,故称为源层,在内边距以内,下层先绘制,故为目标层,在边框以内 */ /* 注意 这里与多层背景先写的 */ mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0) border-box; /* exclude 即排除多层 mask 重叠部分,exclude 仅 firefox 支持 */ mask-composite: exclude; /* webkit 兼容写法 */ -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0) border-box; /* webkit 支持属性与 firefox 不一致,更像是 Canvas 的 globalCompositeOperation 属性,只是不如 Canvas 中支持的属性多,且个别属性表现行为稍有不一致。*/ /* destination-out 指 目标遮罩与源遮罩重叠部分全部排除,显示为透明,仅保留目标遮罩不重叠部分 */ -webkit-mask-composite: destination-out; }</style>最佳方案中用到了 CSS Mask 属性,可以在另一篇文章——css遮罩mask中详细了解 CSS Mask。
参考资料
Border Gradient with Border Radius
Possible to use border-radius together with a border-image which has a gradient?