使用css3实现不规则边框

7,705 阅读11分钟

使用css3实现不规则边框

——denghuan 2022.4.1

背景

今年做了很多可视化的项目,在css上的学习与使用也是加深了许多。这样的项目中有很多小部件背景复杂度不高,但是在项目中以不同的颜色出现复用,为了减少图片的加载,尝试使用css来实现一些效果,特别是不规则的边框。

知识储备

本篇中会使用到background-image border-image clip-path mask box-shadow 等相关属性

实现

1. 使用border-image

​ 使用border-image来实现边框其实并不属于本篇内容的范畴,但是这里会稍微介绍一下,使用border属性能实现dotted dashed solid三种类型的纯色边框,而border-image允许我们使用渐变或者位图来使边框呈现更多种的形式,值得一提的是在使用border-image后,border-radiusborder-style设置的样式会失效,所以虽然它赋予边框更多形式,但是也有一些局限,无法满足到我们进一步的开发需求。

2. 使用background-image来实现border

​ 在对background-image有更深入的了解之前,我只会在使用图片当背景的时候使用到它的属性background-size,但是事实上在使用渐变来当背景的时候也可以通过设置background-sizebackground-position在容器中各个位置摆放任意大小的色块,更加灵活的是,background-image允许设置多个,所以理论上我们可以实现控制容器背景每一个像素点的颜色。

首先看一下比较常见的边框:8.png

这个边框宽度为2px,四角上分别有一个6px * 6px的矩形块

平常的实现方式是利用两个伪元素,如下所示,这种方法的缺点在于,把两个伪元素都使用完了,代码量大

.box {
    position: relative;
    border: 2px solid #ffa500;
}
.box::before,
.box::after {
    content: '';
    position: absolute;
    box-sizing: border-box;
    left: 0;
    width: 100%;
    height: 6px;
    border-color: #ffa500;
    border-width: 0 6px;
    border-style: solid;
}
.box::before { top: 0; }
.box::after { bottom: 0; }

而使用background-image实现,不需要额外的元素支持,代码如下,逻辑是定义了四个6px * 6px的渐变色块,定位在了容器四个角

.box {
    border: 2px solid #ffa500;
    background: linear-gradient(#ffa500, #ffa500),
      linear-gradient(#ffa500, #ffa500) right top,
      linear-gradient(#ffa500, #ffa500) left bottom,
      linear-gradient(#ffa500, #ffa500) right bottom;
    background-repeat: no-repeat;
    background-size: 6px 6px;
}

伪元素可以帮我们实现简单的边框,当边框线条复杂起来,就需要添加更多子元素来实现了,比如这样的效果

2.png

但是使用background-image,我们可以通过更多的色块摆放与堆叠实现更加复杂的效果

.box {
    background: linear-gradient(#ffa500, #ffa500) left top,
      linear-gradient(#ffa500, #ffa500) left top,
      linear-gradient(#ffa500, #ffa500) right top,
      linear-gradient(#ffa500, #ffa500) right top,
      linear-gradient(#ffa500, #ffa500) left bottom,
      linear-gradient(#ffa500, #ffa500) left bottom,
      linear-gradient(#ffa500, #ffa500) right bottom,
      linear-gradient(#ffa500, #ffa500) right bottom;
    background-repeat: no-repeat;
    background-size: 2px 10px, 10px 2px;
}

以上展示的都是规则的矩形

我们最常用的渐变是线性渐变,线性渐变可以调整线性渐变的角度,控制一部分为透明,从而实现三角形,梯形等

.box {
    background: linear-gradient(135deg, transparent 20%, #ffa500 20%),
      linear-gradient(-135deg, transparent 20%, #ffa500 20%) right top,
      linear-gradient(45deg, transparent 20%, #ffa500 20%) left bottom,
      linear-gradient(-45deg, transparent 20%, #ffa500 20%) right bottom;
    background-repeat: no-repeat;
    background-size: 50% 50%;
}

实现效果:4.png

除了线性渐变,径向渐变也可以用来实现一些需要弧度的边框,下面是使用径向渐变来实现内圆角的效果

.box {
    background: radial-gradient(circle at left top, transparent 20%, #ffa500 20%),
      radial-gradient(circle at right top, transparent 20%, #ffa500 20%) right top,
      radial-gradient(circle at left bottom, transparent 20%, #ffa500 20%) left bottom,
      radial-gradient(circle at right bottom, transparent 20%, #ffa500 20%) right bottom;
    background-repeat: no-repeat;
    background-size: 50% 50%;
}

实现效果:5.png

如上面说到的,使用background-image理论上我们可以实现控制容器背景每一个像素点的颜色。通过一些计算,能够实现大多数我们想要的效果。我们可以赋予每一块色块不同的渐变颜色,但是如果我们想要实现虚线渐变呢?在这种情况下,为单独的色块赋予颜色的background-image在连贯性上实在无法完全满足我们的需求

3. 灵活的mask

想要得到一个虚线渐变的边框,我们无法简单地通过border-imageborder-style:solid来实现,我们可以采用反向镂空的方式。

.box {
    border: 2px dashed #fff;
    background: linear-gradient(#fff, #fff),
      linear-gradient(90deg, #24ea7f, #ffa08c);
    background-origin: border-box;
    background-clip: padding-box, border-box;
 }

实现效果:

6.png

这里使用一个白色色块和渐变色块重叠,通过裁剪将渐变块的外圈露出来,然后设置白色的虚线边框,使看起来是渐变边框,我们很明显就能察觉到这个方法是很局限的,它要求容器背后区域是没有透明度且是纯色的,而在可视化项目中很难派上用场。

我们可以使用maskmask定义一个遮罩,使用遮罩后原图形只展示遮罩非透明的部分,使用是与background类似的

.box {
    border: 2px solid transparent;
    border-image: linear-gradient(90deg, #24ea7f, #ffa08c) 2;
    -webkit-mask:
      linear-gradient(90deg, #000 4px, transparent 4px) repeat-x,
      linear-gradient(#000 4px, transparent 4px) repeat-y,
      linear-gradient(90deg, #000 4px, transparent 4px) repeat-x 0 100%,
      linear-gradient(#000 4px, transparent 4px) repeat-y 100% 0;
    -webkit-mask-size: 8px 2px, 2px 8px;
}

实现效果:

7.png

以上的效果实际上可以分解为这样

11.png

使用border-image实现了左边渐变的边框,定义了右边的遮罩,由于规则只展示遮罩非透明的部分,两者叠加就形成了虚线的效果

mask也是十分灵活的,它提供了repeat,position,size等属性的设置,它理论上也能控制到容器的每一个像素点,但是和background不同的是,它依靠的是展示或者隐藏容器其他区域的样式来达到效果,自身并不会展示,background是通过自身直接设置

上面展示的还是较规整的矩形边框,想要实现曲折一些的边框呢?

目标效果:

10.png

我们只需要给容器一个to right的渐变,使用mask,将我们的边框刻画一遍

.box {
    background-image: linear-gradient(90deg, #24ea7f, #ffa08c);
    -webkit-mask:
      linear-gradient(90deg, #000 80%, transparent 80%) no-repeat,
      linear-gradient(#000 80%, transparent 80%) no-repeat,
      linear-gradient(90deg, transparent 20%, #000 20%) no-repeat 0 100%,
      linear-gradient(transparent 20%, #000 20%) no-repeat 100% 0,
      linear-gradient(
        45deg,
        transparent 10%,
        #000 10%,
        #000 calc(10% + 2px),
        transparent calc(10% + 2px),
        transparent calc(90% - 2px),
        #000 calc(90% - 2px),
        #000 90%,
        transparent 90%
      );
    -webkit-mask-size: 100% 2px, 2px 100%, 100% 2px, 2px 100%, 100% 100%;
}

mask还有更多的使用,如使用图像做遮罩。能弥补background-image的一些限制

4. 强大的clip-path

clip-path使用裁剪方式创建元素的可显示区域。区域内的部分显示,区域外的隐藏。

它首先提供了inset circle ellipse polygon四种基础图形

/* 边框为了区分边缘为父元素设置的,背景颜色设置等代码已省略  */
.box:nth-child(1) {
  clip-path: inset(50% 0% 0% 50%);
}
.box:nth-child(2) {
  clip-path: circle(25% at 25% 25%);
}
.box:nth-child(3) {
  clip-path: ellipse(50% 25% at 50% 50%);
}
.box:nth-child(4) {
  clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}

实现效果:

12.png

polygon为多边形,我们可以定义多个点的坐标,从而裁剪出目标图形

polygon已经能够满足我们日常开发中的绝大多数场景,但是它最令人赞叹的是支持通过path来切割图形,语法来源于svg,换言之,单独svg能实现的图形,我们都能通过clip-path切割出来

以下两段代码切割了一个回型镖和一棵树

.box {
  clip-path: path('M 0,0 A 80,80 0,0,1 80,80 A 80,80 0,0,1 0,0 M 80,0 A 80,80 0,0,1 0,80 A 80,80 0,0,1 80,0 z')
}
.box {
  clip-path: path('M 36,80 h 8 v -20 H 80 l -40,-20 h 32 l -32,-20 h 20 l -20,-20 l -20,20 h 20 l -32,20 h 32 l -40,20 h 36 L 36,80 z');
}

实现效果:

13.png

maskbackground-image依靠将不规则边框分解成多个规则图形,然后通过设置多个遮罩或者图像组合起来达到效果,而clip-path是通过定位图形所有拐点来直接勾勒出一个完整图形,在制作实心且拐点多的边框时会有优势

目标效果:

14.png

/* clip-path */
.box {
  background-color: #24ea7f;
  clip-path: polygon(0% 0%, 70% 0%, 100% 50%, 70% 100%, 0% 100%, 30% 50%);
}
/* mask */
.box {
  background-color: #24ea7f;
  -webkit-mask:
    linear-gradient(56deg, transparent 25%, #000 25%, #000 75%, transparent 75%) no-repeat,
    linear-gradient(124deg, transparent 25%, #000 25%, #000 75%, transparent 75%) no-repeat 0 100%;
  -webkit-mask-size: 100% 50%;
}
/* background */
.box {
  background:
    linear-gradient(56deg, transparent 25%, #24ea7f 25%, #24ea7f 75%, transparent 75%) no-repeat,
    linear-gradient(124deg, transparent 25%, #24ea7f 25%, #24ea7f 75%, transparent 75%) no-repeat 0 100%;
  background-size: 100% 50%;
}

可以看出maskbackground-image需要通过三角函数去计算渐变角度,而且如果以后宽高比发生变化又需要重新计算。

clip-pathmask有一个缺点是我们他们实际是控制容器的部分显示,所以当我们设置box-shadow或者filter:drop-shadow()之类的属性,不包含在切割区域内的阴影也不会显示,还是推荐使用伪元素来制作整个边框装饰

5. 多重边框

html元素只有一个边框,如果我们想要制作多重的边框,该怎样通过css实现呢

box-shadow

box-shadow(offset-x offset-y blur-radius spread-radius color),设置spread-radius能向四周增加对应颜色的阴影,能够产生类似边框的效果,又由于box-shadow允许设置多个,由此实现多重边框

.box {
    border-radius: 20px;
    box-shadow: 0 0 0 2px #24ea7f, 0 0 0 4px #ffa08c, 0 0 0 6px #4197be, 0 0 0 8px #9daaff; 
  }

实现效果:

15.png

box-shadow支持圆角,如果容器背后是纯色额话,还可以通过间隔设置一层底色阴影达到镂空多重边框的效果

outline

outline border使用很类似,但是outline不占据空间,且不支持圆角

.box {
    border-radius: 20px;
    border: 2px solid #24ea7f;
    outline: 2px solid #ffa08c;
  }

实现效果:

16.png

background-clip

background-clip主要用到以下几个属性

.box {
  display: inline-block;
  vertical-align: top;
  box-sizing: border-box;
  width: 120px;
  height: 40px;
  margin: 16px;
  padding: 5px;
  text-align: center;
  font-size: 12px;
  line-height: 26px;
  border: 2px dashed #000;
  background: linear-gradient(90deg, #24ea7f, #ffa08c);
}
.box:nth-child(1) {
  background-clip: border-box;
}
.box:nth-child(2) {
  background-clip: padding-box;
}
.box:nth-child(3) {
  background-clip: content-box;
}
.box:nth-child(4) {
  color: transparent;
  -webkit-background-clip: text;
}

效果:

17.png

所以实现多重边框我们主要是利用padding-boxcontent-box重叠,将设置的padding部分的背景圆环显示出来,达到边框效果,相较于上两种方法,它能够实现渐变的边框且支持圆角,但是局限是需要底色是纯色无透明度的。

.box {
  padding: 2px;
  border: 2px solid #9daaff;
  border-radius: 20px;
  background: linear-gradient(90deg, #fff, #fff), linear-gradient(90deg, #24ea7f, #ffa08c);
  background-clip: content-box,padding-box;
}

实现效果:

18.png

总结

在本篇中提到的css属性远比文中展示的实现效果要强大,在其他的开发需求中也能起到一定的作用,希望本文能帮助大家对这些css属性有更多的认识和学习兴趣。