DRY Don’t Repact Youself
WET We Enjoy Typing or Write Everything Twice
border-radius 属性是一个最多可指定四个 border -*- radius 属性的复合属性。
border-radius: 1-4 length|% / 1-4 length|%;
注意: 每个半径的四个值的顺序是:左上角,右上角,右下角,左下角。如果省略左下角,右上角是相同的。如果省略右下角,左上角是相同的。如果省略右上角,左上角是相同的。
border-radius,它可以单独指定水平和垂直半径,只要用一个斜杠(/)分隔这两个值即可。它不仅可以接受长度值,还可以接受百分比值。这个百分比值会基于元素的尺寸进行解析,即宽度用于水平半径的解析,而高度用于垂直半径的解析。这意味着相同的百分比可能会计算出不同的水平和垂直半径。
如果我们传给它四个值,这四个值就会被分别从左上角开始以顺时针顺序应用到元素的各个拐角。如果
我们提供的值少于四个,则它们会以CSS 的常规方式重复,类似于borderwidth的值。如果只提供了三个值,则意味着第四个值与第二值相同;如果只有两个值,则意味着第三个值与第一个相同。
border-radius: 50% / 100% 100% 0 0;
border-radius: 50% / 0 0 100% 100%;
border-radius: 100% 0 0 100% / 50%;
border-radius: 0 100% 100% 0 / 50%;
其中一个角的水平和垂直半径值都需要是100%,而其他三个角都不能设为圆角。
border-radius: 100% 0 0 0;
我们可以对内容再应用一次反向的skew() 变形,从而抵消容器的变形效果,不幸的是,这意味着我们将不得不使用一层额外的HTML 元素来包裹内容。
<a href="#yolo" class="button">
<div>Click mediv>
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);
}
这个技巧不仅对skew() 变形来说很有用,还适用于其他任何变形样式,当我们想变形一个元素而不想变形它的内容时就可以用到它。
需要把图片用一个 div 包裹起来,然后对其应用相反的rotate() 变形样式。让图片的宽度与容器的对角线相等,而不是与边长相等,把max-width 的值设置为 2 r \sqrt{2}r 2r ,或者把这个值向上取整为 142%。
如果用scale() 变形样式来把这个图片放大,实际上会更加合理。我们希望图片的尺寸属性保留 100% 这个值,通过 scale() 变形样式来缩放图片时,是以它的中心点进行缩放的。
<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) scale(1.42);
}
使用clip-path 属性。裁切路径允许我们把元素裁剪为我们想要的任何形状。在这个例子中,我们将会使用polygon()(多边形)函数来指定一个菱形。实际上,它允许我们用一系列(以逗号分隔的)坐标点来指定任意的多边形。我们甚至可以使用百分比值,它们会解析为元素自身的尺寸。
clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
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%, 0 100%);
}
假设我们只需要一个角被切掉的效果,利用渐变可以接受一个角度(比如45deg)作为方向,而且色标的位置信息也可以是绝对的长度值,这一点丝毫不受容器尺寸的影响。
假设我们想要两个角被切掉的效果,要再加一层,可是,这样写是行不通的。默认情况下,这两层渐变都会填满整个元素,因此它们会相互覆盖。需要让它们都缩小一些,于是我们使用background-size 让每层渐变分别只占据整个元素一半的面积。尽管我们已经用了background-size,但这两层渐变仍然是相互覆盖的。原因在于,我们忘记把background-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;
它的可维护性并不理想。我们在改变背景色时需要修改五处;而在改变切角尺寸时需要修改四处。
创建弧形切角,唯一的区别在于,我们会用径向渐变来替代上述线性渐变。
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;
background: #58a;
clip-path: polygon(
20px 0, calc(100% - 20px) 0, 100% 20px,
100% calc(100% - 20px), calc(100% - 20px) 100%,
20px 100%, 0 calc(100% - 20px), 0 20px
);
这个方法最大的好处在于,我们可以使用任意类型的背景,甚至可以对替换元素(比如图片)进行裁切。
在现实的三维世界中旋转一个矩形。由于透视的关系,我们最终看到的二维图像往往就是一个梯形!
transform: perspective(.5em) rotateX(5deg);
对元素使用了3D 变形之后,其内部的变形效应是“不可逆转”的。因此,如果我们想发挥3D 变形的功能来生成梯形,唯一可行的途径就是把变形效果作用在伪元素上。
为了让它的尺寸更好掌握,我们可以为它指定transform-origin: bottom;,当它在3D 空间中旋转时,可以把它的底边固定住。
.tab {
position: relative;
display: inline-block;
padding: .5em 1em .35em;
color: white;
}
.tab::before {
content: ''; /* 用伪元素来生成一个矩形 */
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
z-index: -1;
background: #58a;
transform: scaleY(1.3) perspective(.5em) rotateX(5deg);
transform-origin: bottom;
}
我们只需要把transform-origin 改成bottom left 或bottom right,就可以立即得到左侧倾斜或右侧倾斜的标签页。
只需要一个元素作为容器,而其他部分是由伪元素、变形属性和CSS 渐变来实现的。
<div class="pie">div>
.pie {
width: 100px; height: 100px;
border-radius: 50%;
background: yellowgreen;
background-image: linear-gradient(to right, transparent 50%, #655 0);
overflow: hidden;
}
把圆形的左右两部分指定为上述两种颜色,然后用伪元素覆盖上去,通过旋转来决定露出多大的扇区。要么给.pie 设置overflow: hidden 的样式,要么给这个伪元素指定合适的border-radius 属性来把它变成一个半圆。把50%~ 100% 的比率看作另外一个问题,可以使用0%~ 50%的一个反向版本来实现这个范围内的比率:设置一个棕色的伪元素,让它在0 至.5turn 的范围内旋转。
@keyframes spin { to { transform: rotate(.5turn); } }
@keyframes bg { 50% { background: #655; } }
.pie::before {
content: '';
display: block;
margin-left: 50%;
height: 100%;
background-color: inherit;
transform-origin: left;
animation: spin 3s linear infinite, bg 6s step-end infinite;
}
用负的动画延时来直接跳至动画中的任意时间点,并且定格在那里。一个负的延时值是合法的。与0s 的延时类似,它意味着动画会立即开始播放,但会自动前进到延时值的绝对值处,就好像动画在过去已经播放了指定的时间一样。因此实际效果就是动画跳过指定时间而从中间开始播放了。
动画是作用在伪元素上的,但我们希望最终内联样式可以设置在.pie 元素上。不过,由于div 上并没有任何动画效果,我们可以用内联样式的方式为其设置animation-delay 属性,然后再在伪元素上应用animation-delay: inherit; 属性。
.pie::before {
content: "";
display: block;
margin-left: 50%;
height: 100%;
background-color: inherit;
transform-origin: left;
animation: spin 50s linear infinite, bg 100s step-end infinite;
animation-play-state: paused;
animation-delay: inherit;
}
(1) 以该元素相同的尺寸和位置,画一个rgba(0,0,0,.5) 的矩形。
(2) 把它向右移2px,向下移3px。
(3) 使用高斯模糊算法(或类似算法)将它进行4px 的模糊处理。这在本质上表示在阴影边缘发生阴影色和纯透明色之间的颜色过渡长度近似于模糊半径的两倍(比如在这里是8px)。
(4) 接下来,模糊后的矩形与原始元素的交集部分会被切除掉,因此它看起来像是在该元素的“后面”。
box-shadow 鲜为人知的第四个长度参数,称作扩张半径。这个参数会根据你指定的值去扩大或(当指定负值时)缩小投影的尺寸。
如果我们应用一个负的扩张半径,而它的值刚好等于模糊半径,那么投影的尺寸就会与投影所属元素的尺寸完全一致。除非用偏移量(前两个长度参数)来移动它,我们将完全看不见任何投影。因此,如果给投影应用一个正的垂直偏移量,我们就会在元素的底部看到一道投影,而元素的另外三侧是没有投影的。
box-shadow: 0 5px 4px -4px black;
(1) 我们不应该把投影缩得太小,而是只需把阴影藏进一侧,另一侧自然露出就好。因此,扩张半径不应设为模糊半径的相反值,而应该是这个相反值的一半。
(2) 需要指定两个偏移量,因为我们希望投影在水平和垂直方向上同时移动。它们的值需要大于或等于模糊半径的一半,因为我们希望把投影藏进另外两条边之内。
box-shadow: 3px 3px 6px -3px black;
扩张半径在四个方向上的作用是均等的(也就是说,我们无法指定投影在水平方向上放大,而在垂直方向上缩小),唯一的办法是用两块投影(每边各一块)来达到目的。
box-shadow: 5px 0 5px -5px black, -5px 0 5px -5px black;
filter 属性定义了元素(通常是img )的可视效果(例如:模糊与饱和度)。
filter: none | blur() | brightness() | contrast() | drop-shadow() | grayscale()
| hue-rotate() | invert() | opacity() | saturate() | sepia() | url();
值 | 描述 |
---|---|
grayscale(%) | 将图像转换为灰度图像。值定义转换的比例。值为100%则完全转为灰度图像,值为0%图像无变化。值在0%到100%之间,则是效果的线性乘子。若未设置,值默认是0。 |
drop-shadow(h-shadow v-shadow blur spread color) | 给图像设置一个阴影效果。阴影是合成在图像下面,可以有模糊度的,可以以特定颜色画出的遮罩图的偏移版本。 函数接受shadow (在CSS3背景中定义)类型的值,除了"inset"关键字是不允许的。该函数与已有的box-shadow 属性很相似。 |
drop-shadow() 滤镜可接受的参数基本上跟box-shadow 属性是一样的,但不包括扩张半径,不包括inset 关键字,也不支持逗号分割的多层投影语法。
filter: drop-shadow(2px 2px 10px rgba(0,0,0,.5));
任何非透明的部分都会被一视同仁地打上投影,包括文本(如果背景是透明的)。
color: deeppink;
border: 2px solid;
text-shadow: .1em .2em yellow;
filter: drop-shadow(.05em .05em .1em gray);
filter: none | blur() | brightness() | contrast() | drop-shadow() | grayscale()
| hue-rotate() | invert() | opacity() | saturate() | sepia() | url();
值 | 描述 |
---|---|
sepia(%) | 将图像转换为深褐色。值定义转换的比例。值为100%则完全是深褐色的,值为0%图像无变化。值在0%到100%之间,则是效果的线性乘子。若未设置,值默认是0。 |
saturate(%) | 转换图像饱和度。值定义转换的比例。值为0%则是完全不饱和,值为100%则图像无变化。其他值,则是效果的线性乘子。超过100%的值是允许的,则有更高的饱和度。 若值未设置,值默认是1。 |
hue-rotate(deg) | 给图像应用色相旋转。"angle"一值设定图像会被调整的色环角度值。值为0deg,则图像无变化。若值未设置,默认值是0deg。该值虽然没有最大值,超过360deg的值相当于又绕一圈。 |
由于没有一种现成的滤镜是专门为这个效果而设计的,我们需要花一些心思,把多个滤镜组合起来。
我们要使用的第一个滤镜是sepia(),它会给图片增加一种降饱和度的橙黄色染色效果,几乎所有像素的色相值会被收敛到35~40。如果我们想要的主色调的饱和度比这更高,可以用saturate() 滤镜来给每个像素提升饱和度。再添加一个hue-rotate() 滤镜,把每个像素的色相以指定的度数进行偏移。
img {
transition: .5s filter;
filter: sepia(1) saturate(4) hue-rotate(295deg);
}
img:hover, img:focus {
filter: none;
}
当两个元素叠加时,“混合模式”控制了上层元素的颜色与下层颜色进行混合的方式。用它来实现染色效果时,需要用到的混合模式是luminosity。这种luminosity 混合模式会保留上层元素的HSL 亮度信息,并从它的下层吸取色相和饱和度信息。
要对一个元素设置混合模式,有两个属性可以派上用场:mix-blendmode 可以为整个元素设置混合模式,background-blend-mode 可以为每层背景单独指定混合模式。
第一种选择:
需要把图片包裹在一个容器中,并把容器的背景色设置为我们想要的主色调。
<a href="#something">
<img src="tiger.jpg" alt="Rawrrr!" />
a>
a {
background: hsl(335, 100%, 50%);
}
img {
mix-blend-mode: luminosity;
}
有一件事情需要注意,滤镜是可动画的,而混合模式则不是。mix-blend-mode 是把整个元素向下进行混合,而不管它的下层是什么。因此,如果我们把这个属性设置为luminosity 混合模式,那图片就总是会跟某些东西进行混合。
第二种选择:
不用图片元素,而是用 div 元素——把这个元素的第一层背景设置为要染色的图片,并把第二层的背景设置为我们想要的主色调。
使用backgroundblend-mode 属性则可以让每层背景跟它的下层背景进行混合,但并不关心元素之外是什么情况。另外,当我们只有一个背景图像以及一个透明背景色时,不会出现任何混合效果!
<div class="tinted-image" style="background-image:url(tiger.jpg)">div>
.tinted-image {
width: 640px; height: 440px;
background-size: cover;
background-color: hsl(335, 100%, 50%);
background-blend-mode: luminosity;
transition: .5s background-color;
}
.tinted-image:hover {
background-color: transparent;
}
这两种方法都不够理想,它们的主要问题在于:
图片的尺寸需要在 CSS 代码中写死;
在语义上,这个元素并不是一张图片,因此并不会被读屏器之类的设备读出来。
filter: none | blur() | brightness() | contrast() | drop-shadow() | grayscale()
| hue-rotate() | invert() | opacity() | saturate() | sepia() | url();
值 | 描述 |
---|---|
blur(px) | 给图像设置高斯模糊。"radius"一值设定高斯函数的标准差,或者是屏幕上以多少像素融在一起, 所以值越大越模糊;如果没有设定值,则默认是0;这个参数可设置css长度值,但不接受百分比值。 |
把文本层所覆盖的那部分图片区域作模糊处理。模糊的背景看起来不那么花哨,因此在它之上的文本就相对比较易读了。借助blur() 滤镜,我们在CSS 中获得了对元素进行模糊处理的能力。
由于我们不能直接对元素本身进行模糊处理,就对一个伪元素进行处理,然后将其定位到元素的下层,它的背景将会无缝匹配body 的背景。
<main>
<blockquote>
"The only way to get rid of a temptation[...]"
<footer>-
<cite>Oscar Wilde, The Picture of Dorian Graycite>
footer>
blockquote>
main>
body, main::before {
background: url("tiger.jpg") 0 / cover fixed;
}
main {
position: relative;
background: hsla(0,0%,100%,.3);
overflow: hidden;
}
main::before {
content: '';
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
filter: blur(20px);
margin: -30px;
}
模糊效果在中心区域看起来非常完美,但在接近边缘处会逐渐消退。这是因为模糊效果会削减实色像素所能覆盖的范围,削减的幅度正是模糊半径的长度。让伪元素相对其宿主元素的尺寸再向外扩大至少20px(即它的模糊半径),可以通过-20px 的外边距来达到目的。这个方法可以修复边缘模糊消退的问题,但现在的情况是有一圈模糊效果超出了容器,这让它看起来不像毛玻璃,而更像是玻璃脏了。只要对main 元素应用overflow: hidden;,就可以把多余的模糊区域裁切掉了。
创建一个右上角具有斜面切角的元素,然后增加一个暗色的三角形来实现翻折效果。实现方法是增加另一层渐变来生成这个三角形并将其定位在右上角,这样就可以通过background-size 来控制折角的大小。
第二层渐变中的折角尺寸是写在色标中的,因此它是沿着渐变轴进行度量的,是对角线尺寸。另一方面,在background-size 中的长度是背景贴片的宽度和高度,是在水平和垂直方向上进行度量的。要保留水平和垂直方向上的长度,就要用切角渐变的角标位置值除以 2 \sqrt{2} 2 。
background: #58a; /* 回退样式 */
background:
linear-gradient(to left bottom, transparent 50%, rgba(0,0,0,.4) 0)
no-repeat 100% 0 / 2em 2em,
linear-gradient(-135deg, transparent 1.5em, #58a 0);
只要知道了直角三角形的角度和某一条边的长度,就可以通过正弦函数、余弦函数以及勾股定理计算出另外两条边的长度。
现实世界中的折角,我们会发现这个折页三角形是需要微微旋转的,它的尺寸跟我们从元素角上“切”下来的那个三角形应该是一致的。由于我们无法旋转背景,这里终于轮到伪元素登场了。
把折页三角形的width 和height 对调,以此改变它的方向,就可以得到跟折页缺口对称的三角形。然后,我们再以逆时针来旋转这个折页三角形,可以让它的斜边与折线平行。把transform-origin 设置为bottom right,让三角形的右下角成为旋转的中心,就可以让它的右下角保持固定。现在只需在垂直方向上向上移动这个折页三角形就可以了,垂直偏移量是 x − y 。
.note {
position: relative;
background: #58a; /* 回退样式 */
background: linear-gradient(-150deg, transparent 1.5em, #58a 0); /* 斜面切角 */
border-radius: .5em;
}
.note::before {
content: '';
position: absolute;
top: 0; right: 0;
background:
linear-gradient(to left bottom, transparent 50%, rgba(0,0,0,.2) 0, rgba(0,0,0,.4))
100% 0 no-repeat; /* 生成折纸三角形 */
width: 1.73em;
height: 3em;
transform: translateY(-1.3em) rotate(-30deg); /* 旋转和向上偏移 */
transform-origin: bottom right;
border-bottom-left-radius: inherit;
box-shadow: -.2em .2em .3em -.1em rgba(0,0,0,.15);
}