前言
本文是筆者寫CSS時(shí)常用的套路讯檐。不論效果再怎么華麗,萬變不離其宗染服。
1别洪、交錯(cuò)動畫
有時(shí)候,我們需要給多個(gè)元素添加同一個(gè)動畫柳刮,播放后挖垛,不難發(fā)現(xiàn)它們會一起運(yùn)動痒钝,一起結(jié)束,這樣就會顯得很平淡無奇痢毒。那么如何將動畫變得稍微有趣一點(diǎn)呢送矩?很簡單,既然它們都是同一時(shí)刻開始運(yùn)動的哪替,那么讓它們不在同一時(shí)刻運(yùn)動不就可以了嗎益愈。如何讓它們不在同一時(shí)刻運(yùn)動呢?注意到CSS動畫有延遲(delay
)這一屬性夷家。舉個(gè)栗子,比如有十個(gè)元素播放十個(gè)動畫敏释,將第二個(gè)元素的動畫播放時(shí)間設(shè)定為比第一個(gè)元素晚0.5秒(也就是將延遲設(shè)為0.5秒)库快,其他元素以此類推,這樣它們就會錯(cuò)開來钥顽,形成一種獨(dú)特的視覺效果义屏。
這就是所謂的交錯(cuò)動畫:通過設(shè)置不同的延遲時(shí)間,達(dá)到動畫交錯(cuò)播放的效果蜂大。本demo地址:https://codepen.io/alphardex/pen/XWWWBmQ
<div class="loading">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
body {
display: flex;
height: 100vh;
justify-content: center;
align-items: center;
background: #222;
}
.loading {
$colors: #7ef9ff, #89cff0, #4682b4, #0f52ba, `#000080;
display: flex;
animation-delay: 1s;
.dot {
position: relative;
width: 2em;
height: 2em;
margin: 0.8em;
border-radius: 50%;
&::before {
position: absolute;
content: "";
width: 100%;
height: 100%;
background: inherit;
border-radius: inherit;
animation: wave 2s ease-out infinite;
}
@for $i from 1 through 5 {
&:nth-child(#{$i}) {
background: nth($colors, $i);
&::before {
animation-delay: $i * 0.2s;
}
}
}
}
}
@keyframes wave {
50%,
75% {
transform: scale(2.5);
}
80%,
100% {
opacity: 0;
}
}
2闽铐、用JS分割文本
還有一種經(jīng)常用到的玩法:用JS將句子或單詞分割成字母,并給每個(gè)字母加上不同延時(shí)的動畫奶浦,同樣也很華麗兄墅。<p class="landIn">Ano hi watashitachi mada shiranai no Fushigi no monogatari desu.</p>
@import url("https://fonts.googleapis.com/css?family=Lora:400,400i,700");
body {
display: flex;
flex-direction: column;
height: 100vh;
justify-content: center;
align-items: center;
background-image: linear-gradient(rgba(16, 16, 16, 0.8),
rgba(16, 16, 16, 0.8)),
url(https://i.loli.net/2019/10/18/buDT4YS6zUMfHst.jpg);
background-size: cover;
}
p {
margin: 0 9em;
font-size: 2em;
font-weight: 600;
}
.landIn {
display: flex;
flex-wrap: wrap;
line-height: 1.8;
color: white;
font-family: Lora, serif;
white-space: pre;
span {
animation: landIn 0.8s ease-out both;
}
}
@keyframes landIn {
from {
opacity: 0;
transform: translateY(-20%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
let landInTexts = document.querySelectorAll(".landIn");
landInTexts.forEach(landInText => {
let letters = landInText.textContent.split("");
landInText.textContent = "";
letters.forEach((letter, i) => {
let span = document.createElement("span");
span.textContent = letter;
span.style.animationDelay = `${i * 0.05}s`;
landInText.append(span);
});
});
本demo地址:https://codepen.io/alphardex/full/KKwvKGY一般我們都是從第一個(gè)元素開始交錯(cuò)的。但如果要從中間元素開始交錯(cuò)的話澳叉,就要給當(dāng)前元素的延時(shí)各加上一個(gè)值隙咸,這個(gè)值就是中間元素的下標(biāo)到當(dāng)前元素的下標(biāo)的距離(也就是下標(biāo)之差的絕對值)與步長的乘積,即:delay + Math.abs(i - middle) * step
成洗,其中中間元素的下標(biāo)middle = letters.filter(e => e !== "").length / 2
本demo地址:https://codepen.io/alphardex/full/eYYMYXJ所有有交錯(cuò)特性的動畫都在這兒
<div class="reveal">sword art online</div>
@import url("https://fonts.googleapis.com/css?family=Raleway:400,400i,700");
body {
display: flex;
height: 100vh;
justify-content: center;
align-items: center;
text-align: center;
background: #222;
}
.reveal {
position: relative;
display: flex;
color: #6ee1f5;
font-size: 2em;
font-family: Raleway, sans-serif;
letter-spacing: 3px;
text-transform: uppercase;
white-space: pre;
span {
opacity: 0;
transform: scale(0);
animation: fadeIn 2.4s forwards;
}
&::before,
&::after {
position: absolute;
content: "";
top: 0;
bottom: 0;
width: 2px;
height: 100%;
background: white;
opacity: 0;
transform: scale(0);
}
&::before {
left: 50%;
animation: slideLeft 1.5s cubic-bezier(0.7, -0.6, 0.3, 1.5) forwards;
}
&::after {
right: 50%;
animation: slideRight 1.5s cubic-bezier(0.7, -0.6, 0.3, 1.5) forwards;
}
}
@keyframes fadeIn {
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes slideLeft {
to {
left: -6%;
opacity: 1;
transform: scale(0.9);
}
}
@keyframes slideRight {
to {
right: -6%;
opacity: 1;
transform: scale(0.9);
}
}
let duration = 0.8;
let delay = 0.3;
let revealText = document.querySelector(".reveal");
let letters = revealText.textContent.split("");
revealText.textContent = "";
let middle = letters.filter(e => e !== " ").length / 2;
letters.forEach((letter, i) => {
let span = document.createElement("span");
span.textContent = letter;
span.style.animationDelay = `${delay + Math.abs(i - middle) * 0.1}s`;
revealText.append(span);
});
3五督、隨機(jī)粒子動畫
說到隨機(jī)性,我們可以實(shí)現(xiàn)一種更瘋狂的效果:給幾百個(gè)粒子添加交錯(cuò)動畫瓶殃,并且交錯(cuò)時(shí)間隨機(jī)充包,位置大小也都是隨機(jī)。如此一來我們就能用純CSS模擬出下雪的效果遥椿。又到了白色相簿的季節(jié)呢~為什么你寫CSS這么熟練盎?本demo地址:https://codepen.io/alphardex/full/dyPorwJ
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
body {
height: 100vh;
background: radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%);
overflow: hidden;
filter: drop-shadow(0 0 10px white);
}
@function random_range($min, $max) {
$rand: random();
$random_range: $min + floor($rand * (($max - $min) + 1));
@return $random_range;
}
.snow {
$total: 200;
position: absolute;
width: 10px;
height: 10px;
background: white;
border-radius: 50%;
@for $i from 1 through $total {
$random-x: random(1000000) * 0.0001vw;
$random-offset: random_range(-100000, 100000) * 0.0001vw;
$random-x-end: $random-x + $random-offset;
$random-x-end-yoyo: $random-x + ($random-offset / 2);
$random-yoyo-time: random_range(30000, 80000) / 100000;
$random-yoyo-y: $random-yoyo-time * 100vh;
$random-scale: random(10000) * 0.0001;
$fall-duration: random_range(10, 30) * 1s;
$fall-delay: random(30) * -1s;
&:nth-child(#{$i}) {
opacity: random(10000) * 0.0001;
transform: translate($random-x, -10px) scale($random-scale);
animation: fall-#{$i} $fall-duration $fall-delay linear infinite;
}
@keyframes fall-#{$i} {
#{percentage($random-yoyo-time)} {
transform: translate($random-x-end, $random-yoyo-y) scale($random-scale);
}
to {
transform: translate($random-x-end-yoyo, 100vh) scale($random-scale);
}
}
}
}
偽類和偽元素
4修壕、偽類
HTML元素的狀態(tài)是可以動態(tài)變化的愈捅。舉個(gè)栗子,當(dāng)你的鼠標(biāo)懸浮到一個(gè)按鈕上時(shí)慈鸠,按鈕就會變成“懸浮”狀態(tài)蓝谨,這時(shí)我們就可以利用偽類:hover
來選中這一狀態(tài)的按鈕灌具,并對其樣式進(jìn)行改變。:hover
是筆者最最常用的一個(gè)偽類譬巫。還有一個(gè)很常用的偽類是:nth-child
咖楣,用于選中元素的某一個(gè)子元素。其他的類似:focus
芦昔、:focus-within
等也有一定的使用诱贿。本demo地址:https://codepen.io/alphardex/pen/pooYKVa
<button
data-text="Start"
class="btn btn-primary btn-ghost btn-border-stroke btn-text-float-up"
>
<div class="btn-borders">
<div class="border-top"></div>
<div class="border-right"></div>
<div class="border-bottom"></div>
<div class="border-left"></div>
</div>
<span class="btn-text">Start</span>
</button>
@import url(https://fonts.googleapis.com/css?family=Lato);
body {
display: flex;
height: 100vh;
justify-content: center;
align-items: center;
text-align: center;
background: #222;
}
.btn {
--hue: 190;
--ease-in-duration: 0.25s;
--ease-out-duration: 0.65s;
--ease-out-delay: var(--ease-in-duration);
position: relative;
padding: 1rem 3rem;
font-size: 1rem;
line-height: 1.5;
color: white;
text-decoration: none;
background-color: hsl(var(--hue), 100%, 41%);
border: 1px solid hsl(var(--hue), 100%, 41%);
outline: transparent;
overflow: hidden;
cursor: pointer;
user-select: none;
white-space: nowrap;
transition: 0.25s;
&:hover {
background: hsl(var(--hue), 100%, 31%);
}
&-primary {
--hue: 171;
}
&-ghost {
color: hsl(var(--hue), 100%, 41%);
background-color: transparent;
border-color: hsl(var(--hue), 100%, 41%);
&:hover {
color: white;
}
}
&-border-stroke {
border-color: hsla(var(--hue), 100%, 41%, 0.35);
.btn-borders {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
.border-top {
position: absolute;
top: 0;
width: 100%;
height: 1px;
background: hsl(var(--hue), 100%, 41%);
transform: scaleX(0);
transform-origin: left;
}
.border-right {
position: absolute;
right: 0;
width: 1px;
height: 100%;
background: hsl(var(--hue), 100%, 41%);
transform: scaleY(0);
transform-origin: bottom;
}
.border-bottom {
position: absolute;
bottom: 0;
width: 100%;
height: 1px;
background: hsl(var(--hue), 100%, 41%);
transform: scaleX(0);
transform-origin: left;
}
.border-left {
position: absolute;
left: 0;
width: 1px;
height: 100%;
background: hsl(var(--hue), 100%, 41%);
transform: scaleY(0);
transform-origin: bottom;
}
// when unhover, ease-in top, right; ease-out bottom, left
.border-left {
transition: var(--ease-out-duration) var(--ease-out-delay)
cubic-bezier(0.2, 1, 0.2, 1);
}
.border-bottom {
transition: var(--ease-out-duration) var(--ease-out-delay)
cubic-bezier(0.2, 1, 0.2, 1);
}
.border-right {
transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
}
.border-top {
transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
}
}
&:hover {
color: hsl(var(--hue), 100%, 41%);
background: transparent;
.border-top,
.border-bottom {
transform: scaleX(1);
}
.border-left,
.border-right {
transform: scaleY(1);
}
// when hover, ease-in bottom, left; ease-out top, right
.border-left {
transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
}
.border-bottom {
transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
}
.border-right {
transition: var(--ease-out-duration) var(--ease-out-delay)
cubic-bezier(0.2, 1, 0.2, 1);
}
.border-top {
transition: var(--ease-out-duration) var(--ease-out-delay)
cubic-bezier(0.2, 1, 0.2, 1);
}
}
}
&-text-float-up {
&::after {
position: absolute;
content: attr(data-text);
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
transform: translateY(35%);
transition: 0.25s ease-out;
}
// when hover, ease-in top-text; ease-out bottom-text
.btn-text {
display: block;
transition: 0.75s 0.1s cubic-bezier(0.2, 1, 0.2, 1);
}
&:hover {
// when hover, ease-in bottom-text; ease-out top-text
.btn-text {
opacity: 0;
transform: translateY(-25%);
transition: 0.25s ease-out;
}
&::after {
opacity: 1;
transform: translateY(0);
transition: 0.75s 0.1s cubic-bezier(0.2, 1, 0.2, 1);
}
}
}
}
5、絕對定位實(shí)現(xiàn)多重邊框
誰規(guī)定按鈕只能有一套邊框的咕缎?利用絕對定位和padding
珠十,我們可以給按鈕做出3套大小不一的邊框來,這樣效果更炫了凭豪。
本demo地址:https://codepen.io/alphardex/full/ZEYXomW
<button class="btn btn-primary btn-ghost btn-multiple-border-stroke">
<div class="btn-borders-group">
<div class="border-top"></div>
<div class="border-right"></div>
<div class="border-bottom"></div>
<div class="border-left"></div>
</div>
<div class="btn-borders-group">
<div class="border-top"></div>
<div class="border-right"></div>
<div class="border-bottom"></div>
<div class="border-left"></div>
</div>
<div class="btn-borders-group">
<div class="border-top"></div>
<div class="border-right"></div>
<div class="border-bottom"></div>
<div class="border-left"></div>
</div>
<span class="btn-text">Start</span>
</button>
@import url(https://fonts.googleapis.com/css?family=Lato);
body {
display: flex;
height: 100vh;
justify-content: center;
align-items: center;
text-align: center;
background: #222;
}
.btn {
--hue: 190;
--ease-in-duration: 0.25s;
--ease-out-duration: 0.65s;
--ease-out-delay: var(--ease-in-duration);
position: relative;
padding: 1rem 3rem;
font-size: 1rem;
line-height: 1.5;
color: white;
text-decoration: none;
background-color: hsl(var(--hue), 100%, 41%);
border: 1px solid hsl(var(--hue), 100%, 41%);
outline: transparent;
cursor: pointer;
user-select: none;
white-space: nowrap;
transition: 0.25s;
&:hover {
background: hsl(var(--hue), 100%, 31%);
}
&-primary {
--hue: 171;
}
&-ghost {
color: hsl(var(--hue), 100%, 41%);
background-color: transparent;
border-color: hsl(var(--hue), 100%, 41%);
&:hover {
color: white;
}
}
&-multiple-border-stroke {
border-color: transparent;
.btn-borders-group {
position: absolute;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
border: 1px solid hsla(var(--hue), 100%, 41%, 0.35);
&:nth-child(1) {
left: -8px;
padding: 0 8px;
}
&:nth-child(2) {
top: -8px;
padding: 8px 0;
}
&:nth-child(3) {
top: -4px;
left: -4px;
padding: 4px;
}
.border-top {
position: absolute;
top: 0;
width: 100%;
height: 1px;
background: hsl(var(--hue), 100%, 41%);
transform: scaleX(0);
transform-origin: left;
}
.border-right {
position: absolute;
right: 0;
width: 1px;
height: 100%;
background: hsl(var(--hue), 100%, 41%);
transform: scaleY(0);
transform-origin: bottom;
}
.border-bottom {
position: absolute;
bottom: 0;
width: 100%;
height: 1px;
background: hsl(var(--hue), 100%, 41%);
transform: scaleX(0);
transform-origin: left;
}
.border-left {
position: absolute;
left: 0;
width: 1px;
height: 100%;
background: hsl(var(--hue), 100%, 41%);
transform: scaleY(0);
transform-origin: bottom;
}
// when unhover, ease-in top, right; ease-out bottom, left
.border-left {
transition: var(--ease-out-duration) var(--ease-out-delay) cubic-bezier(0.2, 1, 0.2, 1);
}
.border-bottom {
transition: var(--ease-out-duration) var(--ease-out-delay) cubic-bezier(0.2, 1, 0.2, 1);
}
.border-right {
transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
}
.border-top {
transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
}
}
&:hover {
color: hsl(var(--hue), 100%, 41%);
background: transparent;
.border-top,
.border-bottom {
transform: scaleX(1);
}
.border-left,
.border-right {
transform: scaleY(1);
}
// when hover, ease-in bottom, left; ease-out top, right
.border-left {
transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
}
.border-bottom {
transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
}
.border-right {
transition: var(--ease-out-duration) var(--ease-out-delay) cubic-bezier(0.2, 1, 0.2, 1);
}
.border-top {
transition: var(--ease-out-duration) var(--ease-out-delay) cubic-bezier(0.2, 1, 0.2, 1);
}
}
}
}
6焙蹭、偽元素
簡而言之,偽元素就是在原先的元素基礎(chǔ)上插入額外的元素嫂伞,而且這個(gè)元素不充當(dāng)HTML的標(biāo)簽孔厉,這樣就能保持HTML結(jié)構(gòu)的整潔。我們知道每個(gè)元素都有::before
和::after
這兩個(gè)偽元素帖努,也就是說每個(gè)元素都提供了3個(gè)矩形(元素本身1個(gè)撰豺,偽元素2個(gè))來供我們進(jìn)行形狀的繪制。現(xiàn)在又有了clip-path
這個(gè)屬性拼余,幾乎任意的形狀都可以被繪制出來污桦,全憑你的想象力上面的動圖是條子劃過文本的動畫,條子就是每個(gè)文本所對應(yīng)的偽元素匙监,對每個(gè)文本和其偽元素應(yīng)用動畫寡润,就能達(dá)到上圖的效果了本demo地址:https://codepen.io/alphardex/pen/jOEOEzZ
<header>
<h1 class="title">I'm alphardex.</h1>
<p class="subtitle">A CSS Wizard</p>
</header>
// palette: https://www.materialpalette.com/light-blue/pink
@import url("https://fonts.googleapis.com/css?family=Lato");
@import url("https://fonts.googleapis.com/css?family=Lora:400,400i,700");
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: #222;
}
header {
.title,
.subtitle {
position: relative;
width: 250px;
height: 30px;
color: transparent;
animation: fadeIn 2s 1.6s forwards;
&::before {
position: absolute;
content: "";
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: scaleX(0);
transform-origin: left;
animation: slideRight 2s cubic-bezier(0.75, 0, 0, 1) forwards;
}
}
.title {
margin: 0;
font-family: Lora, serif;
font-size: 32px;
line-height: 30px;
&::before {
background: #FF4081;
}
}
.subtitle {
margin: 10px 0 0 0;
font-family: Lato, sans-serif;
font-size: 12px;
line-height: 30px;
letter-spacing: 5px;
text-transform: uppercase;
animation-delay: 3.2s;
&::before {
background: #03A9F4;
animation-delay: 2s;
}
}
}
@keyframes fadeIn {
to {
color: white;
}
}
@keyframes slideRight {
50% {
transform: scaleX(1);
transform-origin: left;
}
50.1% {
transform-origin: right;
}
to {
transform-origin: right;
}
}
7、attr()生成文本內(nèi)容
元素可以有自定義的屬性值舅柜,它的命名格式通常為data-*``attr()
用于獲取元素的這種自定義屬性值梭纹,并賦值給其偽元素的content
作為其生成的內(nèi)容利用這個(gè)函數(shù),我們可以用偽元素在原先文本的基礎(chǔ)上“復(fù)制”出另一個(gè)文本致份,如下圖所示变抽。
overflow: hidden
绍载,把多余的文本遮住。通過JS分割文本并應(yīng)用交錯(cuò)動畫滔蝉,就得到了如下的效果击儡,這也是接下來本文要講的overflow
障眼法。本demo地址:https://codepen.io/alphardex/full/wvBeXjd
<ul class="float-text-menu">
<li><a href="#" data-text="Home">Home</a></li>
<li><a href="#" data-text="Archives">Archives</a></li>
<li><a href="#" data-text="Tags">Tags</a></li>
<li><a href="#" data-text="Categories">Categories</a></li>
<li><a href="#" data-text="About">About</a></li>
</ul>
@import url(https://fonts.googleapis.com/css?family=Lato);
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: black;
}
.float-text-menu {
display: flex;
flex-direction: column;
list-style-type: none;
li {
a {
display: flex;
padding: 6px;
color: white;
font-family: Lato, sans-serif;
text-decoration: none;
overflow: hidden;
span {
position: relative;
transition: 0.3s;
&::before {
position: absolute;
content: attr(data-text);
transform: translateY(130%);
}
}
&:hover {
span {
transform: translateY(-130%);
}
}
}
}
}
let floatTextMenuLinks = document.querySelectorAll(".float-text-menu li a");
floatTextMenuLinks.forEach(link => {
let letters = link.textContent.split("");
link.textContent = "";
letters.forEach((letter, i) => {
let span = document.createElement("span");
span.textContent = letter;
span.style.transitionDelay = `${i / 20}s`;
span.dataset.text = letter;
link.append(span);
});
});
8蝠引、overflow障眼法
之前有做過閃光按鈕的效果:鼠標(biāo)懸浮按鈕上時(shí)一道光從左到右劃過去阳谍。筆者就用漸變來模擬那道光蛀柴,通過transform: translateX()
將其平移至右邊。
但這樣明顯不對啊矫夯,這光為啥能被看見呢鸽疾?不應(yīng)該把它給“擋”起來嗎?于是乎训貌,給按鈕加上overflow: hidden
制肮,光在按鈕外的位置時(shí)就被隱藏起來了。
這就是障眼法的力量:)本demo地址:https://codepen.io/alphardex/pen/eYYzXBZ更多障眼法可以看看這個(gè)作品递沪,一次性看個(gè)夠XD
<button class="btn btn-primary btn-ghost btn-shine">
hover me
</button>
@import url(https://fonts.googleapis.com/css?family=Lato);
body {
display: flex;
height: 100vh;
justify-content: center;
align-items: center;
background: #222;
}
.btn {
--hue: 190;
position: relative;
padding: 1rem 3rem;
font-size: 1rem;
line-height: 1.5;
color: white;
text-decoration: none;
text-transform: uppercase;
background-color: hsl(var(--hue), 100%, 41%);
border: 1px solid hsl(var(--hue), 100%, 41%);
outline: transparent;
overflow: hidden;
cursor: pointer;
user-select: none;
white-space: nowrap;
transition: 0.25s;
&:hover {
background: hsl(var(--hue), 100%, 31%);
}
&-primary {
--hue: 187;
}
&-ghost {
color: hsl(var(--hue), 100%, 41%);
background-color: transparent;
border-color: hsl(var(--hue), 100%, 41%);
&:hover {
color: white;
}
}
&-shine {
color: white;
&::before {
position: absolute;
content: "";
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
120deg,
transparent,
hsla(var(--hue), 100%, 41%, 0.5),
transparent
);
transform: translateX(-100%);
transition: 0.6s;
}
&:hover {
background: transparent;
box-shadow: 0 0 20px 10px hsla(var(--hue), 100%, 41%, 0.5);
}
&:hover::before {
transform: translateX(100%);
}
}
}
9豺鼻、兄弟選擇符定制表單元素
提示:這里最好將input
作為label
的子元素,這樣用戶點(diǎn)擊label
時(shí)就能傳到input
上默認(rèn)的input
太丑怎么辦款慨?那就把它先抹掉拘领,用appearance: none
或opacity: 0
都可以然后,利用兄弟選擇符~
來定制和input
相鄰的所有元素(+
號也行樱调,只不過只能選中最近的元素),例如可以用偽元素生成一個(gè)新的方框代替原先的input
届良,利用偽類:checked
和動畫來表示它被勾選后的狀態(tài)笆凌,本質(zhì)上還是障眼法哦~
CSS特性
善用某些CSS特性,也可以為你的作品增色不少哦
<form>
<fieldset class="todo-list">
<legend class="todo-list__title">My Special Todo List</legend>
<label class="todo-list__label">
<input type="checkbox" name="" id="" />
<i class="check"></i>
<span>Make awesome CSS animation</span>
</label>
<label class="todo-list__label">
<input type="checkbox" name="" id="" />
<i class="check"></i>
<span>Watch awesome bangumi</span>
</label>
<label class="todo-list__label">
<input type="checkbox" name="" id="" />
<i class="check"></i>
<span>Encounter awesome people</span>
</label>
<label class="todo-list__label">
<input type="checkbox" name="" id="" />
<i class="check"></i>
<span>Be an awesome man</span>
</label>
</fieldset>
</form>
// color scheme: https://coolors.co/e63946-585b57-7b9fa1-264456-0b1420
@import url("https://fonts.googleapis.com/css?family=Lato:400,400i,700");
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: #0b1420;
}
.todo-list {
display: flex;
flex-direction: column;
padding: 0 75px 10px 30px;
background: #162740;
border: transparent;
.todo-list__title {
padding: 3px 6px;
color: #f1faee;
background-color: #264456;
}
.todo-list__label {
display: flex;
align-items: center;
margin: 40px 0;
font-size: 24px;
font-family: Lato, sans-serif;
color: #f1faee;
cursor: pointer;
input[type="checkbox"] {
opacity: 0;
appearance: none;
& + .check {
position: absolute;
width: 25px;
height: 25px;
border: 2px solid #f1faee;
transition: 0.2s;
}
&:checked + .check {
width: 25px;
height: 15px;
border-top: transparent;
border-right: transparent;
transform: rotate(-45deg);
}
& ~ span {
position: relative;
left: 40px;
white-space: nowrap;
transition: 0.5s;
&::before {
position: absolute;
content: "";
top: 50%;
left: 0;
width: 100%;
height: 1px;
background: #f1faee;
transform: scaleX(0);
transform-origin: right;
transition: transform 0.5s;
}
}
&:checked ~ span {
color: #585b57;
&::before {
transform: scaleX(1);
transform-origin: left;
}
}
}
}
}
10士葫、animation
此處包括transition
和transform
CSS動畫可以說是利用CSS設(shè)計(jì)炫酷特效的最強(qiáng)法器乞而,它幾乎貫穿了我的所有作品有人問我為什么我能想出這么多的動畫?筆者閱番百部慢显,對常用的動畫技巧了如指掌,同樣那些酷炫的網(wǎng)站只要細(xì)心觀察,也會給筆者帶來很多設(shè)計(jì)上的靈感究抓。一言以蔽之:只有多欣賞動畫邪财,才能寫出好的動畫。
11应狱、border-radius
為盒子添加圓角共郭,經(jīng)常用來美化按鈕等組件如果設(shè)定為50%
則是圓形,也很常用
不規(guī)則的曲邊形狀
調(diào)整多個(gè)頂點(diǎn)的border-radius
可以做出不規(guī)則的曲邊形狀
本demo地址:https://codepen.io/alphardex/full/abbWOPR
<nav class="navtab">
<ul>
<li class="navtab-item active">
<i class="fas fa-home"></i>
<span>Home</span>
</li>
<li class="navtab-item">
<i class="fas fa-compass"></i>
<span>Explore</span>
</li>
<li class="navtab-item">
<i class="fas fa-bell"></i>
<span>Notification</span>
</li>
<li class="navtab-item">
<i class="fas fa-user"></i>
<span>Profile</span>
</li>
</ul>
</nav>
@import url(https://fonts.googleapis.com/css?family=Lato);
@mixin center {
display: flex;
justify-content: center;
align-items: center;
}
body {
@include center;
height: 100vh;
font-family: Lato, sans-serif;
background: #03a9f4;
}
.navtab {
--navtab-width: 600px;
--navtab-item-width: calc(var(--navtab-width) / 4 - 20px);
--navtab-overlay-width: calc(var(--navtab-item-width) + 80px);
--active-index: 0;
position: relative;
width: var(--navtab-width);
height: 150px;
background: white;
border: 1em solid white;
// https://9elements.github.io/fancy-border-radius/full-control.html#15.5.15.15-50.95.50.85-150.600
border-radius: 5% 5% 15% 15% / 15% 15% 50% 50%;
overflow: hidden;
ul {
@include center;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
list-style-type: none;
li.navtab-item {
@include center;
z-index: 2;
flex-direction: column;
width: var(--navtab-item-width);
height: 100%;
color: #0288d1;
cursor: pointer;
transition: 0.5s ease;
i {
font-size: 2em;
transition: 0.5s ease;
}
span {
font-size: 20px;
user-select: none;
opacity: 0;
transition: 0.5s ease;
}
&.active {
width: var(--navtab-overlay-width);
i {
transform: translateY(-10px);
}
span {
opacity: 1;
}
}
}
}
&::after {
position: absolute;
content: "";
top: 0;
left: 0;
height: 100%;
width: var(--navtab-overlay-width);
background: #b3e5fc;
border-radius: 20px;
transform: translateX(calc(var(--navtab-item-width) * var(--active-index)));
transition: 0.5s ease;
}
}
let navtab = document.querySelector("nav.navtab");
let navtabItems = document.querySelectorAll("li.navtab-item");
navtabItems.forEach((navtabItem, activeIndex) =>
navtabItem.addEventListener("click", () => {
navtabItems.forEach(navtabItem => navtabItem.classList.remove("active"));
navtabItem.classList.add("active");
(navtab as HTMLElement).style.setProperty(
"--active-index",
`${activeIndex}`
);
})
);
12疾呻、box-shadow
為盒子添加陰影除嘹,增加盒子的立體感,可以多層疊加岸蜗,并且會使陰影更加絲滑
<ul class="pagination">
<li class="page-prev">
<a class="prev" href="#"><i class="fas fa-arrow-left"></i></a>
</li>
<li class="page-number active"><a href="#">1</a></li>
<li class="page-number"><a href="#">2</a></li>
<li class="page-number"><a href="#">3</a></li>
<li class="page-number"><a href="#">4</a></li>
<li class="page-number"><a href="#">5</a></li>
<li class="page-number"><a href="#">6</a></li>
<li class="page-next">
<a class="next" href="#"><i class="fas fa-arrow-right"></i></a>
</li>
</ul>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: lightblue;
}
.pagination {
--active-index: 0;
display: flex;
padding: 10px 20px;
background: white;
border-radius: 50px;
box-shadow:
0 0.3px 0.6px rgba(0, 0, 0, 0.056),
0 0.7px 1.3px rgba(0, 0, 0, 0.081),
0 1.3px 2.5px rgba(0, 0, 0, 0.1),
0 2.2px 4.5px rgba(0, 0, 0, 0.119),
0 4.2px 8.4px rgba(0, 0, 0, 0.144),
0 10px 20px rgba(0, 0, 0, 0.2)
;
list-style-type: none;
li {
margin: 0 5px;
&.page-number {
width: 50px;
height: 50px;
line-height: 50px;
text-align: center;
&:hover a {
color: white;
background: #777;
}
&.active a {
color: white;
background: #333;
}
}
&.page-prev,
&.page-next {
font-weight: 700;
}
&.page-prev {
margin-right: 20px;
}
&.page-next {
margin-left: 20px;
}
a {
display: block;
line-height: 50px;
font-size: 20px;
font-weight: 600;
text-decoration: none;
color: #777;
border-radius: 50%;
transition: 0.3s;
&.prev:hover,
&.next:hover {
color: darken(#777, 50%);
}
}
}
}
let prevLink = document.querySelector(".prev");
let nextLink = document.querySelector(".next");
let pagination = document.querySelector(".pagination");
let pageNumberLinks = document.querySelectorAll(".page-number a");
let maxPageIndex = pageNumberLinks.length - 1;
pageNumberLinks.forEach((pageNumberLink, activeIndex) => {
pageNumberLink.addEventListener("click", () => {
pageNumberLinks.forEach(pageNumberLink =>
pageNumberLink.parentElement.classList.remove("active")
);
pageNumberLink.parentElement.classList.add("active");
(pagination as HTMLElement).style.setProperty(
"--active-index",
`${activeIndex}`
);
});
});
prevLink.addEventListener("click", () => {
pageNumberLinks.forEach(pageNumberLink =>
pageNumberLink.parentElement.classList.remove("active")
);
let activeIndex = Number(
(pagination as HTMLElement).style.getPropertyValue("--active-index")
);
activeIndex = activeIndex > 0 ? activeIndex - 1 : 0;
pageNumberLinks[activeIndex].parentElement.classList.add("active");
(pagination as HTMLElement).style.setProperty(
"--active-index",
`${activeIndex}`
);
});
nextLink.addEventListener("click", () => {
pageNumberLinks.forEach(pageNumberLink =>
pageNumberLink.parentElement.classList.remove("active")
);
let activeIndex = Number(
(pagination as HTMLElement).style.getPropertyValue("--active-index")
);
activeIndex = activeIndex < maxPageIndex ? activeIndex + 1 : maxPageIndex;
pageNumberLinks[activeIndex].parentElement.classList.add("active");
(pagination as HTMLElement).style.setProperty(
"--active-index",
`${activeIndex}`
);
});
13尉咕、遮罩
如果給box-shadow
的擴(kuò)張半徑設(shè)定足夠大的值,可以用它來遮住背景璃岳,而無需額外的div元素
14年缎、內(nèi)發(fā)光
注意到box-shadow
還有個(gè)inset
悔捶,用于盒子內(nèi)部發(fā)光利用這個(gè)特性我們可以在盒子內(nèi)部的某個(gè)范圍內(nèi)設(shè)定顏色,做出一個(gè)新月形
本demo地址:https://codepen.io/alphardex/full/eYmGEGp
text-shadow
文本陰影,本質(zhì)上和box-shadow
相同缓溅,只不過是相對于文本而言蛇损,常用于文本發(fā)光,也可通過多層疊加來制作霓虹文本和偽3D文本等效果
15坛怪、發(fā)光文本
本demo地址:https://codepen.io/alphardex/full/Exxodoq
16淤齐、霓虹文本
本demo地址:https://codepen.io/alphardex/full/rNNwmZz
17、偽3D文本
本demo地址:https://codepen.io/alphardex/full/QWWavvx
18袜匿、background-clip:text
能將背景裁剪成文字的前景色更啄,常用來和color: transparent
配合生成漸變文本
本demo地址:https://codepen.io/alphardex/full/QWwveZG
gradient
漸變可以作為背景圖片的一種,具有很強(qiáng)的色彩效果居灯,甚至可以用來模擬光
19祭务、linear-gradient
線性漸變是筆者最常用的漸變這個(gè)作品用到了HTML的dialog
標(biāo)簽,漸變背景怪嫌,動畫以及overflow
障眼法义锥,細(xì)心的你看出來了嗎:)本demo地址:https://codepen.io/alphardex/full/eYYxzBm
20、radial-gradient
徑向漸變常用于生成圓形背景岩灭,上面例子中Snow的背景就是一個(gè)橢圓形的徑向漸變此外拌倍,由于背景可以疊加,我們可以疊加多個(gè)不同位置大小的徑向漸變來生成圓點(diǎn)群噪径,再加上動畫就產(chǎn)生了一種微粒效果柱恤,無需多余的div
元素。
本demo地址:https://codepen.io/alphardex/full/OJPvMGx
21找爱、conic-gradient
圓錐漸變可以用于制作餅圖用一個(gè)偽元素疊在餅圖上面梗顺,并將content
設(shè)為某個(gè)值(這個(gè)值通過CSS變量計(jì)算出來),就能制作出度量計(jì)的效果车摄,障眼法又一次完成了它的使命荚守。
本demo地址:https://codepen.io/alphardex/full/BaydVvQ
filter
PS里的濾鏡,玩過的都懂练般,blur
最常用
22矗漾、backdrop-filter
對背景應(yīng)用濾鏡,產(chǎn)生毛玻璃的效果本demo地址:https://codepen.io/alphardex/full/pooQMVp
23薄料、mix-blend-mode
PS里的混合模式敞贡,常用于文本在背景下的特殊效果以下利用濾色模式(screen
)實(shí)現(xiàn)文本視頻蒙版效果
本demo地址:https://codepen.io/alphardex/full/wvvLYpV
24、clip-path
PS里的裁切摄职,可以制作各種不規(guī)則形狀誊役。如果和動畫結(jié)合也會相當(dāng)有意思获列。本demo地址:https://codepen.io/alphardex/full/ZEEBRrq
25、-webkit-box-reflect
投影效果蛔垢,不怎么常用击孩,適合立體感強(qiáng)的作品。本demo地址:https://codepen.io/alphardex/full/ExaZgxp
26鹏漆、web animations
雖然這并不是一個(gè)CSS特性巩梢,但是它經(jīng)常用于完成那些CSS所做不到的事情那么何時(shí)用它呢?當(dāng)CSS動畫中有屬性無法從CSS中獲取時(shí)艺玲,自然就會使用到它了
跟蹤鼠標(biāo)的位置
目前CSS還尚未有獲取鼠標(biāo)位置的API括蝠,因此考慮用JS來進(jìn)行通過查閱相關(guān)的DOM API,發(fā)現(xiàn)在監(jiān)聽鼠標(biāo)事件的API中饭聚,可通過e.clientX
和e.clientY
來獲得鼠標(biāo)當(dāng)前的位置既然能夠獲取鼠標(biāo)的位置忌警,那么跟蹤鼠標(biāo)的位置也就不是什么難事了:通過監(jiān)聽mouseenter
和mouseleave
事件,來獲取鼠標(biāo)出入一個(gè)元素時(shí)的位置秒梳,并用此坐標(biāo)來當(dāng)作鼠標(biāo)的位移距離法绵,監(jiān)聽mousemove
事件,來獲取鼠標(biāo)在元素上移動時(shí)的位置酪碘,同樣地用此坐標(biāo)來當(dāng)作鼠標(biāo)的位移距離朋譬,這樣一個(gè)跟蹤鼠標(biāo)的效果就實(shí)現(xiàn)了。
本demo地址:https://codepen.io/alphardex/full/OJPmQGz
CSS Houdini
CSS Houdini是CSS的底層API婆跑,它使我們能夠通過這套接口來擴(kuò)展CSS的功能
讓漸變動起來
目前來說,我們無法直接給漸變添加動畫庭呜,因?yàn)闉g覽器不理解要改變的值是什么類型這時(shí)滑进,我們就可以利用CSS.registerProperty()
來注冊我們的自定義變量,并聲明其語法類型(syntax
)為顏色類型<color>
募谎,這樣瀏覽器就能理解并對顏色應(yīng)用插值方法來進(jìn)行動畫還記得上文提到的圓錐漸變conic-gradient()
嗎扶关?既然它可以用來制作餅圖,那么我們能不能讓餅圖動起來呢数冬?答案是肯定的节槐,定義三個(gè)變量:--color1
、--color2
和--pos
拐纱,其中--pos
的語法類型為長度百分比<length-percentage>
铜异,將其從0
變?yōu)?code>100%,餅圖就會順時(shí)針旋轉(zhuǎn)出現(xiàn)秸架。
本demo地址:https://codepen.io/alphardex/full/RwNxpXQ
27、彩蛋
將交錯(cuò)動畫和偽類偽元素結(jié)合起來寫出來的慎重勇者風(fēng)格的菜單本demo地址:https://codepen.io/alphardex/full/ExavZdV
最后:
恭喜你將本文讀完了食茎。不論是過了一場視覺盛宴也好蒂破,還是學(xué)到了不少東西也好,還是直接從書簽?zāi)菍?dǎo)航到這里也好(笑)别渔,CSS的力量始終超乎你的想象附迷。只要敢于創(chuàng)作,你就是這個(gè)世界的神钠糊。
`