文章目录
背景与边框
半透明边框
目标:给一个容器设置一层白色背景和一道半透明白色边框
写法:
border:10px solid hsla(0,0%,100%,.5);
background:white;
效果:
原因:默认情况下,背景会延伸到边框所在的区域下层,所以在半透明白色边框处透出了这个容器自己的纯白实色背景,实际效果跟纯白实色的边框完全一样。
解决:background-clip
属性的初始值是border-box
,意味着背景会被元素的border box(边框的外沿框)裁切掉。如果不希望背景侵入边框所在的范围,需要把该值设为padding-box
,这样浏览器就会用内边距的外沿来把背景裁切掉:
border:10px solid hsla(0,0%,100%,.5);
background:white;
background-clip:padding-box;
多重边框
box-shadow
box-shadow
:接受第四个参数(称作“扩张半径”),通过指定正值或负值,可以让投影面积加大或者减小。一个正值的扩张半径加上两个为零的偏移量以及为零的模糊值,得到的“投影”其实就像一道实线边框:
background:yellowgreen;
box-shadow:0 0 0 10px #655;
box-shadow支持逗号分隔语法,我们可以创建任意数量的投影。因此,我们可以非常轻松地在上面的示例中再加上一道deeppink颜色的“边框”:
background:yellowgreen;
box-shadow:0 0 0 10px #655, 0 0 0 15px deeppink;
为“边框”的底下再加一层常规的投影:
background:yellowgreen;
box-shadow:0 0 0 10px #655,
0 0 0 15px deeppink,
0 2px 5px 15px rgba(0,0,0,.6);
⚠️注意:
box-shadow
是层层叠加的,第一层投影位于最顶层,所以,在前面的代码中,我们想在外圈再加一道5px的外框,那就需要指定扩张半径的值为15px(10px+5px)- 投影的行为跟边框(border)不一样,它不影响布局,也不受
box-sizing
属性的影响。不过,你还是可以通过内边距或外边距(这取决于投影是内嵌和还是外扩的)来额外模拟出边框所需要占据的空间。- 上述方法所创建出的假“边框”出现在元素的外圈。它们并不会响应鼠标事件,比如悬停或点击。如果这一点非常重要,你可以给
box-shadow
属性加上inset
关键字,来使投影绘制在元素的内圈。请注意,此时你需要增加额外的内边距来腾出足够的空隙。- 只能模拟实线边框
outline
如果只需要两层边框,那就可以先设置一层常规边框,再加上outline
(描边)属性来产生外层的边框,如果想得到上图【双重边框的效果】,outline
代码如下:
background:yellowgreen;
border:10px solid #655;
outline:5px solid deeppink;
outline
可以模拟虚线边框,outline-offset
属性来控制它跟元素边缘之间的间距,接受负值。对一层dashed
(虚线)描边使用负的outline-offset
后,可以得到简单的缝边效果:
background:yellowgreen;
outline:5px dashed deeppink;
outline-offset: -15px;
如下图所示:
背景定位
background-position
目标:针对容器某个角对背景图片做偏移定位,比如让背景图片跟右边缘保持20px的偏移量,同时跟底边保持10px的偏移量,如下图所示:(背景图片即虚线框框出来的范围)
解决办法:background-position
允许我们指定背景图片距离任意角的偏移量,只要我们在偏移量前面指定关键字:
background:url(code-pirate.svg) no-repeat #58a;
background-position:right 20px bottom 10px;
问题在不支持background-position
扩展语法的浏览器中,背景图片会紧贴在左上角(背景图片的默认位置),而且它会干扰到文字的可读性,因此需要在代码中提供一个回退方案,把老套的bottom right定位值写进background的简写属性中即可:
background:url(code-pirate.svg) no-repeat bottom right #58a;# 但是这种方式存在的问题是:背景图片无法指定距离容器底部、右部的距离
background-position:right 20px bottom 10px;
background-origin
目标:针对容器某个角对背景图片做偏移定位,且偏移量与容器的内边距一致,比如容器内边距10px,背景图片距离容器底部10px,距离容器右边10px,依前述,代码应为:
padding:10px;
background:url(code-pirate.svg) no-repeat #58a;
background-position:right 10px bottom 10px;
问题:每次改动内边距的值都需要在三个地方更新这个值!能否让背景图片自动地跟着我们设定的内边距走,不用另外声明偏移量的值?
解决方案:每个元素身上都存在三个矩形框:border box(边框的外沿框)、padding box(内边距的外沿框)和content box(内容区的外沿框)。
默认情况下,background-position是以padding box为准的,这样边框才不会遮住背景图片。因此,top left默认指的是padding box的左上角。我们可以使用background-origin
来改变这种现象,background-origin
默认值为padding-box
,如果把它的值改成content-box
,那么在background-position
属性中使用的边角关键字将会以内容区的边缘作为基准(也就是说,此时背景图片距离边角的偏移量就跟内边距保持一致了):
padding:10px;
background:url("code-pirate.svg") no-repeat #58a
bottom right; /* 或 100% 100% */
background-origin:content-box;
如果想让偏移量与内边距稍稍有些不同(比如稍微收敛或超出),那么可以在使用
background-origin: content-box
的同时,再通过background-position
的扩展语法来设置这些额外的偏移量。
calc()
目标:把背景图片定位到距离底边10px且距离右边20px的位置。
解决方案:如果我们仍然以左上角偏移的思路来考虑,其实就是希望有一个100% -20px的水平偏移量,以及100% -10px的垂直偏移量(这里其实不太懂,我理解为:background-position: calc(100%) calc(100%);就是背景图片位于容器右下方,然后各自向右边便宜20px,向上偏移10px,所以就是:background-position:calc(100% - 20px)calc(100% - 10px);)
background:url("code-pirate.svg") no-repeat;
background-position:calc(100% - 20px)calc(100% - 10px);
⚠️注意:在使用
calc()
时,不要忘记在calc()
函数内部的-和+运算符的两侧各加一个空白符
条纹背景
水平条纹
目标:使用css实现条纹
解决方案:
- 生成等宽条纹
background:linear-gradient(#fb3 50%,#58a 50%);
background-size:100% 30px;
/* background-size:宽 高; */
- 创建不等宽的条纹,只需调整色标的位置值即可:
background:linear-gradient(#fb3 30%,#58a 0);
background-size:100% 30px;
height: 200px;
- 生成三种颜色的水平条纹:
background:linear-gradient(#fb3 33.3%,#58a 0,#58a 66.6%,yellowgreen 0);
background-size:100% 45px;
height: 200px;
linear-gradient(颜色 颜色终止位置),那么linear-gradient(#fb3 20%,#58a 80%)表示:容器顶部的20%区域被填充为#fb3实色,底部20%区域被填充为#58a实色,中间为二者过度区域
形状
圆形
容器宽高固定且相等,为其设置border-radius
,border-radius
的值大于等于容器宽高可以得到一个圆形:
background:#fb3;
width:200px;
height:200px;
border-radius: 100px; /* >= 正方形边长的一半 */
圆柱
容器宽高固定且不相等,为其设置border-radius
,border-radius
的值大于等于容器宽高可以得到一个圆柱:
background:#fb3;
width:300px;
height:200px;
border-radius: 100px;
自适应的椭圆
如果我们有一个尺寸为200px×150px的元素,就可以把它圆角的两个半径值分别指定为元素宽高的一半,从而得到一个精确的椭圆:
background:#fb3;
width:200px;
height:150px;
border-radius: 100px /75px; /* 容器宽高的一半 */
自适应的椭圆:
background:#fb3;
width:200px;
height:150px;
border-radius: 50%; /* 圆角的宽高始终为容器宽高的一半 */
半椭圆
目标:
基础知识:border-radius
其实是一个简写属性。我们可以为元素的每个角指定不同的值
- 如果我们传给它四个值,这四个值就会被分别从左上角开始以顺时针顺序应用到元素的各个拐角。
- 如果只提供了三个值,则意味着第四个值与第二值相同;
- 如果只有两个值,则意味着第三个值与第一个相同
我们甚至可以为所有四个角提供完全不同的水平和垂直半径,方法是在斜杠前指定1~4个值,在斜杠后指定另外1~4个值。请注意这两组值是单独展开为四个值的。例如,当border-radius的值为10px / 5px 20px时,其效果相当于10px 10px 10px 10px / 5px20px 5px 20px。
还可以使用border-radius所对应的各个展开式属性:border-top-left-radius、border-top-right-radius、border-bottom-right-radius、border-bottom-left-radius来指定各个角的大小
思路:
- 这个形状是垂直对称的,这意味着左上角和右上角的半径值应该是相同的;与此类似,左下角和右下角的半径值也应该是相同的。
- 顶部边缘并没有平直的部分(也就是说,整个顶边都是曲线),这意味着左上角和右上角的半径之和应该等于整个形状的宽度。
- 基于前两条观察,我们可以推断出,左半径和右半径在水平方向上的值应该均为50%。
- 再看看垂直方向,似乎顶部的两个圆角占据了整个元素的高度,而且底部完全没有任何圆角。因此,在垂直方向上border-radius的合理值似乎就是100%100% 0 0。
- 因为底部两个角的垂直圆角是零,那么它们的水平圆角是多少就完全不重要了,因为此时水平圆角总是会被计算为零。
实现方案:border-radius:50% /100% 100% 0 0;
四分之一椭圆
平行四边形
目标:实现一个平行四边形,如下图中的很多平行四边形按钮
方法:使用skew()
来改变元素在页面中的形状:transform:skewX(-45deg);
skew()
是transform
的一个属性,用法如下:
skew(xdeg)
:向横向倾斜指定度数,x取值为正表示X轴不动,y轴逆时针倾斜一定角度;x取值为负表示X轴不动,y轴顺时针倾斜一定角度,如skew(30deg)skew(xdeg,ydeg)
:ydeg : 纵向倾斜度数,y取值为正表示y轴不动,x轴顺时针倾斜一定角度;y取值为负表示y轴不动,x轴逆时针倾斜一定角度- skewX(xdeg)
- skewY(ydeg)
问题:
- 在行内的元素中不生效:把display属性设置为其他值,比如inline-block或block,否则变形是不会生效的。这一点对它内层的元素也是适用的。
- 里面的内容也发生了斜向变形:
让容器的形状倾斜,而保持其内容不变的解决方案:
- 对内容再应用一次反向的
skew()
变形,从而抵消容器的变形效果:
<a href="#yolo"class="button">
<div>Click me</div>
</a>
.button{transform:skewX(-45deg); }
.button>div{transform:skewX(45deg); }
- 把所有样式(背景、边框等)应用到伪元素上,然后再对伪元素进行变形。因为我们的内容并不是包含在伪元素里的,所以内容并不会受到变形的影响。
.button{
position:relative;
/* 其他的文字颜色、内边距等样式…… */
}
.button::before{
content:''; /* 用伪元素来生成一个矩形 */
position:absolute;
top:0;right:0;bottom:0;left:0;
z-index:-1;/* 让展示形状的伪元素置于底层 */
background:#58a;
transform:skew(45deg);
}
当我们想变形一个元素而不想变形它的内容时都可以用到这个技巧
菱形图片
目标:实现下图的菱形图片
方法:如前述第一种解决方案,用div将图片包裹,再旋转div:
<div class="picture">
<img src="adam-catlace.jpg"alt="..."/>
</div>
.picture{
width:400px;
transform:rotate(45deg);
overflow:hidden;
}
.picture>img{
max-width:100%;
transform:rotate(-45deg);
}
问题:得到的图片如下,.picture div用一个虚线框标示
解决方案一:出现上述情况的原因在于:max-width:100%;
表示img的最大宽度100%即容器(.picture)的边长,而其实我们希望img的宽度为容器(.picture)边长的根号2倍,约为1.414(由勾股定理得出)。如果用scale()
变形样式来把这个图片放大,实际上会更加合理:
- 我们希望图片的尺寸属性保留100%这个值,这样当浏览器不支持变形样式时仍然可以得到一个合理的布局。
- 通过
scale()
变形样式来缩放图片时,是以它的中心点进行缩放的(除非我们额外指定了transform-origin
样式)。通过width属性来放大图片时,只会以它的左上角为原点进行缩放,从而迫使我们动用额外的负外边距来把图片的位置调整回来。
.picture{
width:400px;
transform:rotate(45deg);
overflow:hidden;
}
.picture>img{
max-width:100%;
transform:rotate(-45deg)scale(1.42);
}
解决方案二:上述解决方案无法处理一张非正方形的图片,我们其实还可以使用clip-path进行裁剪,clip-path会根据我们提供一系列的X和Y值来创建路径,根据这些路径裁剪图片,那么我们可以使用polygon()
(多边形)函数来指定一个菱形:clip-path:polygon(50% 0,100% 50%,50% 100%,0 50%);
polygon()
的参数是一组坐标对(<shape-arg> <shape-arg>
),每一个坐标对代表多边形的一个顶点坐标。浏览器会将最后一个顶点和第一个顶点连接得到一个封闭的多边形。坐标对使用逗号来进行分隔,可以使用绝对单位或百分比单位值
扩展:clip-path可以参与动画,只要我们的动画是在同一种形状函数(比如这里是polygon()
)之间进行的,而且点的数量是相同的。因此,如果我们希望图片在鼠标悬停时平滑地扩展为完整的面积,只需要这样做:
img{
clip-path:polygon(50% 0,100% 50%,
50% 100%,0 50%);
transition:1s clip-path;
}
img:hover{
clip-path:polygon(0 0,100% 0,
100% 100%,0100%);
}
切角效果
单边切角
目标:实现单边(此处为右下角)切角效果
方法:渐变可以接受一个角度(比如45deg)作为方向,而且色标的位置信息也可以是绝对的长度值,因此可以利用线性渐变实现:把一个透明色标放在切角处,然后在相同位置设置另一个色标,并且把它的颜色设置为我们想要的背景色。CSS代码如下所示(假设切角的深度是15px):
background:#58a;// 回退作用
background:
linear-gradient(-45deg,transparent 15px,#58a 0);
多边切角
目标:实现多变(此处以底部两个角为例)切角效果
方法:叠加两层渐变:
background:#58a;
background:
linear-gradient(-45deg,transparent 15px,#58a 0),
linear-gradient(45deg,transparent 15px,#655 0);
问题:两层渐变会叠加,形成下图效果:
linear-gradient(-45deg,transparent 15px,#58a 0)
会得到单边切图linear-gradient(45deg,transparent 15px,#655 0)
会得到左下角为透明色,其余为#655的单边切角图
二者叠加得到下图(疑问:为什么得到的不是#655为背景色,左下角为#58a的图片?)
解决方案:使用background-size让每层渐变分别只占据整个元素一半的面积,且取消背景平铺
background:#58a;
background:
linear-gradient(-45deg,transparent 15px,#58a 0)
right,
linear-gradient(45deg,transparent 15px,#655 0)
left;
background-size:50% 100%;
background-repeat:no-repeat;
扩展:四个角都切角:
background:#58a;
background:
linear-gradient(135deg, transparent 15px,#58a 0)
top left,
linear-gradient(-135deg,transparent 15px,#58a 0)
top right,
linear-gradient(-45deg,transparent 15px,#58a 0)
bottom right,
linear-gradient(45deg,transparent 15px,#58a 0)
bottom left;
background-size:50% 50%;
background-repeat:no-repeat;
问题:可维护性差,我们在改变背景色时需要修改五处;而在改变切角尺寸时需要修改四处
解决:使用SCSS的mixin优化:
@mixinbeveled-corners($bg,
$tl:0,$tr:$tl,$br:$tl,$bl:$tr) {
background:$bg;
background:
linear-gradient(135deg,transparent $tl,$bg0)
top left,
linear-gradient(225deg,transparent $tr,$bg0)
top right,
linear-gradient(-45deg,transparent $br,$bg0)
bottom right,
linear-gradient(45deg,transparent $bl,$bg0)
bottom left;
background-size:50% 50%;
background-repeat:no-repeat;
}
使用:@includebeveled-corners(#58a,15px,5px);
,元素的左上角和右下角会得到15px的切角效果,而右上角和左下角会得到5px的切角效果
弧形切角(内凹圆角)
目标:实现弧形切角(内凹圆角):
方法:用径向渐变来替代上述线性渐变
background:#58a;
background:
radial-gradient(circle at top left,
transparent 15px,#58a 0)top left,
radial-gradient(circle at top right,
transparent 15px,#58a 0)top right,
radial-gradient(circle at bottom right,
transparent 15px, #58a 0)bottom right,
radial-gradient(circle at bottom left,
transparent 15px,#58a 0)bottom left;
background-size:50% 50%;
background-repeat:no-repeat;