譯文出自:閃電礦工翻譯組css
原文地址:Drawing Realistic Clouds with SVG and CSShtml
原文做者:Beau Jacksongit
倉庫原文連接:Drawing Realistic Clouds with SVG and CSSgithub
譯者:sichenguo算法
希臘神話中有這樣一個故事是講述宙斯創造出來一個雲女神涅斐勒,而且相似大多數的希臘神話同樣的,這個故事很是的奇異且限制級。下面一個簡短剋制的版本。數組
咱們可以知道的是: 涅斐勒是由宙斯以他本身美麗的妻子的形象創造的。一個凡人碰見涅斐勒,陷入愛河,而且他們一塊兒有了一個孩子,確切的說是一個半人半馬的嬰兒。瀏覽器
很怪誕對吧,值得慶幸的是,在瀏覽器中建立雲的過程要簡單得多,並且風險要小得多。 ide
(Demo)svg
最近,我發現開發者Yuan Chuan 已經實現了用代碼生成逼真的雲。對我來講,瀏覽器中的雲這個概念一直如同希臘神話中的那邊神祕。函數
讓咱們來看一下這個’畫筆‘吧 (點這裏),能夠見到的是做者經過使用 box-shadow
做爲包含兩個濾鏡的 <filter>
元素的補充實現了這個使人驚歎的‘雲圖’!
想要繪製出兼顧寫實和精緻的雲圖,須要搭配使用feTurbulence
和feDisplacementMap
這兩個濾鏡。這兩個濾鏡不只能夠具備強大且複雜的功能,而且還能夠提供一些使人興奮的特性(其中包含奧斯卡獲獎算法))!固然,這些功能在瀏覽器‘引擎蓋’下有着使人生畏的複雜性。
雖然這些 SVG 濾鏡的物理特性超出了本文的範圍,但 MDN 和 w3.org 上提供了大量文檔供學習參考。另外還有 feTurbulence
和 feDisplacement
介紹。
對於本文,咱們將專一於學習使用這些 SVG 濾鏡來實現使人驚奇的效果。濾鏡背後的實現算法並不在咱們的研究範圍內,就像藝術家雖然能夠繪製出美麗的景觀但卻不用懂得油漆的分子結構。
而咱們須要作的只是密切關注一小部分 SVG 屬性,這些屬性使得咱們能夠在瀏覽器中繪製逼真的雲圖。經過學習這些屬性可讓咱們在項目中按照本身的意願更好的製做出特定的濾鏡效果。
CSS 規則 box-shadow
的五個值得關注的屬性:
box-shadow: <offsetX> <offsetY> <blurRadius> <spreadRadius> <color>;
複製代碼
讓咱們來增大這些值(可能會高於正常的開發者會作的),這樣在視圖的右下方就會有陰影出現。 !
( Demo)#cloud-square {
background: turquoise;
box-shadow: 200px 200px 50px 0px #000;
width: 180px;
height: 180px;
}
#cloud-circle {
background: coral;
border-radius: 50%;
box-shadow: 200px 200px 50px 0px #000;
width: 180px;
height: 180px;
}
複製代碼
你確定表演或者見過過皮影戲吧?
Credit: Double-M相似於經過改變手的形狀來改變陰影的方式,咱們的 HTML 中的「源形狀」能夠經過移動或者變形來同步影響其在瀏覽器中呈現的陰影的形狀。box-shadow
複製原始圖形的圓角效果。SVG 濾鏡對於元素都以及元素的 shadow
的都會生效。
<svg width="0" height="0">
<filter id="filter">
<feTurbulence type="fractalNoise" baseFrequency=".01" numOctaves="10" />
<feDisplacementMap in="SourceGraphic" scale="10" />
</filter>
</svg>
複製代碼
到目前爲止,上面的 SVG
標籤不會被渲染出來的,由於咱們沒有定義任何視覺樣式(更不用說零寬度、高度)。它的惟一目的是保持咱們餵養的過濾器 SourceGraphic(也就是咱們的<div>
)。咱們的源<div>
和它的陰影都被濾波器獨立地扭曲。
咱們經過 ID
選擇器(#cloud-circle
) 將下面的 CSS
規則添加到目標元素上:
#cloud-circle {
filter: url(#filter);
box-shadow: 200px 200px 50px 0px #fff;
}
複製代碼
好吧,添加上 SVG 濾鏡依舊沒能給咱們帶來什麼驚喜。
(Demo)別擔憂!這才僅僅只是開始,更有趣的還在後面。
使用這一屬性進行的一些實驗能夠產生顯著的效果。暫時,讓咱們保持它的 feTurbulence
不變,只調整 DisplacementMap
的 scale
屬性。
隨着 scale
的增長(每次增長 30 ),咱們的源 <div>
開始扭曲變形且它的投下陰影更接近天空中雲隨機的形狀。
<feDisplacementMap in="SourceGraphic" scale="180" />
複製代碼
The scale attribute incremented by values of 30. (
Demo)
好的,經過改變 scale
,好像終於接近了正確雲形狀的的數值範圍!讓咱們稍微改變下顏色,以便看起來更像一朵 雲彩 ☁️。
body {
background: linear-gradient(165deg, #527785 0%, #7fb4c7 100%);
}
#cloud-circle {
width: 180px;
height: 180px;
background: #000;
border-radius: 50%;
filter: url(#filter);
box-shadow: 200px 200px 50px 0px #fff;
}
複製代碼
如今咱們的圖形愈來愈接近真實的雲效果了!
下面的一組圖像顯示了 blur 對 box-shadow 效果的影響。下面的圖形中 blur 的數組依次遞增。
The cloud becomes "softer" as the blur value increases.爲了使咱們的雲帶有一些積雲效果,能夠稍微擴大 <div>
的尺寸。
#cloud-circle {
width: 500px;
height: 275px;
background: #000;
border-radius: 50%;
filter: url(#filter);
box-shadow: 200px 200px 60px 0px #fff;
}
複製代碼
好的,可是如今的源
div
元素正在成爲障礙。😫
等等!咱們已經擴大了源元素的尺寸,可是它如今已經開始影響咱們的「雲(白色陰影)」了。 很簡單,只須要相距更遠的地方投影遍能夠解決這個遮擋問題。
而經過經過一些 CSS 定位很好地實現源元素的隱層。<body>
元素是雲 div
元素的父元素,默認狀況下是靜態定位的。 將蘇設置爲就絕對定位就能夠將不須要展現的 #cloud-circle
隱藏。
#cloud-circle {
width: 500px;
height: 275px;
background: #000;
border-radius: 50%;
filter: url(#filter);
box-shadow: 400px 400px 60px 0px #fff; /* Increase shadow offset */
position: absolute; /* Take the parent out of the document flow */
top: -320px; /* Move a little down */
left: -320px; /* Move a little right */
}
複製代碼
很棒,這樣看起來咱們的雲圖已經初步成型了。
平攤在屏幕之上的雲彩,嗯,這應該是一個對咱們繪製出的雲圖一個很好的描述,老是感受還缺乏點什麼,咱們應該還能夠作的會更好的!
這就是咱們想要的:
Credit: 圖源:pcdazero從這張照片中雲的深度,質感和豐富程度來看,有一件事是清楚的:宙斯是上過藝術學校。至少,他確定是閱讀過通用組件設計原則的,這個原則闡述了一個通用性的概念:
[...]照明誤差在深度和天然的解釋中起着重要做用,而且能夠由設計師以各類方式操縱......使用明暗區域之間的對比度使得外觀具備景深。
這段話爲咱們提供了一個對雲圖進行優化更加真實的提示。經過將不一樣形狀,大小和顏色的圖層堆疊在一塊兒,咱們能夠在參考圖像製造對比度中以來渲染雲圖。須要的只是使用 filter
製造多個圖層。
<svg width="0" height="0">
<!-- Back Layer -->
<filter id="filter-back">
<feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" />
<feDisplacementMap in="SourceGraphic" scale="170" />
</filter>
<!-- Middle Layer -->
<filter id="filter-mid">
<feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" />
<feDisplacementMap in="SourceGraphic" scale="150" />
</filter>
<!-- Front Layer -->
<filter id="filter-front">
<feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" />
<feDisplacementMap in="SourceGraphic" scale="100" />
</filter>
</svg>
複製代碼
再加上圖層能夠提供更多的角度去探索 feTurbulence
以及 實現更多的功能。 咱們選擇使用更加平滑的 type
:fractalNoise
,而後將 numOctaves
設置爲 6。
<feTurbulence type="fractalNoise" baseFrequency="n" numOctaves="6" />
複製代碼
如今,讓咱們將注意力集中在 baseFrequency
屬性上。如下是咱們在逐漸增長 baseFrequency
值的時獲得的結果 :
像 turbulence
,噪音,頻率和 octave
這樣的詞可能看起來很奇怪甚至使人困惑。但不要懼怕!將濾鏡的效果類比爲聲波實際上很是準確。咱們能夠將低頻率(baseFrequency
=0.001)等同於低噪聲和低頻率,更高的 (baseFrequency
=0.1) 值則與更清晰的音調相對應。
咱們能夠看到,咱們對積雲效果的對應的 baseFrequency
值大概位於 0.005~0.01 範圍之間。
增大 numOctaves
的值能夠以極其細緻的細節渲染圖像。可是這須要大量計算,所以須要注意的是:更細緻的渲染也可能帶來的還有性能問題,因此如非必要,請將此值儘可能控制在必定範圍內。
好消息就是,在此例中,咱們的雲圖須要的細節並不須要使用太高的 numOctaves
值,如上圖所示,numOctaves
爲 4 或者 5 就能夠獲得不錯的效果。
至於 seed
屬性還能夠再深刻研究一下。可是,就咱們的目的而言, seed
的做用能夠對形狀形成影響。
Perlin Noise 函數(上文提到
)會使用此值做爲其隨機數生成器的初始值。若是不設置該屬性將默認 seed
爲零。跟 baseFrequency 的區別就是,不管咱們給出什麼價值 seed
,都不須要擔憂性能損失。
不一樣的 seed 值產生不一樣的形狀
上面的 GIF 展現了不一樣 seed
值時的效果。須要注意的是,每朵雲都是分層的複合雲。(雖然我調整了每一個圖層的屬性,但我保持各自的 seed
值分佈均勻。)
在這裏,仔細觀察參考圖像,我將 3 個‘雲’層(不透明度不一樣)疊加到一個 div
上。經過反覆試驗和調整 seed
參數值,最終獲得的下面這個相似於照片中雲的形狀的效果圖。
固然,若是咱們認爲咱們繪製的這個雲彩要比宙斯創造的女神還要漂亮,確定就是狂妄自大了。
可是,隨着咱們對 CSS
和 SVG
濾鏡的更多的瞭解,咱們就越有能力創造出在視覺上使人驚歎的圖形,好比下面這些:
在這篇文章中咱們沉浸在 SVG
強大功能和複雜性的海洋中,雖然 SVG
濾鏡一般看起來比較複雜且難以理解。
然而就好像A Single Div project這個項目和Diana Smith's painting techniques這個例子同樣,有趣的和有創意的方法老是能夠獲得使人驚豔的效果。
我相信不少開發者都有能力製做出一個雲彩出來,可是一個能夠輕鬆製做出雲彩的工具會更受歡迎。所以我開發了一個小工具作這件事。