前言

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