CSS 揭祕

前言

《CSS 揭祕》這本書是由做者 Lea Verou 編寫的,書中包含了不少 css 技巧,好比咱們經常使用的漸變色,做者能利用漸變色實現不少有意思的 css 效果,確實很佩服做者的 css 功底。下面我把我認爲日常能用上的效果放在這裏。javascript

效果

一、半透明邊框

若直接爲邊框設置半透明背景,border box 呈現出的是 content box 的背景。須要將 background-clip (默認值是 border-box )設置爲 padding-box ,這樣內容區域的背景從 padding box 處裁切,border box 就會呈現本身本來的半透明背景。css

.box {
  width: 15em;
  height: 8em;
  border: 1em solid hsla(0, 0%, 100%, .5);
  background:white;
  background-clip: padding-box;
}

二、多重邊框

利用 box-shadow 能夠建立任意數量的投影來實現多重邊框。每一層外框的設置值是外框內部全部 border-shadow 的第四個參數與將要設置外框值的和。html

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

三、背景定位

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

四、邊框圓內角

若是邊框的顏色只是純色,可使用一個元素來實現;若是邊框的顏色不是純色,可使用兩個元素來實現。java

.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%);
}

五、條紋背景

以橫向條紋進行介紹,豎向條紋幾乎與橫向條紋一致,只需在 linear-gredient 的第一個位置設置 to right/left參數,而且將 background-size 的值左右互換便可。瀏覽器

等寬條紋漸變app

.box {
  width: 15em;
  height: 10em;
  /* 使用#fb3顏色在50%的位置開始漸變,使用#58a顏色在50%的位置中止漸變 */
  background: linear-gradient(#fb3 50%, #58a 50%);
  background-size: 100% 2em;
}

不等寬條紋漸變ide

.box {
  width: 15em;
  height: 10em;
  /* 若是第二個色標的位置值設置爲0,那它的位置就老是會被瀏覽器調整爲前一個色標的位置值 */
  background: linear-gradient(#fb3 30%, #58a 0);
  background-size: 100% 2em;
}

斜向條紋svg

.box {
  width: 15em;
  height: 10em;
  background: repeating-linear-gradient(45deg,#79b 0, #79b 15px, #58a 0, #58a 30px);
}

六、連續的圖像邊框

  • 老式信封邊框

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

七、平行四邊形

若是直接對元素進行變形,元素內容也會隨之改變,因此咱們須要兩個元素,一個存放內容,一個應用變形樣式。這裏利用僞元素實現,將全部的樣式應用到僞元素上,再對僞元素進行變形。函數

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

八、菱形圖片

@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%);
}

九、切角效果

  • 漸變方案
    字體

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

    十、餅圖

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

    十一、投影

    能夠利用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);
    }

    十二、不規則投影

    兩個例子的公共代碼

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

    1三、毛玻璃效果

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

    1四、折角效果

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

    1五、插入換行

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

    1六、文本行的斑馬條紋

    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
    }

    1七、自定義下劃線

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

    1八、擴大可點擊區域

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

1九、自定義複選框

在絕大多數瀏覽器中咱們基本沒法控制複選框和單選框的樣式,但咱們能夠利用<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;
}

2一、交互式圖片對比控件

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

2二、自適應內部元素

figure {
  /* 回退樣式 */
  max-width: 300px;
  /* 這個容器內部最大的不可斷行元素的寬度(即最寬的單詞、圖片或具備固定寬度的盒元素) */
  max-width: min-content;
  /* 水平居中 */
  margin: auto;
  border: 1px solid silver;
  padding: 10px;
}
figure > img {
  max-width: inherit;
}

2三、Fluid Fixed

header, section, footer {
  /* 回退樣式 */
  padding: 1em;
  padding: 1em calc(50% - 350px);
}

2四、Sticky Footer

body {
  display: flex;
  flex-flow: column;
  /* 1vh = 視口高度的1% */
  min-height: 100vh;
}
main {
  /* flex-grow flex-shrink flex-basis的簡寫語法 */ 
  flex: 1;
}

2五、彈跳動畫

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

2六、彈性過渡

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

2七、逐幀動畫

準備工做:須要將每一幀圖片橫向拼接,以下圖所示。最終利用動畫向左循環移動每一幀圖片。

最終效果圖:

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

2八、打字動畫

@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`;
  })
}

2九、狀態平滑的動畫

@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;
}
相關文章
相關標籤/搜索