在写第一期有关于CSS技巧的时候立下过一个Flag,即每周末发布一篇有关于自己在本周看到过有关于CSS的小技巧(有意思的CSS)。但由于上周末去深圳参回第五届CSS Conf,没有如期整理发布,所以今天整理了发出来。在这期的小技巧中,大部分是与边框相关的故事。希望大家会喜欢。
说到边框,估计很多同学都会想到CSS的border
属性。ref="https://www.w3.org/TR/css-backgrounds-3/#borders">CSS的border也是CSS中重要属性之一,现在和background
相关属性编入在同一个模块之中(CSS Backgrounds and Borders Module Level 3),同时也是CSS盒模型中的重要部分之一。对于怎么给一个元素添加边框,我想这没有任何的技术含量。如果我换着说:
border-style: solid
),你能想到哪些方法?这个时候估计不少同学会有很多疑惑吧!没有任何疑惑的同学,请跳过这个部分继续往下阅读你感兴趣的部分,如果有疑惑的同学,请花点时间阅读,最好是自己能实战一下,加强这方面的理解。
这里所说的Solid边框是指border-style
属性取值为border
的边框。我想问的是,在CSS中你可以想出多少种方案来实现类似于Solid的边框效果。如下图所示:
请转动你的大脑,大声的告诉自己:
我能使用xxx种方案来实现类似上图的边框效果。
在继续之前,我再添加一个限制性的条件,请在单个元素上实现类似border-style: solid
的边框效果。下面是我个人想到的一些方法:
首先想到的是CSS的outline
属性,outline
和border
非常的类似,可以使用outline-style: solid
来实现。
[class="outline"] {
outline: 1em solid;
}
注意:outline
是不占用盒模型空间的!
如果不添加限制条件的话,我们可以通过多个元素来,给容器元素添加一个背景颜色,内元素设置另一个颜色并且设置TRBL
值来模拟边框宽度。如果有限制条件的话,那么可以借助伪元素来替代:
[class="pseudo elements"] {
background: linear-gradient(to right, #fff, #fff);
position: relative;
padding: 1em;
background-clip: content-box;
&::after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #000;
z-index: -1;
}
}
为了容器大小更好的控制,我们在容器中设置了一个padding
值来模拟边框的宽度,在伪元素中设置了一个背景色,通过z-index:-1
让整个伪元素在z
轴的位置在下面,以免遮住元素的内容。因为伪元素的整个大小和容器大小是一致的,整个black
为覆盖容器,这里借助linear-gradient()
设置一个纯色(#fff
),并且background-clip
来控制裁切位置。从而模拟出一个边框的效果。
这种方法还可以调整其他的参数来实现类似的效果,感兴趣的同学自己可以尝试一下。
事实上,用伪元素来模拟一层颜色有点浪费。在CSS中的多背景特性会更方便一些,也更易于处理,唯一要注意的是颜色的层级(一般采用渐变来处理,纯色的话可以通过设置两个相同颜色)和 background-clip
来控制背景裁切区域:
[class="linear-gradient"] {
padding: 1em;
background: linear-gradient(to right, #fff, #fff), #000;
background-clip: content-box, padding-box;
}
在box-shadow中第四个参数扩展(spread)可以用来模拟一个硬阴影,也就是说,如果我们前三个参数都设置为
0
的时候,可以用其来模拟一个实体边框效果,比如:
[class="box-shadow"] {
box-shadow: 0 0 0 1em #000;
}
还记得border-image
吧,可以利用他来做图片边框,按这样的原理,如果图片是一个纯色,那是不是也可以实现我们想要的效果呢?答案是的:
[class="border-image"] {
border: 1em solid transparent;
border-image: linear-gradient(to right, #000, #000) 10% round;
}
在线Demo
可能很多人都会认为,直接一个border: 1em solid currentColor
,多么省事的的事情,何必这么的纠结呢?事实上它们之间还是有差异的,不同的方法造成的盒模型大小是不一致的。如果你不想让边框占用盒模型的空间时,可以考虑类似outline
和box-shadow
等方法。特别是在做一些小的动效效果时候,比如鼠标悬浮要改变边框粗细。这些方式特别的有效。
在Web有实际使用的时候,有的时候需要一些多边框的效果。但在CSS的世界中是没有多边框这样的属性,也就是说,我们要实现一个多边框的效果,我们需要借助一些属性结合在一起使用。简单地说,将上面制作边框的属性结合在一起,就可以很容易的绘制多边框的效果。
具体代码可以打开Codepen查阅:
上面演示的是双色边框,如果你还想要更多颜色的边框,你也可以将更多的属性叠加在一起使用,当然其中最为简单的是你还可以使用
box-shadow
的多个值,但这种方式对性能有一定的影响。慎用!
如果我们在多色边框的基础上,将其叠加在一起,又将是一个什么样的效果呢?
只会看到其中一种颜色。
那是两个颜色都是实体线,其中一个遮盖了另一个。如果我们把上面的实所dotted
或dashed
呢?将看到的效果类似下图这样:
而这种效果就是一个层叠边框的效果。实现这样的效果,同样需要将多个属性结合在一起使用。和上面的示例,在CSS中实现层叠边框的效果也有多种方式。
在线Demo。
除了上述的三种方法之外,还可以使用border-image
方法,使用border-image
方法需要备好一种图,作为边框图片源:
左图是边框图片源,右侧是使用下面代码得到的效果:
.borderimage-me {
border: solid 5px;
border-image: url(triple-stack-border.gif) 15 / 15px round;
}
有关于层叠边框的效果更详细的介绍可以阅读 @Eric Meyer 的《Stacked “Borders”》一文。
@Ana Tudor在CSS-Tricks上分享了一篇有关于模糊边框的文章。比如像下面这样的一个效果:
其实就是一个毛玻璃的边框效果。根据前面所学的知识,在CSS中通过filter
、CSS的混合模式和backdrop-filter
等方法都可以实现毛玻璃的效果。
@Ana Tudor在这篇文章中也介绍了几种实现方式,主要运用到了CSS的filter
的blur()
、clip-path
,background-clip
等相关属性,当然还运用到了伪元素之类的。具体效果如下:
在线Demo。
@Leaverou 早在多年前就使用CSS的linear-gradient
、radial-radient
、repeating-linear-gradient
和background-size
等属性绘制纹理背景:
和前面的原理一样,配合background-clip
和background-origin
等属性,可以轻易的实现纹理边框的效果:
在线Demo。
如果阅读了上面的代码的话,可以发现,代码中有一个非常有意思的属性,即background-repeat属性。大家肯定会说,这个属性不是非常的常见吗?难道有什么特别之处吗?事实确实有所不同,在很多同学的认知中,background-repeat
的属性值是repeat
、repeat-x
、repeat-y
和no-repeat
,其实除了这三个值之外还有另外几个值:
round
:像repeat
一样,背景图片会平铺整个容器,但和repeat
不一样的是,他会根据容器尺寸和图片尺寸做一个自适应处理,不会像repeat
一样,造成图片显示不全space
:和round
又会不一样,但也有类似之处。类似之处是,背景图片会平铺整个容器,不会造成背景图片裁剪;但也和round
将不一样,使用space
时,不够整数背景图片平铺整个容器的时候,会将剩余的空间平均分配给相邻背景之间有关于这方面更详细的介绍,可以阅读早前整理的一篇博文《 href=" https://www. w3cplus.com/css3/css3-b ackground-repeat-space-round.html ">单聊background-repeat》。
事实上,除了纯CSS之外,还可以通过CSS的Houdini相关的特性,实现更为复杂的边框效果,比如下面这个效果:
在线Demo。
如果你理解了前面介绍的边框效果,特别是上面的纹理边框效果,那么对于渐变边框的效果明白怎么回事了。就算是你不太明白也不要紧,在《聊聊双11互动主玩法中前端技术亮点》一文中就曾详细的介绍过了具体的实现方案和思路。
在线Demo。
上面看到的边框效果都是静态的,但我们可以借助CSS的animation
属性,让其带有动效。比如下面这个示例:
在线Demo。
有关于CSS实现的边框动画效果还有很多,比如@Girish Karthik 整理的一些效果。如果你想查看更多的效果,可以在Codepen上搜索CSS border animation,你可以获得很多有意思的Demo效果。
波浪边框就是指边框效果有点像水波一样。实现原理和上面的有点类似,就是用径向渐变(radial-gradient()
)和background-size
等相关属性配合使用。比如下面这个示例:
在线Demo。
这是最简单的一种方法,但相对而言局限性是要更大的。在CSS中除了使用渐变的方法之外,还可以尝试着mask
、clip-path
甚至是配合SVG相关来处理。如果您感兴趣的话,可以尝试写写相关的Demo。写出来了,记得在评论中分享哟。
在波浪边框的基础上改造一下,我们就可以实现类似于邮票的边框效果。比如下面这个示例:
[class="gradient"] {
background: darken(#f36, 25%);
margin: 8px;
&::after {
content: "";
position: absolute;
top: -8px;
left: -8px;
right: -8px;
bottom: -8px;
z-index: -1;
background-image: radial-gradient(transparent 0px, transparent 4px, darken(#f36, 25%) 4px, darken(#f36, 25%));
background-size: 15px 15px;
background-position: -7.5px -7.5px;
}
}
你可能看到的效果如下:
看上去效果不错,那是因为伪元素::after
的渐变平铺时刚好的容器大小相吻合。言外之意,上面的邮票边框效果是不佳的,因为有可以渐变在伪元素上平铺时,不一定吻合,有可能会是像下面这样:
所以说,使用这种方式的话,只适合那种容器大小固定的情况,如果是一个流式布局,那么这个效果有可能就会有问题了,如上图所示。在上面的基础上换一下,整个多背景,会比单个背景好控制:
[class="gradient new"] {
background: #3396f5;
color: #fff;
position: relative;
height: 100%;
&::before,
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: radial-gradient(circle, #fff 50%, transparent 50%), radial-gradient(circle, #fff 50%, transparent 50%);
background-size: 20px 20px;
}
&::before {
top: 5px;
text-align: center;
background-repeat: repeat-y;
background-position: -10px 0%, calc(100% + 10px) 0;
}
&::after {
left: 5px;
background-repeat: repeat-x;
background-position: 0 -10px, 0 calc(100% + 10px);
}
}
除了渐变之外,还可以使用border-image
来处理,对于这样简单的邮票边框效果,可以配合.svg
来实使用:
[class="border-image"]{
border-width: 1rem;
border-style: solid;
border-image-source: url(//www.w3cplus.com/sites/default/files/blogs/2019/1904/border.svg);
border-image-slice: 5;
border-image-repeat: round;
border-image-width: 2.5;
border-image-outset: 1.25;
}
使用border-image
有一个不好之处呢?需要不断的修改图片(当颜色发生变更时),因此这里建议使用.svg
文件是有其一定原因存在的,可以在.svg
文件中随时候修改图片颜色,比如上例中的图片:
只需要在修改fill
的值,然后保存即可:
如果你对mask
属性熟悉的话,ef="https://www.w3cplus.com/blog/tags/339.html">还可以使用mask来处理。当然,使用mask
也同样需要一个图片,也可以使用.svg
文件来处理:
将上面的图片运用在mask
相关属性上:
[class="mask"]{
-webkit-mask-box-image: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/stampTiles.svg) 30 round;
mask-border: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/stampTiles.svg) 30 round;
}
有关于邮票边框的效果如下:
在线Demo。
另外,使用该技术配合一些有意思的背景图,你还可以仿做一些古典的邮戳效果(橡皮图章):
.stamp-text{
border:10px solid red;
-webkit-mask-size: contain;
-webkit-mask-image: url("/crack-grunge-texture-PNG-thumb24.png");
}
在线Demo。
手绘边框的思路是来自于@Tiffany Rayside 在Codepen上写的一个按钮效果。
刚开始看到上面的边框效果,我觉得很有意思,一直以为是使用border-image
或svg
来完成的。后来阅读了Demo代码才知道,所谓的手绘边框就是border
和border-radius
做的。这里有一个最关键的地方,就是通过border-radius
来绘制不规则的圆角:
border: .3em solid currentColor;
border-radius: 8vw 2vw 8vw 2vw / 2vw 8vw 2vw 8vw;
效果如下:
在线Demo。
CSS中transform
可以轻易的让元素进行变形转换。如果我们把这个运用在伪元素上,是不是可以让我们做出非常多的不一样的边框效果呢?比如下面这个示例效果:
在线Demo。
原理非常的简单,在伪元素上运用了linear-gradient
绘制渐变色,让边框看上去有一些颜色,然后借助transform: skew()
,并且在不同的元素上改变skew()
函数的x
和y
的值。所以你看到的效果会有所差异。如果你感兴趣的话,可以试用一下其他的transform
还能做出什么样不同的边框效果出来。
上面是一些较为简单的效果,但有一些带切角的边框效果要做起来就比较难了。比如像下图的效果:
早在2013年的时候 @Lea verou就提到过,CSSWG中有一个
border-corner-shape的属性讨论,这个属性就是用来实现类似上图的效果。遗憾的是,这么多年来,都不见有任何的响应。
虽然在CSS中没有直接属性可以实现,但同样的,我们可以借助强大的渐变属性来做相应的事情。这个时候是不是觉得CSS的渐变是TMD万能的。
div {
background:
linear-gradient(135deg, transparent 10px, #c00 0) top left,
linear-gradient(225deg, transparent 10px, #c00 0) top right,
linear-gradient(315deg, transparent 10px, #c00 0) bottom right,
linear-gradient(45deg, transparent 10px, #c00 0) bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;
}
div.round {
background-image:
radial-gradient(circle at 0 0, rgba(204,0,0,0) 14px, #c00 15px),
radial-gradient(circle at 100% 0, rgba(204,0,0,0) 14px, #c00 15px),
radial-gradient(circle at 100% 100%, rgba(204,0,0,0) 14px, #c00 15px),
radial-gradient(circle at 0 100%, rgba(204,0,0,0) 14px, #c00 15px);
}
去年 @Chris Coyier的教程详细介绍了CSS如何实现元素斜切口的效果(@Lea Verou的书中也有相关介绍),最为详细的是@Ana Tudor的教程详细介绍了如何通过使用CSS实现内凹角效果。
你要是花了一定的时间把上面的教程整明白了的话,那么要实现类似于@Bogdan Saliuk写的这个边框效果应该就不会太难。
详细代码可以查阅下面这个Demo。
从字面上不好理解吧,其实就是类似下图这样的效果:
如果使用一个div
的话,代码大致如下:
div {
width: 50vw;
height: 50vh;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.6);
padding: 1vw;
background-color: #9CB3E3;
background-image:
// top line
linear-gradient(to right, rgba(0, 0, 0, 0.8) 60%, transparent 60%, transparent 100%),
linear-gradient(to right, rgba(255, 255, 255, 0.5) 60%, transparent 60%, transparent 100%),
// right line
linear-gradient(to bottom, rgba(255, 255, 255, 0.5) 60%, transparent 60%, transparent 100%),
linear-gradient(to bottom, rgba(0, 0, 0, 0.8) 60%, transparent 60%, transparent 100%),
// bottom line
linear-gradient(to right, rgba(0, 0, 0, 0.8) 60%, transparent 60%, transparent 100%),
linear-gradient(to right, rgba(255, 255, 255, 0.5) 60%, transparent 60%, transparent 100%),
// left line
linear-gradient(to bottom, rgba(0, 0, 0, 0.8) 60%, transparent 60%, transparent 100%),
linear-gradient(to bottom, rgba(255, 255, 255, 0.5) 60%, transparent 60%, transparent 100%),
linear-gradient(to right, #9CB3E3, #9CB3E3);
background-repeat:
repeat-x, repeat-x, // top line
repeat-y, repeat-y, // right line
repeat-x, repeat-x, // bottom line
repeat-y, repeat-y, // left line
repeat;
background-size:
40px 1px,40px 1px, // top line
1px 40px,1px 40px, // right line
40px 1px,40px 1px, // bottom line
1px 40px,1px 40px, // left line
cover;
background-position:
2vw 2vw, 2vw calc(2vw + 1px), // top line
calc(100% - 2vw) 1vw, calc(100% - 2vw - 1px) 1vw, // right line
2vw calc(100% - 2vw), 2vw calc(100% - 2vw + 1px), // top line
3vw 1vw, calc(3vw - 1px) 1vw, // right line
0 0;
background-clip:
content-box, content-box, // top line
content-box, content-box, // right line
content-box, content-box, // bottom line
content-box, content-box, // left line
border-box;
}
在线Demo。
内边框指的是Inner borders,在盒子内部绘制边框。最能想到的方案是box-shadow
,而且是内阴影。另外还是渐变。不过 @iamvdo在CSS Houdini库中提供了另一种方式,就是采用CSS Houdini来绘制各式各样的内边框:
.el--1 {
background: paint(inner-border, 2px, white, 10px, 0deg),
url(https://picsum.photos/256/256?image=62);
}
.el--2 {
background: paint(inner-border, 2px, white, 10px, 0deg),
paint(inner-border, 1px, white, 14px, 0deg),
url(https://picsum.photos/256/256?image=154);
}
.el--3 {
background: paint(inner-border, 2px, black, 10px, -2deg),
paint(inner-border, 2px, black, 10px, 0deg),
paint(inner-border, 2px, black, 10px, 3deg),
url(https://picsum.photos/300/300?image=201);
}
.el--4 {
background: url(https://picsum.photos/256/256?image=14);
-webkit-mask-image: paint(inner-border, 5px, white, 10px, 0deg), linear-gradient(black, black);
-webkit-mask-composite: xor;
mask-image: paint(inner-border, 5px, white, 10px, 0deg), linear-gradient(black, black);
mask-composite: exclude;
}
.el--5 {
background: paint(inner-border, 2px, white, 10px, 0deg) #2785b8;
}
.el--6 {
background: paint(inner-border, 10px, white, 0, -10deg),
url(https://picsum.photos/256/256?image=41);
}
// registerPaint module
registerPaint('inner-border', class {
static get inputArguments() {
return [
'',
'',
'',
''
]
}
paint (ctx, geom, properties, args) {
let pad = parseFloat(args[2].toString());
let width = parseFloat(args[0].toString());
let rot = parseFloat(args[3].toString()) || 0;
let color = args[1].toString();
ctx.strokeStyle = color;
ctx.lineWidth = width;
ctx.translate(geom.width / 2, geom.height / 2);
ctx.rotate(rot * Math.PI / 180);
ctx.translate(-geom.width / 2, -geom.height / 2);
ctx.strokeRect(
pad + (width / 2),
pad + (width / 2),
geom.width - (pad + (width / 2)) * 2,
geom.height - (pad + (width / 2)) * 2);
}
})
如果你的本地能跑起CSS Houdini的话,看到的效果将会如下图所示:
另外仓库中还提供了其他几种边框效果:
上面介绍了14种不同的边框效果。从上面的示例中我们不难再次地发现:
CSS的渐变真的好强大!
使用CSS的渐变属性,再配上多背景特性,box-shadow
、mask
,甚至再上加animation
、混合模式和filter
之类的。我们可以做出很多不同的边框效果。这14种边框效果不一定都实用,但大部分我都在生产环境中有碰到或者说在别人的项目中有看到相关的效果。当然还有很多是我没有碰到的,也没想到的。如果你感兴趣,而且在这方面又有足够的创意和创新,欢迎在下面的评论中分享您的成果。
@Yangguang Li在CSS-Tricks上发了一篇文章,主要讲述了CSS Houdini给CSS带来的变化。暂不说功能上的变化,就从书写CSS和打理CSS两个方面来说,都将有很大的变化。
CSS Houdini的工作方式带来了两个优点:模块化和可配置性。而这两种方法都是我们平时开发中常见的方法。在JavaScript中,经常能听到这方面的概念,但是在CSS有关于这方面的概念是现在才有(也就是CSS Houdini出现之后才有)。@Yangguang 提供了一个工作流的表格,主要比较了传统的CSS和CSS Houdini的工作流差异性。为了能让大家更易于理解,作者还把JavaScript也添加进来:
主要来看CSS Houdini中怎么来做。如果你对Houdini有一定的了解,都知道它有一个worklet
。也就是说,你可以将每个常用的东西都封装成一个模块。使用的时候只需要一行代码注入到你的项目中即可:
言外之意,这样一来你可以拥有自己的模块集合,这些模块集合你可以用于任何项目中,甚至还可以彼此共享。这一点是不是和熟悉的JavaScript非常的相似。写JavaScript的同学都不喜欢重复去实现常用的函数。如果有一天,CSS Houdini得到更好的支持(或更好的流行起来),我们就可以整一个CSS Houdini包管理服务,任何人都可以发布,下载。比如你可以在这个包管理器上轻易的获取到你想要的功能模块。
大部分优秀的JavaScript都会提供一个可配置的API,或相关功能会提供可配置参数选项。在CSS Houdini中也有点类似,她可以很好的处理CSS自定义属性,这样一来在很大程度上增强了它自身的能力。简单地说:
使用CSS自定义属性,用户可以配置一个Houdini Worklet。
比如:
.my-element {
background-image: paint(triangle);
--direction: top;
--size: 20px;
}
这样一来,CSS Houdini工作流可以改变我们编写和维护CSS的方式,也使我们的开发过程更愉悦,效率更高。
为了更好的说明这一点,@Yangguang还提供了一个Tooltip的示例。在传统的开发方式中,它的工作流程大致是:
border
属性来绘制这个三角形一旦你换成了CSS Houdini来开发一个Tooltip,那么有可能是这样的方式了:
worklet
引入到你的项目中(这个worklet有可能在以前的项目中写好了,有可能是你第一次使用CSS Houdini)比如:
This is a tip
比如@Yangguang写的一个示例。
是不是很有意思,像一个用JavaScript写好的Tooltip,暴露了很多个参数options
(自定义属性),使用的时候,只需要调整自定义属性的值即可。这就是CSS Houdini的强势之处。在未来,她有可能将是会CSSer的主流开发模式之一。如果你这个感兴趣,可以阅读@Yangguan的原文,示例代码的仓库在这里。
这篇文章事实上主要围绕着CSS中border
来展开,虽然很多东西都不是新东西,也不是新技术,但每一种边框的效果是我们在Web运用程序和Web页面开发过程中会涉及到的,并且每种效果都有不同的思路来扩展。这里仅仅提供一些扩展思路,让大家在平时在项目中开发时能根据自己的需要选择最为合适的方式。另外,很多同学,特别对于很多不擅长写CSS的同学而言,写CSS和管理CSS是一件极其痛苦的事情,这里抛砖引玉地向大家介绍了在不久的将来我们可以借助CSS Houdini的特性来更好的写CSS和管理CSS。最后希望这篇文章中提到的一些小技巧或思路对于自己写CSS有所帮助。
如需转载,烦请注明出处:https://www.w3cplus.com/css/css-tips-0904-1.html