CSS動效集錦,視覺魔法的碰撞與融合(三)
- 2020 年 2 月 18 日
- 筆記
本文講述的原理和相關demo
- 扇形DIV的使用——實現雷達掃描圖
- DIV環形布局—實現loading圈
- 動畫的向量合成—實現拋物線動畫
- 無限滾動動畫—實現跑馬燈效果
- perspective和transform的運用——實現卡片翻轉
話不多說,請看。
扇形DIV的使用——實現雷達掃描圖
在一些殺毒或文件掃描類的軟件上,我們可能會看到一些雷達掃描的UI樣式,例如下圖所示

如果我們要通過CSS該如何去實現話,我們的想法一般是先畫個扇形,然後給它加上漸變。
實現漸變的方式很簡單,但我們該如何實現一個扇形呢?
我們可以通過一些技巧實現這一點,請看:

沒錯,我們可以通過skew函數,將黃色的div傾斜,然後溢出部分通過overflow:hidden遮住就可以了。
- 銳角扇形:deg<0,向右邊傾斜,即可得到銳角扇形
- 鈍角扇形:deg>0, 向左邊傾斜,即可得到鈍角扇形
代碼如下
// CSS代碼 @keyframes rotateAnimate { from { transform: rotate(0deg) skew(-30deg) } to { transform: rotate(360deg) skew(-30deg) } } .fan-wrapper { overflow: hidden; position: relative; margin: 100px; width: 200px; height: 200px; border-radius: 50%; background: red; } .fan { position: absolute; right: 0; animation: rotateAnimate 2s linear infinite; /* 這一行很重要,設置左下角為旋轉點 */ transform-origin: 0% 100%; width: 100px; height: 100px; background: blue; } // HTML代碼 <div class="fan-wrapper"> <div class="fan"></div> </div>
實現效果如下圖所示
(因為篇幅有限,漸變就不加了2333)

DIV環形布局—實現loading圈
loading加載條是常見的一種UI組件,如下圖所示

而要實現它,就需要考慮怎麼把一堆小圓等距地布局在一個「大圓」的邊框上,也就是DIV的環形布局的問題。
當然我們可以通過暴力測量解決,但很麻煩且不優雅,而且如果小圓的數量變化的話要重新測一遍。
我的解決辦法如下:
第一步:根據圓的數量計算相鄰圓和圓心形成的夾角
例如假設我們需要排列8個圓,那麼夾角為360度 / 8 = 45度。圖示如下,每個數字代表以該位置為圓心放一個小圓

第二步:以外部DIV左下角為原點,批量計算小圓圓心的橫縱坐標
批量算出所有圓的相對坐標,我們以編號8的圓為例,假設半徑R和X軸的逆時針夾角為θ,則有以下等式

(cos/sin可能有正負,而等式同樣成立)
第三步,外部div相對定位,內部小圓絕對定位,並且將步驟二中計算的X/Y作為小圓的bottom和left去設置
這一步也是批量完成,下圖以編號8的圓為例

代碼
CSS/HTML代碼如下:
我們在一個父div內部放8個子div。父div相對定位,而子div絕對定位
// CSS代碼 .circles { position: relative; margin: 50px; width: 200px; height: 200px; } .circle { position: absolute; width: 20px; height: 20px; border-radius: 50%; background: black; } // HTML <div class="circles"> <div class="circle circle1"></div> <div class="circle circle2"></div> <div class="circle circle3"></div> <div class="circle circle4"></div> <div class="circle circle5"></div> <div class="circle circle6"></div> <div class="circle circle7"></div> <div class="circle circle8"></div> </div>
JS代碼如下
第一步:編寫calcXYs方法: 以外部DIV左下角為原點,批量計算小圓圓心的橫縱坐標
/** * R:大圓半徑,2*R = 外部正方形的邊長 * r:在大圓邊上等距排列的小圓的半徑 * counts: 圓的數量 * 返回值: * [ * [x1,y1], * [x2,y2], * ... * ] */ function calcXYs(R, r, counts) { // 當前度數 let deg = 0; // 單位度數,兩小圓和圓心的夾角 const pDeg = 360 / counts; // 存放返回結果 const arr = []; for (let i = 0; i < counts; i++) { // 度數以單位度數遞增 deg = pDeg * i; // Math.sin接收的參數以 π 為單位,需要根據360度 = 2π進行轉化 const proportion = Math.PI / 180; // 以外部DIV左下角為原點,計算小圓圓心的橫縱坐標 let Y = R + R * Math.sin(proportion * deg); let X = R + R * Math.cos(proportion * deg); // 存放結果 arr.push([X, Y, deg]); } return arr; }
第二步:編寫resizeCircles方法: 根據上一步的結果:調整絕對定位的小圓的位置
/** * R,r,counts:含義同上 * selector: 獲取所有小圓的標誌符 * 作用:根據上一步的坐標計算結果,調整絕對定位的小圓的位置 */ function resizeCircles(selector, R, r, counts) { // 獲取所有小圓NodeList的選擇器 let list = document.querySelectorAll(selector); //調用calcXYs方法 const XYs = calcXYs(R, r, counts); // 遍歷每個小圓的XY坐標 for (let i = 0; i < list.length; i++) { const [X, Y] = XYs[i]; const e = list[i]; // 修改小圓距離外部DIV底部和左邊的距離 e.style.left = X + "px"; e.style.bottom = Y + "px"; } }
最後我們只需要調用resizeCircles方法就可以啦
resizeCircles(".circle", 60, 20, 8);
實現效果如下

讓loading圖標動起來
好,現在布局完成了,那我們該怎麼去讓這個loading圖標「動起來」呢?
- 給每個圓設置animation實現明暗變化,例如可以設置黑色的背景色然後動態變化opacity
- animation屬性可以設置delay實現動畫延遲播放,我們依次給圓設置等距的delay,例如1s,2s,3s…
- 給animation屬性設置alternate,表示往複播放,設置infinite,表示無限循環播放
@keyframes k { from { opacity: 1; } to { opacity: 0; } } .circle1 { animation: k 1s ease 0s alternate infinite; } .circle2 { animation: k 1s ease 0.2s alternate infinite; } .circle3 { animation: k 1s ease 0.4s alternate infinite; } // circle4 ~ circle8同理,delay以0.2s遞增
Demo

動畫的向量合成—實現拋物線動畫
在餓了么,或者淘寶天貓之類的購物外賣相關的APP里,我們可能會看到類似於下面這種的拋物線的動畫。

如果要實現這種平拋效果,需要一點基礎的高中物理知識。
平拋運動由水平方向的兩種運動合成而得到
- 水平方向: 勻速直線運動
- 垂直方向:初速度為0的勻加速直線運動
如下所示

如果我們通過圖像捕捉的方式就可理解的更清楚了,從下面的圖可以看到:
水平方向的速度是不變的,而垂直方向的速度是不斷加快的

好,下面終於可以講下CSS的實現思路了
CSS實現原理
- 設置兩個div:外層div和內層div
- 外層div設置橫向勻速運動的動畫
- 內層div設置縱向的勻加速直線運動的動畫,加速過程可以用cubic-bezier設置
cubic-bezier又叫做貝塞爾曲線,它可接收四個參數,來規定動畫的速度變化過程,使用方法如下
transition-timing-function: cubic-bezier(0.1, 0.7, 1.0, 0.1);
我們可以通過下面這個官方網站去設置速度變化曲線,然後獲取生成的四個參數

具體代碼如下:
// HTML <div id="outer"> <div id="inner"></div> </div> <button id='btn'>拋物線效果</button> // CSS #outer { transition: all 1.5s linear; } #inner { width: 30px; height: 30px; border-radius: 50%; background: red; transition: all 1.5s cubic-bezier(.54, .11, .95, .68); } .outer-active { transform: translateX(300px); } .inner-active { transform: translateY(300px) scale(0.3); }
JS
document.getElementById("btn").onclick = function() { document.getElementById("outer").classList.add("outer-active"); document.getElementById("inner").classList.add("inner-active"); };
效果如下

無限滾動動畫—實現跑馬燈效果
當文本過長時候,我們可能需要做成跑馬燈效果,然後無限滾動播放。
因為marquee這個HTML元素被廢棄了,所以一般情況下我們需要手動通過動畫去實現跑馬燈

實現圖示如下,注意開始位置和結束位置是不可見的

// HTML <div class="marquee"> <p>ABCDEFGHIJKLMN</p> </div> // CSS @keyframes marquee { from { transform: translateX(-200px) } to { transform: translateX(200px) } } .marquee { overflow: hidden; margin: 100px; width: 200px; } .marquee p { animation: marquee 3s linear infinite; }
結果

perspective和transform的運用——實現卡片翻轉
卡片翻轉三要素
- transform: rotateY(x deg) 翻轉卡片
- backface-visibility:hidden 翻轉後隱藏背面,重要!必須要加
- perspective:增加透視和立體效果
// HTML <div id="img-wrapper"> <img src='./timg.jpg' id='img1' class="img disable-img1" /> <img src='./timg2.jpg' id='img2' class="img" /> </div> // CSS #img-wrapper { perspective: 1200px; position: relative; height: 479px; } #img1, #img2 { position: absolute; transition: all 1s linear; backface-visibility: hidden; } #img1 { transform: rotateY(-180deg); } #img-wrapper:hover #img1 { transform: rotateY(-360deg); } #img-wrapper:hover #img2 { transform: rotateY(-180deg); }
結果
