如何用純 CSS 創作一個“女神來了,快讓路”的動畫
效果預覽
線上演示按下右側的“點選預覽”按鈕可以在當前頁面預覽,點選連結可以全屏預覽。
https://codepen.io/comehope/pen/RYZbmE
可互動視訊
此視訊是可以互動的,你可以隨時暫停視訊,編輯視訊中的程式碼。
請用 chrome, safari, edge 開啟觀看。
https://scrimba.com/p/pEgDAM/c7MPZtg
原始碼下載
本地下載每日前端實戰系列的全部原始碼請從 github 下載:
https://github.com/comehope/front-end-daily-challenges
程式碼解讀
定義 dom,容器中包含 2 個子元素,分別代表 1 個女生和一群男生(4個),每個 span
元素代表 1 個人(1 個方塊):
<figure class="container"> <span class="girl"></span> <div class="boys"> <span></span> <span></span> <span></span> <span></span> </div> </figure>
居中顯示:
body {
margin: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
定義容器尺寸和它的子元素佈局:
.container {
width: 8em;
height: 1em;
font-size: 35px;
display: flex;
justify-content: space-between;
}
畫出 5 個方塊,用邊框作為輔助線幫助定位:
.container span { width: 1em; height: 1em; border: 1px dashed black; /* 輔助線 */ } .boys { width: 6em; display: flex; justify-content: space-between; }
用偽元素設定元素的樣式,使它們變得柔和一些,為男生和男生填上不同的顏色,同時刪掉上一步的輔助線:
.container span::before {
content: '';
position: absolute;
width: inherit;
height: inherit;
border-radius: 15%;
box-shadow: 0 0 0.2em rgba(0, 0, 0, 0.3);
}
.girl::before {
background-color: hotpink;
}
.boys span::before {
background-color: dodgerblue;
}
使 4 個男生色塊的顏色逐漸變淡,增加一點層次感:
.boys span:nth-child(1)::before {
filter: brightness(1);
}
.boys span:nth-child(2)::before {
filter: brightness(1.15);
}
.boys span:nth-child(3)::before {
filter: brightness(1.3);
}
.boys span:nth-child(4)::before {
filter: brightness(1.45);
}
接下來製作動畫效果。
先增加女生移動的效果,同時顏色也做漸淡處理,後面其他動畫的時間要保持一致,所以把動畫時長設定為變數:
.container span {
width: 1em;
height: 1em;
--duration: 3s;
}
.girl {
animation: slide var(--duration) ease-in-out infinite;
}
@keyframes slide {
from {
transform: translateX(0);
filter: brightness(1);
}
to {
transform: translatex(calc(8em - (1em * 1.25)));
filter: brightness(1.45);
}
}
然後增加第 1 個男生跳開的動畫效果,注意從 15% 到 35% 旋轉的原點是在元素的正上方:
.boys span {
animation: var(--duration) ease-in-out infinite;
}
.boys span:nth-child(1) {
animation-name: jump-off-1;
}
@keyframes jump-off-1 {
0%, 15% {
transform: rotate(0deg);
}
35%, 100% {
transform-origin: -50% center;
transform: rotate(-180deg);
}
}
參考第 1 個男生的動畫效果,再增加另外 3 個男生跳開的動畫效果,區別只是調整了關鍵幀的時間,依次後延 15% 的時間:
.boys span:nth-child(2) {
animation-name: jump-off-2;
}
.boys span:nth-child(3) {
animation-name: jump-off-3;
}
.boys span:nth-child(4) {
animation-name: jump-off-4;
}
@keyframes jump-off-2 {
0%, 30% {
transform: rotate(0deg);
}
50%, 100% {
transform-origin: -50% center;
transform: rotate(-180deg);
}
}
@keyframes jump-off-3 {
0%, 45% {
transform: rotate(0deg);
}
65%, 100% {
transform-origin: -50% center;
transform: rotate(-180deg);
}
}
@keyframes jump-off-4 {
0%, 60% {
transform: rotate(0deg);
}
80%, 100% {
transform-origin: -50% center;
transform: rotate(-180deg);
}
}
為第 1 個男生增加擬人的動畫效果,這個效果寫在 ::before
偽元素中,動畫的過程是從正常到壓扁、然後抻長、再壓扁、最後恢復正常,注意從 25% 到 40% 的壓扁變形,因為此時主元素已經翻個兒,所以 transform-origin
的原點和 從 5% 到 15% 的壓扁變形的原點不一樣:
.boys span::before {
animation: var(--duration) ease-in-out infinite;
}
.boys span:nth-child(1)::before {
filter: brightness(1);
animation-name: jump-down-1;
}
@keyframes jump-down-1 {
5% {
transform: scale(1, 1);
}
15% {
transform-origin: center bottom;
transform: scale(1.3, 0.7);
}
20%, 25% {
transform-origin: center bottom;
transform: scale(0.8, 1.4);
}
40% {
transform-origin: center top;
transform: scale(1.3, 0.7);
}
55%, 100% {
transform: scale(1, 1);
}
}
參考第 1 個男生 ::before
偽元素的動畫效果,再增加另外 3 個男生擬人的動畫效果,區別只是調整了關鍵幀的時間,依次後延 15% 的時間:
.boys span:nth-child(2)::before {
animation-name: jump-down-2;
}
.boys span:nth-child(3)::before {
animation-name: jump-down-3;
}
.boys span:nth-child(4)::before {
animation-name: jump-down-4;
}
@keyframes jump-down-2 {
20% {
transform: scale(1, 1);
}
30% {
transform-origin: center bottom;
transform: scale(1.3, 0.7);
}
35%, 40% {
transform-origin: center bottom;
transform: scale(0.8, 1.4);
}
55% {
transform-origin: center top;
transform: scale(1.3, 0.7);
}
70%, 100% {
transform: scale(1, 1);
}
}
@keyframes jump-down-3 {
35% {
transform: scale(1, 1);
}
45% {
transform-origin: center bottom;
transform: scale(1.3, 0.7);
}
50%, 55% {
transform-origin: center bottom;
transform: scale(0.8, 1.4);
}
70% {
transform-origin: center top;
transform: scale(1.3, 0.7);
}
85%, 100% {
transform: scale(1, 1);
}
}
@keyframes jump-down-4 {
50% {
transform: scale(1, 1);
}
60% {
transform-origin: center bottom;
transform: scale(1.3, 0.7);
}
65%, 70% {
transform-origin: center bottom;
transform: scale(0.8, 1.4);
}
85% {
transform-origin: center top;
transform: scale(1.3, 0.7);
}
100%, 100% {
transform: scale(1, 1);
}
}
至此,女生從左側移動到右側的動畫效果已經完成。
把所有動畫屬性都加上 alternate
引數,使所有動畫都往復執行,就實現了從右側再回到左側的效果:
.girl {
animation: slide var(--duration) ease-in-out infinite alternate;
}
.boys span {
animation: var(--duration) ease-in-out infinite alternate;
}
.boys span::before {
animation: var(--duration) ease-in-out infinite alternate;
}
大功告成!
原文地址:https://segmentfault.com/a/1190000016287188