昨天公司做前端的好像对CSS都不是很熟(其实就是一群Javaer兼职干前端),估计是用多了React、Bootstrap这种现成的框架(就只知道写组件了),没写过啥基础的CSS。然后有个优惠券的模块分到了我的头上,正好总结总结。
优惠券最主要就是这个锯齿的问题。其实用图片做也完全可以,反正最后那些小图片都会被webpack编码成Base64的DataURL
关于DataURL的内容可以参考RFC2397
不过用图片方式就没有啥挑战性了,那我也没必要写这篇文章记录这个过程。
我们的目的是用纯CSS实现锯齿
一、使用before和after伪元素的border实现
先看一张效果图
先定义一下最基本的html:
这里我们主要关注怎么实现锯齿,内容什么的我就没写,背景颜色也直接放在html里面了。
1. 用dotted圆点边框覆盖p
我们的主要思路就是用两排圆点覆盖在p上:
.sawtooth { /* 相对定位,方便让before和after伪元素绝对定位偏移 */ position: relative; } .sawtooth:before, .sawtooth:after { content: ' '; width: 0; height: 100%; /* 绝对定位进行偏移 */ position: absolute; top: 0; } .sawtooth:before { /* 圆点型的border */ border-right: 10px dotted white; /* 偏移一个半径,让圆点的一半覆盖p */ left: -5px; } .sawtooth:after { /* 圆点型的border */ border-left: 10px dotted white; /* 偏移一个半径,让圆点的一半覆盖p */ right: -5px; }
得到下面这样的效果:
2. 微调边框位置
但是由于圆点是顶着p开始的,总感觉不怎么好看,我们把before和after两个伪元素往下移动一点:
.sawtooth:before, .sawtooth:after { ... /* 下移一个圆点直径的距离,让最后一个圆点超出p */ top: 10px; }
这样虽然已经出效果了,但是在实际应用中,由于背景色的不同导致,我们border圆点露馅了。
3. 隐藏多余的部分
为了解决这个问题,我们把需要把超出p的部分剪切掉:
.sawtooth { /* 相对定位,方便让before和after伪元素绝对定位偏移 */ position: relative; /* 把超出p的部分隐藏起来 */ overflow: hidden; }
因为border的颜色(白色)和背景色(青绿色)不同,导致优惠券看上去很突兀。
对于这个问题,最开始相到的解决办法是让把border设置为transparent(透明),可惜透明的结果是显示后面p的红色。所以暂时只能用最笨的方法:把border的颜色设置成背景色。
整理以下CSS
.sawtooth { /* 相对定位,方便让before和after伪元素绝对定位偏移 */ position: relative; /* 把超出p的部分隐藏起来 */ overflow: hidden; } .sawtooth:before, .sawtooth:after { content: ' '; width: 0; height: 100%; /* 绝对定位进行偏移 */ position: absolute; top: 10px; } .sawtooth:before { /* 圆点型的border */ border-right: 10px dotted white; /* 偏移一个半径,让圆点的一半覆盖p */ left: -5px; } .sawtooth:after { /* 圆点型的border */ border-left: 10px dotted white; /* 偏移一个半径,让圆点的一半覆盖p */ right: -5px; }
二、使用透明背景实现
上面的方法的缺点是,我们需要让border的颜色和背景色一致,才能让我们的优惠券不“露馅”。
所以这次我们就直接用透明的背景。透明的背景当然不是指全透明,全透明那就是没背景了。看一下下面这张图你就知道我们要怎么干了。
哇塞,这么多洞!密集恐惧症患者,看了估计有点闹心。
我们的主要思路是:在这张有这么多洞的网上面盖一张p,把多余的洞遮住,只保留两边有锯齿的洞。
不过使用这种方式我们先要把html中的背景色干掉,只保留宽高:
1. 用css画出这张网
为了画出这么多的洞,我们要用到radial-gradient这个渐变函数,不过我们并不需要用到它的渐变功能,只要用它来画透明的圆点就行。
radial-gradient要求IE10及以上的版本的浏览器。
关于radial-gradient的更多详细内容,请参考MDN的文档
.sawtooth{ /* 画出一个半径为5px的透明的圆,透明圆以外都是#e24141颜色 */ background-image: radial-gradient(transparent 0, transparent 5px, #e24141 5px); /* 截取上面生成的渐变图的一部分,相当于截取15px的正方形中有一个直径10px的透明圆点 */ background-size: 15px 15px; /* 根据优惠券p大小进行微调 */ background-position: 8px 3px; }
上面简单的三句话就生成了这张网:
也许你看到样式上面的注释会很纳闷,这样就生成了一张网?你把sawtooth类改成下面的样式看一下,估计你就会明白了:
.sawtooth{ background-image: radial-gradient(transparent 0, transparent 5px, #e24141 5px); background-size: 100px 100px; background-repeat: no-repeat; }
2. 找一个元素把多余的洞覆盖掉
为了不影响html的整洁性,我们就使用before或after这两个伪元素中的一个来覆盖多余的洞吧。
.sawtooth { background-image: radial-gradient(transparent 0, transparent 5px, #e24141 5px); background-size: 15px 15px; background-position: 8px 3px; /* 相对定位,让before伪元素方便定位 */ position: relative; } .sawtooth:before { content: ' '; display: block; /* 用相同的颜色覆盖 */ background-color: #e24141; /* 绝对定位,遮住中间所有的洞,只保留边角的锯齿 */ position: absolute; top: 0; bottom: 0; /* 为锯齿保留的距离 */ left: 10px; right: 10px; }
完成的效果图如下:
3. 让遮罩层下移,让它成为背景
上面的样式还有一点点问题。
当我们网p中加内容的时候:
内容内容内容内容内容内容内容内容内容内容 内容内容内容内容内容内容内容内容内容内容 内容内容内容内容内容内容内容内容内容内容 内容内容内容内容内容内容内容内容内容内容 内容内容内容内容内容内容内容内容内容内容 内容内容内容内容内容内容内容内容内容内容 内容内容内容内容内容内容内容内容内容内容 内容内容内容内容内容内容内容内容内容内容 内容内容内容内容内容内容内容内容内容内容
发现它不仅把洞给遮住了,还把我们的内容给遮住了:
出现上面这种现象的原因就是:绝对定位脱离文档流后,会浮在文档的上面。
“我们的内容在地表上,遮罩层却在一楼”
为了解决这个问题,我们还需要加一个样式,让这个遮罩层不再是“遮罩层”,而是在文档下面当做背景。
.sawtooth:before { ... z-index: -1; }
让“遮罩层”到负一楼去。
OK,我们想要的效果有了。
而且不管背景换成什么颜色,我们的“优惠券”都不会“露馅”。
附加:改变形状,生成别样的锯齿
我们可以通过适当修改background的几个参数,将圆形压扁,生成其他形状的锯齿:
.sawtooth { background-image: radial-gradient(transparent 0, transparent 4px, #e24141 4px); background-size: 12px 8px; background-position: -5px 10px; position: relative; } .sawtooth:before { content: ' '; display: block; background-color: #e24141; position: absolute; top: 0; bottom: 0; left: 6px; right: 6px; z-index: -1; }