读《css揭秘》


CSS3 Patterns Gallery (verou.me)

复杂的背景图案

网格

//多背景叠加实现线宽不一的蓝图网格
background: #58a;
background-image:
linear-gradient(white 2px, transparent 0),
linear-gradient(90deg, white 2px, transparent 0),
linear-gradient(hsla(0,0%,100%,.3) 1px,
transparent 0),
linear-gradient(90deg, hsla(0,0%,100%,.3) 1px,
transparent 0);
background-size: 75px 75px, 75px 75px,
15px 15px, 15px 15px;
//径向渐变实现的圆点阵列
background: #655;
background-image: radial-gradient(tan 30%, transparent 0);
background-size: 30px 30px;
//波点图案
background: #655;
background-image: radial-gradient(tan 30%, transparent 0),
radial-gradient(tan 30%, transparent 0);
background-size: 30px 30px;
background-position: 0 0, 15px 15px;
//三角形拼接实现的棋盘
background: #eee;
background-image: linear-gradient(45deg, rgba(0, 0, 0, .25) 25%,
transparent 0, transparent 75%, rgba(0, 0, 0, .25) 0),
linear-gradient(45deg, rgba(0, 0, 0, .25) 25%,
transparent 0, transparent 75%, rgba(0, 0, 0, .25) 0);
background-position: 0 0, 15px 15px;
background-size: 30px 30px;
//svg实现的棋盘
background: #eee url('data:image/svg+xml,\
width="100" height="100" \
fill-opacity=".25">\
\
\
');
background-size: 30px 30px;

伪随机背景

"蝉原则": 用质数隐藏循环的规律

//蝉原则实现的随机背景
background: hsl(20, 40%, 90%);
background-image:
linear-gradient(90deg, #fb3 11px, transparent 0),
linear-gradient(90deg, #ab4 23px, transparent 0),
linear-gradient(90deg, #655 41px, transparent 0);
background-size: 41px 100%, 61px 100%, 83px 100%;

其他应用

  • 在照片图库中,为每幅图片应用细微的伪随机旋转效果时,可以使 用多个 :nth-child(a) 选择符,且让 a 是质数。

  • 如果要生成一个动画,而且想让它看起来不是按照明显的规律在 循环时,我们可以应用多个时长为质数的动画。

//伪随机动画
@keyframes spin { to { transform: rotate(1turn); } }
@keyframes radius { 50% { border-radius: 50%; } }
@keyframes color { 33% { color: orange; } 66% { color: deeppink } }
@keyframes width { 50% { border-width: .3em; } }

.loading:before {
content: '';
display: block;
width: 4em;
height: 4em;
margin: 0 auto 1em;
border: 1.5em solid;
color: yellowgreen;
box-sizing: border-box;
animation: 1s spin, .7s radius, 1.1s color, 1.3s width;
animation-timing-function: linear;
animation-iteration-count: infinite;
}

.loading {
margin: auto;
}

body {
margin: 0;
display: flex;
min-height: 100vh;
text-align: center;
background: #655;
color: white;
}

 

连续的图像边框

//白色背景和图片背景叠加实现图像边框
padding: 1em;
border: 1em solid transparent;
background:
linear-gradient(white, white) padding-box,
url(stone-art.jpg) border-box 0 / cover;
//信封边框
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% } } .marching-ants { 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 / .6em .6em; animation: ants 12s linear infinite; }

自适应的椭圆

//横向劈开的半椭圆
border-radius: 50% / 100% 100% 0 0;
//纵向劈开的半椭圆
border-radius: 100% 0 0 100% / 50%;
//四分之一椭圆
border-radius: 100% 0 0 0;

平行四边形

transform: skewX(-45deg);

菱形

//双元素实现菱形图片(hack)

...

.picture {
width: 400px;
transform: rotate(45deg);
overflow: hidden;
}
.picture > img {
max-width: 100%;
transform: rotate(-45deg) scale(1.42);
}
//clip-path实现菱形图片(推荐)
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%);
}

切角效果

//斜向线性渐变实现切角
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;
//radial-gradient实现内凹圆角
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;
//svg实现切角
border: 20px solid transparent; //回退支持
border-image: 1 url('data:image/svg+xml,\
width="3" height="3" fill="%2358a">\
\
');
background: #58a;
background-clip: padding-box;

梯形标签页

//伪元素+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;
}

简单的饼图

//给pie元素添加animation-delay属性(负值)即可指定百分比

.pie {
position: relative;
width: 100px;
line-height: 100px;
border-radius: 50%;
background: yellowgreen;
background-image:
linear-gradient(to right, transparent 50%, #655 0);
color: transparent;
text-align: center;
}
@keyframes spin {
to { transform: rotate(.5turn); }
}
@keyframes bg {
50% { background: #655; }
}
.pie::before {
content: '';
position: absolute;
top: 0; left: 50%;
width: 50%; height: 100%;
border-radius: 0 100% 100% 0 / 50%;

transform-origin: left;
animation: spin 50s linear infinite,
bg 100s step-end infinite;
animation-play-state: paused;
animation-delay: inherit;
}
//svg实现饼图
$$('.pie').forEach(function(pie) {
var p = parseFloat(pie.textContent);
var NS = "http://www.w3.org/2000/svg";
var svg = document.createElementNS(NS, "svg");
var circle = document.createElementNS(NS, "circle");
var 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 = '';
svg.appendChild(title);
svg.appendChild(circle);
pie.appendChild(svg);
});

单侧投影

//下侧投影
box-shadow: 0 5px 4px -4px black;
//邻边投影
box-shadow: 3px 3px 6px -3px black;
//双侧投影
box-shadow: 5px 0 5px -5px black,
-5px 0 5px -5px black;

不规则投影

filter: drop-shadow(2px 2px 10px rgba(0,0,0,.5));

染色效果

//基于滤镜的方案,支持transition动画
img {
transition: .5s filter;
filter: sepia(1) saturate(4) hue-rotate(295deg);
}
img:hover,
img:focus {
filter: none;
}
//基于混合模式的方案
//双元素
a {
background: hsl(335, 100%, 50%);
}
img {
mix-blend-mode: luminosity;
}
//基于混合模式
//单元素
.tinted-image {
width: 640px; height: 440px;
background-size: cover;

background-blend-mode: luminosity;
transition: .5s background-color;
}
.tinted-image:hover {

}

毛玻璃



"The only way to get rid of a temptation[...]"


Oscar Wilde,
The Picture of Dorian Gray





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;
}

折角效果

//任意角度的折角效果
.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);
}

连字符断行

hyphens: auto;

插入换行

dd + dt::before {
content: '\A';
white-space: pre;
}
dd + dd::before {
content: ', ';
font-weight: normal;
}

文本行的斑马条纹

padding: .5em;
line-height: 1.5;
background: beige;
background-size: auto 3em;
background-origin: content-box;
background-image: linear-gradient(rgba(0,0,0,.2) 50%,
transparent 0);

调整tab的宽度

pre {
tab-size: 2;
}

连字

font-variant-ligatures: common-ligatures
discretionary-ligatures
historical-ligatures;

华丽的&字符

//为某个或某些特定的字符指定字体
@font-face {
font-family: Ampersand;
src: local('Baskerville'),
local('Goudy Old Style'),
local('Palatino'),
local('Book Antiqua');
unicode-range: U+26;
}
h1 {
font-family: Ampersand, Helvetica, sans-serif;
}

自定义下划线

background: linear-gradient(gray, gray) no-repeat;
background-size: 100% 1px;
background-position: 0 1.15em;
text-shadow: .05em 0 white, -.05em 0 white; //避让字符降部
//虚线下划线
background: linear-gradient(90deg,
gray 66%, transparent 0) repeat-x;
background-size: .2em 2px;
background-position: 0 1em;

现实中的文字效果

//凸版印刷效果
background: hsl(210, 13%, 60%);
color: hsl(210, 13%, 30%);
text-shadow: 0 1px 1px hsla(0,0%,100%,.8);
//空心字效果
//不同方向少量偏移量
background: deeppink;
color: white;
text-shadow: 1px 1px black, -1px -1px black,
1px -1px black, -1px 1px black;
//重叠多层轻微模糊投影
text-shadow: 0 0 1px black, 0 0 1px black,
0 0 1px black, 0 0 1px black,
0 0 1px black, 0 0 1px black;
//svg方案
//可读性和可维护性较差



CSS



h1 {
font: 500%/1 Rockwell, serif;
background: deeppink;
color: white;
}
h1 text {
fill: currentColor;
}
h1 svg { overflow: visible }
h1 use {
stroke: black;
stroke-width: 6;
stroke-linejoin: round;
}
//文字外发光效果
//多层重叠的和文字同色的text-shadow
a {
background: #203;
color: white;
transition: 1s;
}
a:hover {
text-shadow: 0 0 .1em, 0 0 .3em;
}

a {
background: #203;
color: white;
transition: 1s;
}
a:hover {
color: transparent;
text-shadow: 0 0 .1em white, 0 0 .3em white;
}
//文字突起效果
color: transparent;
text-shadow: 0 0 .1em white, 0 0 .3em white;
};

//复古标志牌
color: white;
background: hsl(0,50%,45%);
text-shadow: 1px 1px black, 2px 2px black,
3px 3px black, 4px 4px black,
5px 5px black, 6px 6px black,
7px 7px black, 8px 8px black;

环形文字

//svg方案


id="circle" />

circular reasoning works because




.circular {
width: 30em;
height: 30em;
margin: 3em auto 0;
}
.circular svg {
display: block;
overflow: visible;
}

//svg方案的js脚本

circular reasoning works because


$$('.circular').forEach(function(el) {
var NS = "http://www.w3.org/2000/svg";
var xlinkNS = "http://www.w3.org/1999/xlink";
var svg = document.createElementNS(NS, "svg");
var circle = document.createElementNS(NS, "path");
var text = document.createElementNS(NS, "text");
var textPath = document.createElementNS(NS, "textPath");
svg.setAttribute("viewBox", "0 0 100 100");
circle.setAttribute("d", "M0,50 a50,50 0 1,1 0,1z");
circle.setAttribute("id", "circle");
textPath.textContent = el.textContent;
textPath.setAttributeNS(xlinkNS, "xlink:href", "#circle");
text.appendChild(textPath);
svg.appendChild(circle);
svg.appendChild(text);
el.textContent = '';
el.appendChild(svg);
});

鼠标光标

//禁用状态提示
:disabled, [disabled], [aria-disabled="true"] {
cursor: not-allowed;
}
/隐藏光标
cursor: none;

扩大可点击区域

//基于border
border: 10px solid transparent;
background-clip: padding-box;
//此时要用border可用box-shadow模拟
border: 10px solid transparent;
box-shadow: 0 0 0 1px rgba(0,0,0,.3) inset;
background-clip: padding-box;
//伪元素方案
button {
position: relative;
}
button::before {
content: '';
position: absolute;
top: -10px; right: -10px;
bottom: -10px; left: -10px;
}

自定义复选框




input[type="checkbox"] + label::before {
content: '\a0'; /* 不换行空格 */
display: inline-block;
vertical-align: .2em;
width: .8em;
height: .8em;
margin-right: .2em;
border-radius: .2em;
background: silver;
text-indent: .15em;
line-height: .65;
}
input[type="checkbox"]:checked + label::before {
content: '\2713';
background: yellowgreen;
}
input[type="checkbox"] {
position: absolute;
clip: rect(0,0,0,0);
}
input[type="checkbox"]:focus + label::before {
box-shadow: 0 0 .1em .1em #58a;
}
input[type="checkbox"]:disabled + label::before {
background: gray;
box-shadow: none;
color: #555;
}
//开关式按钮
input[type="checkbox"] {
position: absolute;
clip: rect(0,0,0,0);
}
input[type="checkbox"] + label {
display: inline-block;
padding: .3em .5em;
background: #ccc;
background-image: linear-gradient(#ddd, #bbb);
border: 1px solid rgba(0,0,0,.2);
border-radius: .3em;
box-shadow: 0 1px white inset;
text-align: center;
text-shadow: 0 1px 1px white;
}
input[type="checkbox"]:checked + label,
input[type="checkbox"]:active + label {
box-shadow: .05em .1em .2em rgba(0,0,0,.6) inset;
border-color: rgba(0,0,0,.3);
background: #bbb;
}

通过阴影弱化背景

  • 独立元素实现

  • 伪元素实现

  • box-shadow实现(无法拦截鼠标事件)

//box-shadow方案, vmax为vh和vw中的较大值
box-shadow: 0 0 0 50vmax rgba(0,0,0,.8);
//backdrop方案
dialog::backdrop {
background: rgba(0, 0, 0, .8);
}

通过模糊弱化背景

//结构代码
Bacon Ipsum dolor sit amet...


O HAI, I'm a dialog. Click on me to dismiss.
//滤镜
main {
transition: .6s filter;
}
main.de-emphasized {
filter: blur(5px);
}
//滤镜+阴影
main.de-emphasized {
filter: blur(3px) contrast(.8) brightness(.8);
}

滚动提示

容器可滚动时容器上方和下方出现阴影

//结构代码

  • Ada Catlace

  • Alan Purring

  • Schr?dingcat

  • Tim Purrners-Lee

  • WebKitty

  • Json

  • Void

  • Neko

  • NaN

  • Cat5

  • Vector

ul {
display: inline-block;
overflow: auto;
width: 7.2em;
height: 7em;
border: 1px solid silver;
padding: .3em .5em;
list-style: none;
margin-top: 2em;
font: 100 200%/1.6 'Frutiger LT Std', sans-serif;
background: linear-gradient(white 15px, hsla(0,0%,100%,0)) 0 0 / 100% 50px,
radial-gradient(at top, rgba(0,0,0,.2), transparent 70%) 0 0 / 100% 15px,
linear-gradient(to top, white 15px, hsla(0,0%,100%,0)) bottom / 100% 50px,
radial-gradient(at bottom, rgba(0,0,0,.2), transparent 70%) bottom / 100% 15px;
background-repeat: no-repeat;
background-attachment: local, scroll, local, scroll;
margin-top: 30px;
}

交互式的图片对比控件

.image-slider {
position:relative;
display: inline-block;
}
.image-slider > div {
position: absolute;
top: 0; bottom: 0; left: 0;
width: 50%;
max-width: 100%;
overflow: hidden;
resize: horizontal;
}
//resizer美化
.image-slider > div::before {
content: '';
position: absolute;
bottom: 0; right: 0;
width: 12px; height: 12px;
padding: 5px;
background:
linear-gradient(-45deg, white 50%, transparent 0);
background-clip: content-box;
cursor: ew-resize;
}
.image-slider img {
display: block;
user-select: none;
}
//结构代码

Before
After

//js脚本
$$('.image-slider').forEach(function(slider) {
// 创建附加的div元素,并用它包住第一个图片元素
var div = document.createElement('div');
var img = slider.querySelector('img');
slider.insertBefore(img, div);
div.appendChild(img);
// 创建滑块
var range = document.createElement('input');
range.type = 'range';
range.oninput = function() {
div.style.width = this.value + '%';
};
slider.appendChild(range);
});
//css
.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%;
margin: 0;
}

自适应内部元素

figure {
width: min-content;
margin: auto;
}

精确控制表格列宽

table {
table-layout: fixed;
width: 100%;
}

根据兄弟元素的数量来设置

li:first-child:nth-last-child(4),
li:first-child:nth-last-child(4) ~ li {
/* 当列表正好包含四项时,命中所有列表项 */
}
li:first-child:nth-last-child(n+4),
li:first-child:nth-last-child(n+4) ~ li {
/* 当列表至少包含四项时,命中所有列表项 */
}
li:first-child:nth-last-child(-n+4),
li:first-child:nth-last-child(-n+4) ~ li {
/* 当列表最多包含四项时,命中所有列表项 */
}
li:first-child:nth-last-child(n+2):nth-last-child(-n+6),
li:first-child:nth-last-child(n+2):nth-last-child(-n+6) ~ li {
/* 当列表包含2~6项时,命中所有列表项 */
}

满幅的背景,定宽的内容

//结构代码





//css
footer {
padding: 1em;
padding: 1em calc(50% - 450px);
background: #333;
}

垂直居中

//基于绝对定位的方案
//结构代码

Am I centered yet?


Center me, please!



//css
main {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
//基于视口的方案
main {
width: 18em;
padding: 1em 1.5em;
margin: 50vh auto 0;
transform: translateY(-50%);
}
//基于flex布局
//最佳方案
body {
display: flex;
min-height: 100vh;
margin: 0;
}
main {
margin: auto;
}

紧贴底部的页脚

flex实现

缓动效果

//弹跳动画
@keyframes bounce {
60%, 80%, to {
transform: translateY(400px);
animation-timing-function: ease-out;
}
70% { transform: translateY(300px); }
90% { transform: translateY(360px); }
}
.ball {
/* 其余样式写在这里 */
animation: bounce 3s ease-in;
}

贝塞尔曲线: cubic-bezier(x1, y1, x2, y2)

@keyframes bounce {
60%, 80%, to {
transform: translateY(400px);
animation-timing-function: ease;
}
70% { transform: translateY(300px); }
90% { transform: translateY(360px); }
}
.ball {
/* 外观样式 */
animation: bounce 3s cubic-bezier(.1,.25,1,.25);
}
//提示框弹性过度
@keyframes elastic-grow {
from { transform: scale(0); }
70% {
transform: scale(1.1);
animation-timing-function:
cubic-bezier(.1,.25,1,.25); /* 反向的ease */
}
}
input:not(:focus) + .callout { transform: scale(0); }
input:focus + .callout { animation: elastic-grow .5s; }
.callout { transform-origin: 1.4em -.4em; }
//提示框弹性过度纯过度(transition)实现
//通过调整调速函数cubic-bezier使其最高点大于1
input:not(:focus) + .callout { transform: scale(0);
transition: .25s transform; //默认设置了调速函数为ease
}
.callout {
transform-origin: 1.4em -.4em;
transition: .5s cubic-bezier(.25,.1,.3,1.5) transform;
}

逐帧动画

//基于steps调速函数实现
@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);
/* 把文本隐藏起来 */
text-indent: 200%;
white-space: nowrap;
overflow: hidden;
}

闪烁效果

//平滑闪烁
@keyframes blink-smooth { to { color: transparent } }
.highlight {
animation: .5s blink-smooth 6 alternate;
}
//硬切闪烁
@keyframes blink { 50% { color: transparent } }
.highlight {
animation: 1s blink 3 steps(1); /* 或用step-end */
}

打字动画

@keyframes typing {
from { width: 0 }
}
@keyframes caret {
50% { border-color: transparent; }
}
h1 {
width: 15ch; /* 文本的宽度 */
overflow: hidden;
white-space: nowrap;
border-right: .05em solid;
animation: typing 6s steps(15),
caret 1s steps(1) infinite;
}
//js脚本
$$('h1').forEach(function(h1) {
var len = h1.textContent.length, s = h1.style;
s.width = len + 'ch';
s.animationTimingFunction = "steps("+len+"),steps(1)";
});

Julian Cheal

状态平滑的动画

//hover时暂停动画
@keyframes panoramic {
to { background-position: 100% 0; }
}
.panoramic {
width: 150px; height: 150px;
background: url("img/naxos-greece.jpg");
background-size: auto 100%;
animation: panoramic 10s linear infinite alternate;
animation-play-state: paused;
}
.panoramic:hover, .panoramic:focus {
animation-play-state: running;
}

沿环形路径平移的动画

//多元素
//结构代码





//css
@keyframes spin {
to { transform: rotate(1turn); }
}
.avatar {
animation: spin 3s infinite linear;
transform-origin: 50% 150px; /* 150px = 路径的半径 */
}
.avatar > img {
animation: inherit;
animation-direction: reverse;
}
/结构代码





//css
@keyframes spin {
from {
transform: rotate(0turn)
translateY(-150px) translateY(50%)
rotate(1turn);
}
to {
transform: rotate(1turn)
translateY(-150px) translateY(50%)
rotate(0turn);
}
}
.avatar { animation: spin 3s infinite linear; }