單標籤!純CSS實現動態晴陰雨雪

1 引言

本期分享一下如何僅用CSS3,實現單標籤的動態晴陰雨雪。技術關鍵點就是「單標籤」和「純CSS」。先看下最終效果:css

再看看HTML代碼:

<!--晴-->
<div class="weather sunny"></div>
<!--陰-->
<div class="weather cloudy"></div>
<!--雨-->
<div class="weather rainy"></div>
<!--雪-->
<div class="weather snowy"></div>
複製代碼

沒錯,就是這麼任性,每一個動圖就一個標籤,並且無圖無JS!下面就來詳細介紹下技術實現。html

涉及到的關鍵CSS3屬性:web

  1. transform:用於移位、旋轉、縮放效果
  2. box-shadow:利用投影實現圖像的複製(關鍵!)
  3. clip-path:基於繪製的形狀對元素進行遮罩處理
  4. animation:設置元素的動畫

以及實現單標籤最關鍵的:before、:after僞元素運用。微信

經過本期分享,能學到什麼?post

最大的一點就是:box-shadow的另類玩法——「影分身」。動畫

下面開始逐個講解。ui

2 基礎背景

圖中的藍塊背景區域,很基礎了,不用講了。spa

設置了區域的寬高、背景色和圓角效果。3d

.weather {
    position: relative;
    display: inline-block;
    width: 180px;
    height: 240px;
    background: #23b7e5;
    border-radius: 8px;
}
複製代碼

3 晴天

晴天圖標由兩個元素組成:太陽和內六角形陽光。code

:before、:after 兩個僞元素能夠在元素內部分別「添加」一個元素,正好都利用上了。

3.1 繪製太陽

首先,用 :before實現太陽。

.sunny:before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 60px;
    height: 60px;
    background: #F6D963;
    border-radius: 50%;
    box-shadow: 0 0 20px #ff0;
    z-index: 2;
}
複製代碼

content用來生成一個元素。

position、top、left、transform用來實現中心居中。

box-shadow實現外發光效果,這只是box-shadow最基本最經常使用的使用方式。

3.2 繪製內六角形

用 :after實現內六角形。

實現的關鍵就是使用遮罩。經過clip-path繪製一個內六角形。這就變成了一個簡單的初中幾何問題。

內六角形由兩個等邊三角形拼合而成。

合併以後,咱們能夠把總體劃分爲若干個徹底相同的小等邊三角形。

在垂直方向作個輔助線,鏈接中間頂部和底部兩點。不難發現,「垂直方向的最大長度」要大於「水平方向的最大長度」。

設小等邊三角形的邊長爲1,之內六角形中心爲座標原點,能夠計算出每一個點的座標,以下:

爲了使用clip-path的百分比定位來繪製圖像,下一步須要把長度座標轉換爲百分比座標。

設垂直方向最大長度爲100%,仍之內六角形中心爲座標原點,每一個點的座標值轉換以下:

因爲clip-path繪製原點是在左上角,x軸右側爲正值,y軸下方爲正值。須要作下座標系轉換。即:

新x軸座標值 = 舊x軸座標值 + 50%
新y軸座標值 = (舊y軸座標值 - 50%) * -1

使用clip-path的polygon方法繪製內六角形,座標已經過上面的步驟計算出來了。

樣式代碼以下:

.sunny:after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -45px 0 0 -45px; 
    width: 90px;
    height: 90px;
    background: #FFEB3B;
    clip-path: polygon(
    	50% 0%,
        64.43% 25%,
        93.3% 25%,
        78.87% 50%,
        93.3% 75%,
        64.43% 75%,
        50% 100%,
        35.57% 75%,
        6.7% 75%,
        21.13% 50%,
        6.7% 25%,
        35.57% 25%);
    z-index: 1;
    animation: sunScale 2s linear infinite;
}
@keyframes sunScale {
    0% {
        transform: scale(1);
    }
    50% {
    	transform: scale(1.1);
    }
    100% {
        transform: scale(1);
    }
}
複製代碼

※注:safari須要將clip-path改成-webkit-clip-path。因爲代碼太佔篇幅,這裏就不重複寫兩遍了。

實現原理就是經過clip-path繪製了一個內六角形遮罩,把黃顏色背景經過遮罩變成了最終的內六角形。

animation經過關鍵幀動畫實現了「放大縮小」交替動效。

最終效果:

4 陰天

觀察圖形發現,有兩個雲朵:前面的白雲和後面的烏雲。貌似須要分別用 :before和 :after實現。若是這樣作的話,後續章節的雨天和雪天的雨雪元素就沒有多餘的僞元素可用了。因此只能用一個僞元素實現兩朵雲。 這裏就用到了box-shadow的「影分身」了!

因爲後續章節的雨天和雪天都複用了雲的樣式,因此寫在一塊兒了,代碼以下:

.cloudy:before,
.rainy:before,
.snowy:before {
    content: "";
    position: absolute;
    top: 50%;
    left: 25%;
    transform: translate(-50%, -50%);
    width: 36px;
    height: 36px;
    background: #fff;
    border-radius: 50%;
    z-index: 2;
}
複製代碼

真實的元素(真身)就是一個圓。經過box-shodow來把投影做爲「分身」。

先來看看box-shadow的屬性:

box-shadow: h-shadow v-shadow blur spread color inset;
參數詳解:
h-shadow: 陰影的水平偏移量。
v-shadow: 陰影的垂直偏移量。
blur: 模糊距離(就是漸變的距離,設爲0就沒有漸變)。
spread: 投影的尺寸,經過這個控制「影分身」的大小。
color: 投影顏色,經過這個實現後方的烏雲。
inset: 改成內陰影。這裏用不到。

先複製一個影分身試試:

box-shadow: #fff 22px -15px 0 6px;
複製代碼

繼續複製多個影分身,帶所有影分身的完整代碼以下:

.cloudy:before,
.rainy:before,
.snowy:before {
    content: "";
    position: absolute;
    top: 50%;
    left: 25%;
    transform: translate(-50%, -50%);
    width: 36px;
    height: 36px;
    background: #fff;
    border-radius: 50%;
    box-shadow: 
    	#fff 22px -15px 0 6px,
    	#fff 57px -6px 0 2px, 
    	#fff 87px 4px 0 -4px,
    	#fff 33px 6px 0 6px,
    	#fff 61px 6px 0 2px,
    	#ccc 29px -23px 0 6px,
    	#ccc 64px -14px 0 2px,
    	#ccc 94px -4px 0 -4px;
    z-index: 2;
}
複製代碼

五個分身的白圓(#fff),三個分身的灰圓(#ccc)拼成了兩朵雲。

再給雲朵加上「上下浮動」的動效:

.cloudy:before {
    animation: cloudMove 2s linear infinite;
}
@keyframes cloudMove {
    0% {
        transform: translate(-50%, -50%);
    }
    50% {
        transform: translate(-50%, -60%);
    }
    100% {
        transform: translate(-50%, -50%);
    }
}

複製代碼

5 雨天

雲朵的代碼直接複用第4章的陰天。這裏使用 :after 僞元素實現雨滴。

先實現一個雨滴(爲方便觀看,暫時隱藏雲朵):

.rainy:after {
	content: "";
    position: absolute;
    top:50%;
    left: 25%;
    width: 4px;
    height: 14px;
    background: #fff;
    border-radius: 2px;
}
複製代碼

而後經過box-shadow「影分身」:

.rainy:after {
	    content: "";
        position: absolute;
        top:50%;
        left: 25%;
        width: 4px;
        height: 14px;
        background: #fff;
        border-radius: 2px;
+ box-shadow:
+ #fff 25px -10px 0,
+ #fff 50px 0 0,
+ #fff 75px -10px 0,
+ #fff 0 25px 0,
+ #fff 25px 15px 0,
+ #fff 50px 25px 0,
+ #fff 75px 15px 0,
+ #fff 0 50px 0,
+ #fff 25px 40px 0,
+ #fff 50px 50px 0,
+ #fff 75px 40px 0;
    }
複製代碼

再加入下雨的移動動效,修改以下:

.rainy:after {
        ...(略)
+ animation: rainDrop 2s linear infinite; 
    }
+ @keyframes rainDrop {
+ 0% {
+ transform: translate(0, 0) rotate(10deg);
+ }
+ 100% {
+ transform: translate(-4px, 24px) rotate(10deg);
+ box-shadow:
+ #fff 25px -10px 0,
+ #fff 50px 0 0,
+ #fff 75px -10px 0,
+ #fff 0 25px 0,
+ #fff 25px 15px 0,
+ #fff 50px 25px 0,
+ #fff 75px 15px 0,
+ rgba(255, 255, 255, 0) 0 50px 0,
+ rgba(255, 255, 255, 0) 25px 40px 0,
+ rgba(255, 255, 255, 0) 50px 50px 0,
+ rgba(255, 255, 255, 0) 75px 40px 0;
+ }
+ }
複製代碼

動畫添加了10度的旋轉,讓雨滴傾斜,以及垂直方向的移動。

這裏的關鍵就是:雖然本質是垂直移動,但爲了看上去是「循環」效果,須要將最下面的雨滴進行透明漸變,同時調節X和Y軸的值,讓最終位置正好跟初始位置重合,就不會顯得「斷開」。

咱們生成的是三行雨滴,第一行被雲朵擋住了,實際能看到的是下面兩行。在第一行移動到第二行位置的時候,原第三行已經透明看不見了,正好與初始狀態同樣,實現了無縫循環拼接。

6 雪天

雪天與雨天的區別就是把雨滴換成圓形,取消旋轉角度。 代碼以下:

.snowy:after {
    content: "";
    position: absolute;
    top:50%;
    left: 25%;
    width: 8px;
    height: 8px;
    background: #fff;
    border-radius: 50%;
    box-shadow:
        #fff 25px -10px 0,
        #fff 50px 0 0,
        #fff 75px -10px 0,
        #fff 0 25px 0,
        #fff 25px 15px 0,
        #fff 50px 25px 0,
        #fff 75px 15px 0,
        #fff 0 50px 0,
        #fff 25px 40px 0,
        #fff 50px 50px 0,
        #fff 75px 40px 0;
    animation: snowDrop 2s linear infinite; 
}
@keyframes snowDrop {
    0% {
        transform: translateY(0);
    }
    100% {
        transform: translateY(25px);
        box-shadow:
        #fff 25px -10px 0,
        #fff 50px 0 0,
        #fff 75px -10px 0,
        #fff 0 25px 0,
        #fff 25px 15px 0,
        #fff 50px 25px 0,
        #fff 75px 15px 0,
        rgba(255, 255, 255, 0) 0 50px 0,
        rgba(255, 255, 255, 0) 25px 40px 0,
        rgba(255, 255, 255, 0) 50px 50px 0,
        rgba(255, 255, 255, 0) 75px 40px 0;
    }
}
複製代碼

7 所有源碼

源碼以下,方便粘貼保存爲html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>單標籤!純CSS實現動態晴陰雨雪</title>
</head>

<body>
    <div class="weather sunny"></div>
    <div class="weather cloudy"></div>
    <div class="weather rainy"></div>
    <div class="weather snowy"></div>
</body>
<style> .weather { position: relative; display: inline-block; width: 180px; height: 240px; background: #23b7e5; border-radius: 8px; } .sunny:before { content: ""; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 60px; height: 60px; background: #F6D963; border-radius: 50%; box-shadow: 0 0 20px #ff0; z-index: 2; } .sunny:after { content: ""; position: absolute; top: 50%; left: 50%; margin: -45px 0 0 -45px; width: 90px; height: 90px; background: #FFEB3B; clip-path: polygon( 50% 0%, 64.43% 25%, 93.3% 25%, 78.87% 50%, 93.3% 75%, 64.43% 75%, 50% 100%, 35.57% 75%, 6.7% 75%, 21.13% 50%, 6.7% 25%, 35.57% 25%); z-index: 1; animation: sunScale 2s linear infinite; } @keyframes sunScale { 0% { transform: scale(1); } 50% { transform: scale(1.1); } 100% { transform: scale(1); } } .cloudy:before, .rainy:before, .snowy:before { content: ""; position: absolute; top: 50%; left: 25%; transform: translate(-50%, -50%); width: 36px; height: 36px; background: #fff; border-radius: 50%; box-shadow: #fff 22px -15px 0 6px, #fff 57px -6px 0 2px, #fff 87px 4px 0 -4px, #fff 33px 6px 0 6px, #fff 61px 6px 0 2px, #ccc 29px -23px 0 6px, #ccc 64px -14px 0 2px, #ccc 94px -4px 0 -4px; z-index: 2; } .cloudy:before { animation: cloudMove 2s linear infinite; } @keyframes cloudMove { 0% { transform: translate(-50%, -50%); } 50% { transform: translate(-50%, -60%); } 100% { transform: translate(-50%, -50%); } } .rainy:after { content: ""; position: absolute; top:50%; left: 25%; width: 4px; height: 14px; background: #fff; border-radius: 2px; box-shadow: #fff 25px -10px 0, #fff 50px 0 0, #fff 75px -10px 0, #fff 0 25px 0, #fff 25px 15px 0, #fff 50px 25px 0, #fff 75px 15px 0, #fff 0 50px 0, #fff 25px 40px 0, #fff 50px 50px 0, #fff 75px 40px 0; animation: rainDrop 2s linear infinite; } @keyframes rainDrop { 0% { transform: translate(0, 0) rotate(10deg); } 100% { transform: translate(-4px, 24px) rotate(10deg); box-shadow: #fff 25px -10px 0, #fff 50px 0 0, #fff 75px -10px 0, #fff 0 25px 0, #fff 25px 15px 0, #fff 50px 25px 0, #fff 75px 15px 0, rgba(255, 255, 255, 0) 0 50px 0, rgba(255, 255, 255, 0) 25px 40px 0, rgba(255, 255, 255, 0) 50px 50px 0, rgba(255, 255, 255, 0) 75px 40px 0; } } .snowy:after { content: ""; position: absolute; top:50%; left: 25%; width: 8px; height: 8px; background: #fff; border-radius: 50%; box-shadow: #fff 25px -10px 0, #fff 50px 0 0, #fff 75px -10px 0, #fff 0 25px 0, #fff 25px 15px 0, #fff 50px 25px 0, #fff 75px 15px 0, #fff 0 50px 0, #fff 25px 40px 0, #fff 50px 50px 0, #fff 75px 40px 0; animation: snowDrop 2s linear infinite; } @keyframes snowDrop { 0% { transform: translateY(0); } 100% { transform: translateY(25px); box-shadow: #fff 25px -10px 0, #fff 50px 0 0, #fff 75px -10px 0, #fff 0 25px 0, #fff 25px 15px 0, #fff 50px 25px 0, #fff 75px 15px 0, rgba(255, 255, 255, 0) 0 50px 0, rgba(255, 255, 255, 0) 25px 40px 0, rgba(255, 255, 255, 0) 50px 50px 0, rgba(255, 255, 255, 0) 75px 40px 0; } } </style>
</html>
複製代碼

以上就是本期CSS高端玩法的分享,歡迎你們轉發交流分享。

更多精彩傳送門:

【漫畫講技術】CSS系列漫畫教程(1-5講)

【漫畫講技術】CSS系列漫畫教程(6-10講)

【漫畫講技術】CSS系列漫畫教程(11-15講)

《詳細教你微信公衆號正文頁SVG交互開發》

歡迎關注個人我的微信公衆號,隨時獲取最新文章^_^

相關文章
相關標籤/搜索