《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)); }
.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; }
.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); }
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; }
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 }
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; }
.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; }
在絕大多數瀏覽器中咱們基本沒法控制複選框和單選框的樣式,但咱們能夠利用<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; }
<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; }
<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); }) }
figure { /* 回退樣式 */ max-width: 300px; /* 這個容器內部最大的不可斷行元素的寬度(即最寬的單詞、圖片或具備固定寬度的盒元素) */ max-width: min-content; /* 水平居中 */ margin: auto; border: 1px solid silver; padding: 10px; } figure > img { max-width: inherit; }
header, section, footer { /* 回退樣式 */ padding: 1em; padding: 1em calc(50% - 350px); }
body { display: flex; flex-flow: column; /* 1vh = 視口高度的1% */ min-height: 100vh; } main { /* flex-grow flex-shrink flex-basis的簡寫語法 */ flex: 1; }
@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); }
<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; }
準備工做:須要將每一幀圖片橫向拼接,以下圖所示。最終利用動畫向左循環移動每一幀圖片。
最終效果圖:
@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); }
@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`; }) }
@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; }