前言
《CSS 揭秘》这本书是由作者 Lea Verou 编写的,书中包含了很多 css 技巧,比如我们常用的渐变色,作者能利用渐变色实现很多有意思的 css 效果,确实很佩服作者的 css 功底。下面我把我认为平常能用上的效果放在这里。
效果
1、半透明边框
若直接为边框设置半透明背景,border box 呈现出的是 content box 的背景。需要将 background-clip (默认值是 border-box )设置为 padding-box ,这样内容区域的背景从 padding box 处裁切,border box 就会呈现自己原本的半透明背景。
.box {
width: 15em;
height: 8em;
border: 1em solid hsla(0, 0%, 100%, .5);
background:white;
background-clip: padding-box;
}
2、多重边框
利用 box-shadow 可以创建任意数量的投影来实现多重边框。每一层外框的设置值是外框内部所有 border-shadow 的第四个参数与将要设置外框值的和。
.box {
width: 20em;
height: 10em;
background: hsl(200deg, 56%, 55%, 0.9);
border: 1em solid hsl(200deg, 56%, 55%, 0.7);
box-shadow: 0 0 0 1em hsl(200deg, 56%, 55%, 0.5),
0 0 0 2em hsl(200deg, 56%, 55%, 0.2),
0 0.5em 2em 2.5em #ccc;
}
3、背景定位
.first {
background: url('./img//logo.png') no-repeat #233A48;
background-position: right 20px bottom 10px;
}
.second {
box-sizing: border-box;
padding: 10px 20px 10px 10px;
background: url('./img//logo.png') no-repeat bottom right #436E67;
background-origin: content-box;
}
.third {
background: url('./img//logo.png') no-repeat #6C9585;
background-position: calc(100% - 20px) calc(100% - 10px);
}
4、边框圆内角
如果边框的颜色只是纯色,可以使用一个元素来实现;如果边框的颜色不是纯色,可以使用两个元素来实现。
.box {
width: 20em;
height: 10em;
background: hsl(200deg, 56%, 55%, 0.5);
border-radius: 0.8em;
box-shadow: 0 0 0 0.6em hsl(200deg, 56%, 55%);
/* outline的宽度值为: border-radius(√2 - 1),再向上取整*/
outline: 1em solid hsl(200deg, 56%, 55%);
}
5、条纹背景
以横向条纹进行介绍,竖向条纹几乎与横向条纹一致,只需在 linear-gredient 的第一个位置设置 to right/left参数,并且将 background-size 的值左右互换即可。
等宽条纹渐变
.box {
width: 15em;
height: 10em;
/* 使用#fb3颜色在50%的位置开始渐变,使用#58a颜色在50%的位置停止渐变 */
background: linear-gradient(#fb3 50%, #58a 50%);
background-size: 100% 2em;
}
不等宽条纹渐变
.box {
width: 15em;
height: 10em;
/* 如果第二个色标的位置值设置为0,那它的位置就总是会被浏览器调整为前一个色标的位置值 */
background: linear-gradient(#fb3 30%, #58a 0);
background-size: 100% 2em;
}
斜向条纹
.box {
width: 15em;
height: 10em;
background: repeating-linear-gradient(45deg,#79b 0, #79b 15px, #58a 0, #58a 30px);
}
6、连续的图像边框
- 老式信封边框
.box {
width: 15em;
height: 6em;
padding: 1em;
border: 1em solid transparent;
background: linear-gradient(white, white) padding-box,
repeating-linear-gradient(-45deg,
red 0, red 12.5%,
transparent 0, transparent 25%,
#58a 0, #58a 37.5%,
transparent 0, transparent 50%)
0 / 5em 5em;
}
- 蚂蚁行军边框
@keyframes ants {
to {
background-position: 100%;
}
}
.box {
width: 15em;
height: 6em;
padding: 1em;
border: 1px solid transparent;
background: linear-gradient(white, white) padding-box,
repeating-linear-gradient(-45deg,
black 0, black 25%,
white 0, white 50%)
0 / 0.6em 0.6em;
animation: ants 12s linear infinite;
}
7、平行四边形
如果直接对元素进行变形,元素内容也会随之改变,所以我们需要两个元素,一个存放内容,一个应用变形样式。这里利用伪元素实现,将所有的样式应用到伪元素上,再对伪元素进行变形。
.box {
position: relative;
width: 10em;
height: 4em;
}
.box::before {
position: absolute;
content: '';
top: 0; bottom: 0; left: 0; right: 0;
z-index: -1;
background: hsl(200deg, 56%, 55%);
transform: skew(-45deg);
}
8、菱形图片
@keyframes diamond {
to {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
}
.pic {
width: 10%;
clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
transition: 1s clip-path;
}
.pic:hover {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
9、切角效果
渐变方案
.box { width: 10em; height: 8em; /* 当浏览器不支持渐变时的回退方案 */ background: hsl(200deg, 56%, 55%); background: linear-gradient(135deg, transparent 10px,hsl(200deg, 56%, 55%) 0) top left, linear-gradient(-135deg, transparent 10px, hsl(200deg, 56%, 55%, 0.5) 0) top right, linear-gradient(-45deg, transparent 10px,hsl(200deg, 56%, 55%) 0) bottom right, linear-gradient(45deg, transparent 10px, hsl(200deg, 56%, 55%, 0.5) 0) bottom left; /* 以上两层渐变是一层一层覆盖的,所以设置background-size两层渐变左右各占一半*/ background-size: 50% 50%; background-repeat: no-repeat; }
SVG + border-image方案
.box { width: 10em; height: 8em; /* 当浏览器不支持渐变时的回退方案 */ background: #4ca2cd; background: radial-gradient(circle at top left, transparent 10px, #4ca2cd 0) top left, radial-gradient(circle at top right, transparent 10px, #4ca2cd 0) top right, radial-gradient(circle at bottom right, transparent 10px, #4ca2cd 0) bottom right, radial-gradient(circle at bottom left, transparent 10px, #4ca2cd 0) bottom left; background-size: 50% 50%; background-repeat: no-repeat; }
10、饼图
@keyframes fillup { to { stroke-dasharray: 158 158; } } svg { width: 100px; height: 100px; transform: rotate(-90deg); background: yellowgreen; border-radius: 50%; margin: 10px; } circle { /* 填充色 */ fill: yellowgreen; /* 描边色 */ stroke: #655; /* 描边宽度 */ stroke-width: 32; stroke-dasharray: 38 100; animation: fillup 5s linear infinite; }
<svg viewBox="0 0 32 32"> <circle r="16" cx="16" cy="16"></circle> </svg>
可封装一个功能函数来创建指定扇形大小的饼图,之后可通过
<div class="pie">20%</div>
来创建饼图createPie() { let pieNodeList = document.getElementsByClassName('pie'); pieNodeList = Array.prototype.slice.call(pieNodeList); pieNodeList.forEach(pie => { const p = parseFloat(pie.textContent); const NS = "http://www.w3.org/2000/svg"; const svg = document.createElementNS(NS, "svg"); const circle = document.createElementNS(NS, "circle"); const title = document.createElementNS(NS, "title"); circle.setAttribute("r", 16); circle.setAttribute("cx", 16); circle.setAttribute("cy", 16); circle.setAttribute("stroke-dasharray", p + " 100"); svg.setAttribute("viewBox", "0 0 32 32"); title.textContent = pie.textContent; pie.textContent = pie.textContent; pie.textContent = ''; svg.appendChild(title); svg.appendChild(circle); pie.appendChild(svg); }) }
11、投影
可以利用
box-shadow(向右偏移 向下偏移 模糊半径 扩张半径)
来实现。扩张半径会根据你指定的值去扩大或缩小(当指定负值时)投影的尺寸。单侧投影:将扩张半径设置为模糊半径的相反值即可
.unilateral { /* box-shadow(左右偏移 上下偏移 模糊半径 扩张半径) */ box-shadow: 0 5px 4px -4px rgba(0, 0, 0, 0.5); }
邻边投影:将扩张半径设置为模糊半径相反值的一半即可
.adjoin { /* box-shadow(左右偏移 上下偏移 模糊半径 扩张半径) */ box-shadow: 4px 4px 6px -3px rgba(0, 0, 0, 0.5); }
双侧投影:将单侧投影运用两次来设置两块投影
.bilateral { box-shadow: 5px 0 4px -4px rgba(0, 0, 0, 0.5), -5px 0 4px -4px rgba(0, 0, 0, 0.5); }
12、不规则投影
两个例子的公共代码
.sppech_bubble, .cutout_corners { position: relative; width: 8em; height: 6em; background: #4ca2cd; color: #f8f8f8; text-align: center; line-height: 6em; }
.sppech_bubble { border-radius: 0.5em; filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.5)); } .sppech_bubble::before { position: absolute; content: ''; top: 50%; right: -2em; transform: translateY(-50%); border-width: 1em; border-style: solid; border-color: transparent transparent transparent #4ca2cd; }
.cutout_corners { background: linear-gradient(135deg, transparent 10px, #4ca2cd 0) top left, linear-gradient(-135deg, transparent 10px, #4ca2cd 0) top right, linear-gradient(45deg, transparent 10px, #4ca2cd 0) bottom left, linear-gradient(-45deg, transparent 10px, #4ca2cd 0) bottom right; background-size: 50% 50%; background-repeat: no-repeat; filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.5)); }
13、毛玻璃效果
.box, .content::before { background: url('./img//strawberry.jpg') 0 / cover fixed; } .content { position: relative; background: hsla(0, 0%, 100%, 0.3); border-radius: 8px; /* 解决模糊效果超出容器的问题 */ overflow: hidden; } .content::before { content: ''; position: absolute; top: 0; right: 0; left: 0; bottom: 0; filter: blur(20px); z-index: -1; /* filter的模糊效果会存在边缘模糊消退的问题,所以需要让伪元素相对于宿主元素的尺寸再向外扩大至少20px */ margin: -30px; }
14、折角效果
.box { position: relative; width: 15em; height: 8em; background: #4ca2cd; /* 回退样式 */ /* background-size的值为 sin30=1.5em/x cos30=1.5em/y x=3em y=1.732em*/ background: linear-gradient(-150deg, transparent 1.5em, #4ca2cd 0); border-radius: 0.5em; } .box::before { content: ''; position: absolute; top: 0; right: 0; background: linear-gradient(to left bottom, transparent 50%, rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.4)) 100% 0 no-repeat; width: 1.73em; height: 3em; /* 平移的偏移量 x - y = 3 - √3 = 1.3em */ transform: translateY(-1.3em) rotate(-30deg); transform-origin: bottom right; border-bottom-left-radius: inherit; box-shadow: -0.2em 0.2em 0.3em -0.1em rgba(0, 0, 0, 0.15); }
15、插入换行
dt, dd { display: inline; margin: 0; } dd { font-weight: bold; } dd + dt::before { content: "\A"; white-space: pre; } dd + dd::before { content: ', '; font-weight: normal; margin-left: -.25em; }
16、文本行的斑马条纹
pre { width: 20em; line-height: 1.5; padding: 0.5em; background: hsl(20, 50%, 95%); background-image: linear-gradient(rgba(120, 0, 0, 0.1) 50%, transparent 0); /* background-size的位置值是line-height的两倍 */ background-size: auto 3em; /* 渐变背景会从盒子的顶部开始,代码行和条纹会有错位的问题,设置background-origin让渐变背景以content box的外沿作为基准 */ background-origin: content-box; font-family: Consolas, Monaco, monospace; color: #666; } code { font: inherit }
17、自定义下划线
a { background: linear-gradient(#79b, #79b) no-repeat; background-size: 100% 1px; background-position: 0 1em; /* 解决下划线穿过字母的降部 */ text-shadow: 0.05em 0 white, -0.05em 0 white; }
18、扩大可点击区域
.border_one { padding: 0.3em 0.5em; color: #fff; /* 扩大按钮的可点击区域 */ border: 10px solid transparent; border-radius: 50%; background: #4ca2cd; /* 背景色只填充padding-box内部区域,默认值是border-box */ background-clip: padding-box; /* 利用box-shadow内嵌投影(inset)模拟边框 */ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3) inset; font: bold 150%/1 sans-serif; cursor: pointer; margin: 20px; }
上述方案尚有不足之处,就是为元素添加外部投影后会得到一个怪异的效果,所以改用伪元素来实现
.border_two {
position: relative;
border: none;
padding: 0.3em 0.5em;
color: #fff;
border-radius: 50%;
background: #4ca2cd;
/* 利用box-shadow内嵌投影(inset)模拟边框 */
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3) inset;
font: bold 150%/1 sans-serif;
cursor: pointer;
/* 添加外部投影 */
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3) inset,
0 0.1em 0.2em -0.05em rgba(0, 0, 0, 0.5);
}
.border_two::before {
content: '';
position: absolute;
top: -10px; right: -10px;
bottom: -10px; left: -10px;
}
19、自定义复选框
在绝大多数浏览器中我们基本无法控制复选框和单选框的样式,但我们可以利用<label>
标签来模拟复选框,可以让我们自己去自定义复选框样式。
<span>
<input type="checkbox" id="awesome" disabled />
<label for="awesome">Awesome!</label>
</span>
<span>
<input type="checkbox" id="awesome2" checked />
<label for="awesome2">Awesome!</label>
</span>
/* 隐藏原来的复选框 */
input[type="checkbox"] {
position: absolute;
clip: rect(0,0,0,0);
}
input[type="checkbox"] + label::before {
content: '\a0';
display: inline-block;
vertical-align: 0.2em;
width: 0.8em;
height: 0.8em;
margin-right: 0.2em;
border-radius: 0.2em;
background: silver;
text-indent: 0.15em;
line-height: 0.65;
}
input[type="checkbox"]:checked + label::before {
content: '\2713';
background: #4ca2cd;
}
input[type="checkbox"]:focus + label::before {
box-shadow: 0 0 0.1em 0.1em #58a;
}
input[type="checkbox"]:disabled + label::before {
background: #dcdfe6;
box-shadow: none;
cursor: not-allowed;
}
span:nth-child(2) {
margin-left: 10px;
}
20、通过模糊来弱化背景
<div class="box">
<main class="de_emphasized"></main>
<doalog class="doalog">11.11</doalog>
</div>
.box {
position: relative;
width: 45%;
height: 45%;
color: #fff;
font: 200%/1.6 Baskerville, Palatino, serif;
}
main {
width: 100%;
height: 100%;
background: url('./img/bg.jpg') no-repeat;
}
main.de_emphasized {
/* 滤镜的模糊 + 阴影 */
filter: blur(3px) contrast(0.8) brightness(0.8);
}
.doalog {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 150px;
height: 200px;
text-align: center;
line-height: 150px;
border-top: 3px solid #AB2D31;
/* 空心字效果 */
text-shadow: 1px 1px #AB2D31, -1px -1px #AB2D31,
1px -1px #AB2D31, -1px 1px #AB2D31;
background: #fff;
}
21、交互式图片对比控件
<div class="image-slider">
<img src="./img/cat-before.jpg" alt="Before" />
<img src="./img/cat.jpg" alt="After" />
</div>
.image-slider {
position:relative;
display: inline-block;
}
.image-slider div {
position: absolute;
top: 0; bottom: 0; left: 0;
width: 50%;
overflow: hidden;
}
.image-slider img {
display: block;
user-select: none;
}
.image-slider input {
position: absolute;
left: 0;
bottom: 10px;
width: 100%;
filter: contrast(0.5);
margin: 0;
}
imageSlider() {
let imageSliderList = document.getElementsByClassName('image-slider');
imageSliderList = Array.prototype.slice.call(imageSliderList);
imageSliderList.forEach(slider => {
console.log('slid', slider)
// 创建一个div元素,并用它包裹第一个图片元素
const divNode = document.createElement('div');
const imgNode = slider.querySelector('img');
slider.insertBefore(divNode, imgNode);
divNode.appendChild(imgNode);
// 创建滑块
const range = document.createElement('input');
range.type = 'range';
range.oninput = function() {
console.log('dqw', this.value)
divNode.style.width = this.value + '%';
}
slider.appendChild(range);
})
}
22、自适应内部元素
figure {
/* 回退样式 */
max-width: 300px;
/* 这个容器内部最大的不可断行元素的宽度(即最宽的单词、图片或具有固定宽度的盒元素) */
max-width: min-content;
/* 水平居中 */
margin: auto;
border: 1px solid silver;
padding: 10px;
}
figure > img {
max-width: inherit;
}
23、Fluid Fixed
header, section, footer {
/* 回退样式 */
padding: 1em;
padding: 1em calc(50% - 350px);
}
24、Sticky Footer
body {
display: flex;
flex-flow: column;
/* 1vh = 视口高度的1% */
min-height: 100vh;
}
main {
/* flex-grow flex-shrink flex-basis的简写语法 */
flex: 1;
}
25、弹跳动画
@keyframes bounce {
60%, 80%, to {
transform: translateY(200px);
animation-timing-function: ease;
}
70% {
transform: translateY(100px);
}
90% {
transform: translateY(160px);
}
}
.ball {
width: 5em;
height: 5em;
background: #4ca2cd;
border-radius: 50%;
animation: bounce 3s cubic-bezier(0.1, 0.25, 0.1, 0.25);
}
26、弹性过渡
<label>
Your username: <input type="text" id="username" />
<span class="callout">Only letters, numbers, underscores (_) and hyphens (-) allowed!</span>
</label>
input {
display: block;
padding: 0 .4em;
font: inherit;
}
.callout {
position: absolute;
max-width: 14em;
padding: .6em .8em;
border-radius: .3em;
margin: .3em 0 0 -.2em;
background: #fed;
border: 1px solid rgba(0,0,0,.3);
box-shadow: .05em .2em .6em rgba(0,0,0,.2);
font-size: 75%;
}
.callout:before {
content: "";
position: absolute;
top: -.4em;
left: 1em;
padding: .35em;
background: inherit;
border: inherit;
border-right: 0;
border-bottom: 0;
transform: rotate(45deg);
}
input:not(:focus) + .callout {
/* 失去焦点时缩放到0 */
transform: scale(0);
transition: 0.25s transform;
}
.callout {
transform-origin: 1.4em -0.4em;
transition: 0.5s cubic-bezier(0.25, 0.1, 0.3, 1.5) transform;
}
27、逐帧动画
准备工作:需要将每一帧图片横向拼接,如下图所示。最终利用动画向左循环移动每一帧图片。
最终效果图:
@keyframes loader {
to {
background-position: -800px 0;
}
}
.loader {
width: 100px;
height: 100px;
background: url('./img/loader.png') 0 0;
animation: loader 1s infinite steps(8);
}
28、打字动画
@keyframes typing {
from {
width: 0;
}
}
@keyframes caret {
50% {
/* 回退样式 */
border-color: currentColor;
border-color: transparent;
}
}
h1 {
font: bold 200% Consolas, Monaco, monospace;
/* ch: 0字形的宽度。在等宽字体中,“0”字形的宽度与其他所有字形的宽度是一样的 */
white-space: nowrap;
overflow: hidden;
color: gray;
/* 回退样式 */
border-right: 0.05em solid transparent;
border-right: 0.05em solid;
}
typing() {
const typingNode = document.querySelectorAll('h1');
typingNode.forEach(h1 => {
let length = h1.textContent.length, s = h1.style;
s.width = length + 'ch';
s.animation = `typing 6s steps(${length}), caret 1s steps(1) infinite`;
})
}
29、状态平滑的动画
@keyframes panoramic {
to {
/* 左右移动背景图片 */
background-position: 100% 0;
}
}
.box {
width: 250px;
height: 150px;
background: url('./img/strawberry.jpg');
background-size: auto 100%;
animation: panoramic 10s linear infinite alternate;
/* 动画暂停 */
animation-play-state: paused;
}
.box:hover, box:focus {
/* 动画继续 */
animation-play-state: running;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。