⚠️本文爲掘金社區首發簽約文章,未獲受權禁止轉載javascript
有一天開會,產品經理問:你們都升鴻蒙系統了麼?緊接着一羣人答:咱們都用iPhone…
固然哈,我本身用的是安卓,不過也不是華爲(留下了沒錢的淚水
)…css
聽了他這麼一問我還覺得這是要讓咱們開發鴻蒙應用了,還好我作過功課,有提早了解過它的語法,感受跟小程序有些相似,這下前端又要開始捲了… 之後面試的時候估計還要問鴻蒙應用的知識…html
就在這時產品從他的口袋裏掏出了一臺搭載了鴻蒙操做系統的華爲mate40
:來讓大家看看個人開機動畫:前端
我但願這個字母〇
的動畫可以移植到我們的網站上面去,由於我們的好多產品字母裏一樣也有個〇
!vue
害!原來不是要開發鴻蒙應用呀!java
既然需求明確了下來,那麼咱們就要開始對這個動畫進行分析了。第一次看的時候感受挺驚豔的,由於感受就像是一輪空心的明月在水面上升起,水面倒映着月影,讓我想起了唐朝著名浪漫主義詩人李白的那句:舉杯邀明月,對影成三人
:web
前半秒我竟然沒看出來這究竟是個什麼,等這個空心的滿月所有升起的時候我才知道原來這是一個字母:〇
面試
既然是倒映,那麼就證實上下的顯示是一致的,只不過是倒了過來,那麼咱們有(包括但不只限於
)以下幾種選擇:vue-router
-webkit-box-reflect
(專門作倒影或側影的一個屬性)(火狐和IE不支持
)-moz-element()
(能夠將DOM的某部分看成圖片渲染)(只有火狐支持
)transform: rotate(180deg);
把它給倒過來想了一下雖然最後那個最麻煩,但最合適的仍是它,不只僅由於它的兼容性最好,而是由於仔細觀察了一下鴻蒙的開場動畫,那個倒影是有必定的模糊程度的。-webkit-box-reflect
只能控制方向及透明度或漸變透明度,但沒法添加模糊效果。-moz-element()
雖然很是強大,但只有火狐支持那是確定不行的,要知道目前火狐在瀏覽器市場的佔比已經很是低了,因此只好用不那麼優雅的第三個方式了,首先咱們須要繪製一個半圓形的圓環,你能想到幾種方式?小程序
第一種:
一大一小兩個半圓,小半圓的背景色保持與頁面背景色一致的顏色,而後蓋在大半圓上(就像日環食那樣
),這樣看起來就像是個圓環啦(原理示意圖
):
第二種:
先寫出來個半圓,不給加背景色,只給加邊框,最後把下邊框去掉
,因而看起來就是個半圓環啦(原理示意圖
):
第三種:
直接寫個圓,而後寫上邊框,圓環外套個容器,外層容器高度爲圓的一半,最後overflow: hidden;
隱藏掉露在外面那半部分(原理示意圖
):
第四種:
把第三種的overflow: hidden;
換成clip-path
(原理示意圖
):
第五種:
直接用SVG
或Canvas
來進行繪製(原理示意圖
):
最終仍是選擇了overflow: hidden;
,由於用它來作圓環升起的效果很合適,把露在外面的那部分圓隱藏掉,而後控制圓的位置,看起來就像是一輪空心的明月從海面上升起來了同樣(原理示意圖
):
接下來再把兩個半圓環拼接到一塊兒去就能夠了(原理示意圖
):
去掉爲了向你們展現原理的那些雜七雜八的動畫以後,顯示出來的最終效果以下:
是否是有那麼一點點神似了呢?不過在細節上跟鴻蒙的那個開場動畫比起來仍是差了許多,比方說後來我又找到了一版鴻蒙開場動畫,若是跟之前的動畫比起來的話,如今這版本在細節上的處理就更加的遊刃有餘了:
首先咱們能夠看到這個〇
有一個外發光的效果,在黑色背景的陪襯下顯得格外明亮,鴻蒙初版的那個動畫其實也有外發光,你們能夠翻上去仔細對比一下,那一版的外發光沒有這一版明顯,並且細節處理的也沒有這版好。那版是在〇
所有顯現以後馬上消除掉外發光,有些略顯生硬。而仔細看這版的話能夠發現外發光是在不知不覺的過程當中消失的。CSS的外發光效果其實很好作,就是在黑色背景下用box-shadow
給元素添加一個適當模糊的白色陰影,而後求陰影部分面積:
此時溢出隱藏(overflow: hidden;
)這個方案的缺點就會被暴露出來,因爲咱們的陰影部分面積
在上下左右四個方向已經超出了外面盒子的寬高,因此被隱藏掉了,咱們只好爲外面的盒子加入內邊距padding
來解決掉這個缺陷:
咱們也把咱們的〇
變白變粗,但仔細看又會發現新的問題:那就是box-shadow
默認只會在元素的外部添加陰影,咱們〇
這個圓圈的內部卻沒有陰影,好在box-shadow
是支持多重陰影和內陰影的:
並且這種效果用filter: drop-shadow();
也一樣能夠實現,不過因爲在谷歌內核的瀏覽器中,filter: drop-shadow();
在動態變化的元素上渲染效果並不如box-shadow
那樣理想:
因此咱們決定仍是採用box-shadow
內外雙陰影的方案,如今看起來已經不錯了,但仍是少了點什麼,少的就是圓環倒映在水面上的模糊效果。要知道在平常生活中,倒映在水面上的圖案一般會比真正的視圖稍稍模糊一點:
這是由於水面其實並非一個徹底平整的平面,哪怕再小的風也會致使水面上產生必定的水波:
正是這些水波致使了倒映在水面上的圖案會產生必定的模糊度,水波的波紋越細,模糊程度就會越精緻。正如上面那張圖同樣,水波的波紋不夠細,就會致使咱們就可以看到水波的紋理,就像鴻蒙的效果圖那樣:
他這水波的紋理搞得跟指紋同樣… 若是要我們寫出這樣的一個濾鏡的話仍是很是困難的,但好在鴻蒙的開場動畫並無可以看到水波的紋理,因此我們就能夠用模糊效果(filter: blur(2px);
)來寫:
這個效果跟鴻蒙的開場效果比起來差距可就不是一星半點了,因此說鴻蒙那個動畫雖然看起來簡單,好像就是一個圓環從水面上升起來的效果,但實際上蘊含的細節只有親自動手試一遍纔會知道。
咱們來把我們作的圓環升起時的效果和鴻蒙圓環升起時的效果截張圖放在一塊兒對比一下:
發現沒有?我們用的CSS模糊,模糊方向是上下左右東南西北等各個方向的,而鴻蒙的模糊方向是沿着Y軸
也就是上下方向的模糊,若是仍是看着不太明顯的話那我們再來截一張圖看看:
這個是圓環未徹底升起時的效果,這回應該能比較明顯的看出來,水面下的圓環越靠下模糊程度就越高,而且它的模糊主要是沿着上下兩個方向來進行模糊的。並且在向下方向的模糊程度要比在向上方向時的模糊程度要高上許多,這樣看起來就會比較真實,才能給用戶一種在水面上升起的錯覺。
若是不分青紅皁白的按照各個方向一頓模糊的話,那麼圓環看起來的效果就怎麼也不像是在水面上的感受了:
對於這種帶着方向帶着漸變帶着不一樣程度的模糊效果,咱們就不能期望CSS了。這種場景下須要用到的是更爲底層也更加複雜的SVG濾鏡
!
其實好多CSS屬性都是從
SVG
那裏得到的靈感,好比說咱們較爲經常使用的pointer-events
、filter
等,還有一些不經常使用的clip-path
、mask
等…
因爲CSS提供給咱們的模糊只能各個方向都模糊,而在目前這種狀況下咱們須要的是沿着Y軸模糊
,那麼SVG
的代碼就能夠寫成這樣:
<svg>
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="0 5"/>
</filter>
</svg>
複製代碼
看不懂不要緊啊,你們只須要記住stdDeviation
這個屬性是控制模糊的就能夠了,若是隻給一個數字的話,就至關於全方位模糊,跟CSS的filter: blur();
效果是差很少的。但若是給了兩個數字,那麼第一個數字就表明X軸
模糊程度,第二個數字就表明Y軸
模糊程度。在這裏咱們讓X軸的模糊程度爲0
、Y軸的模糊程度爲5
,注意不要像寫CSS的時候給加單位(px
),這裏只寫數字就行了,不要帶單位。
因爲CSS的濾鏡屬性
filter
原本就是從SVG
那邊吸取過來的,因此在CSS中可使用SVG濾鏡
!用法以下:
filter: url(#blur);
複製代碼
咱們以前不是在SVG的<filter>
標籤上加了一個id
屬性麼,這個id
就能夠寫在CSS濾鏡
的url
裏。但也不知是谷歌瀏覽器的filter
在動態變化的元素上渲染很差仍是怎麼着,總之在Chrome
瀏覽器裏顯示效果是這樣的:
而在Safari
瀏覽器裏是這樣的:
在火狐
瀏覽器裏效果最爲完美:
那這可不行啊,谷歌瀏覽器但是市場佔有率最高的瀏覽器了,產品那邊確定通不過的!不過也不是沒辦法解決啦。在谷歌瀏覽器那顯示的問題不就是一開始會有個縫嘛!那我們就margin-top: -2px;
來讓這兩個半圓先負距離接觸
,對於它倆來講也不用-18px
,-2px
就夠啦:
最後一步,就是把下半圓的模糊效果去掉,讓它真正的變成一個字母〇
:
沒想到用了SVG
的CSS filter
竟然沒有任何的過渡效果,那就只好用requestAnimationFrame
來動態改變SVG
裏的<feGaussianBlur>
上的stdDeviation
屬性啦:
把這個效果拿給產品經理看,他很滿意並對此讚不絕口。說看看我們哪一個產品名字裏帶〇
的,全給換上這個動畫!
但實際上吧,我以爲這個動畫在不少細節的處理上跟鴻蒙的開機動畫仍是有差距。不得不佩服開發鴻蒙的工程師團隊,就在這轉瞬即逝的一兩秒裏竟然能蘊含那麼多小細節。你們能夠找一找細節上的差距,有空的話我們再優化一下。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>公衆號:前端學不動</title>
<style> * { padding: 0; margin: 0; } html, body { height: 100% } body { background: black; display: flex; align-items: center; justify-content: center } .ul { position: relative; width: 100px; height: 50px; padding: 10px; list-style: none; overflow: hidden } .ul:first-of-type { padding-bottom: 0 } .ul:last-of-type { padding-top: 0; /* margin-top: -2px; */ /* animation: container-move .1s 1.2s forwards */ } .harmony { position: absolute; top: 10px; left: 10px; width: 70px; height: 70px; border: 15px solid white; border-radius: 50%; transform: translateY(50%); box-shadow: 0 0 6px white, inset 0 0 6px white; animation: move 1.2s forwards } .ul:last-of-type > .harmony { top: auto; bottom: 10px; transform: translateY(-50%); filter: url(#blur) } svg { width: 0; height: 0 } @keyframes move { to { transform: none } } /* @keyframes container-move { to { margin-top: 0 } } */ </style>
</head>
<body>
<div class="container">
<ul class="ul">
<li class="harmony"></li>
</ul>
<ul class="ul">
<li class="harmony"></li>
</ul>
</div>
<svg>
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="0 6"/>
</filter>
</svg>
<script> const filter = document.querySelector('feGaussianBlur') const clearFilter = () => { const value = parseFloat(filter.getAttribute('stdDeviation').split(' ')[1]) - 0.06 if (value > 0) { filter.setAttribute('stdDeviation', `0 ${value}`) requestAnimationFrame(clearFilter) } else { return } } setTimeout(clearFilter, 1200) </script>
</body>
</html>
複製代碼
註釋的那部分代碼就是爲了解決谷歌瀏覽器有縫隙的代碼,能夠解開註釋對比一下在谷歌瀏覽器裏的效果。不解開註釋的話拿火狐瀏覽器打開效果是最好的
。最重要的一點是,咱們能夠經過修改代碼裏的數字來改變這個動畫的效果:
你們以爲這仨哪一個更好看呢?固然若是像鴻蒙那樣做爲開機動畫來講,確定是越快越好。由於這個動畫可能也就前兩次看着能有點新鮮感。但每次開機都看這麼個動畫,很快就會審美疲勞了,用戶只但願可以快點開機少整點那些花裏胡哨的。
不過若是拋開這些應用場景的話,你們以爲是第一張那樣讓模糊慢慢消失好看,仍是最後那張一邊升起一邊就把模糊度給擦除掉了好看呢?