【譯】來,用 SVG & CSS 給你畫一朵真實的雲

原文:Drawing Realistic Clouds with SVG and CSScss

這是做者最終實現的效果: html

......哦,不,應該是這張:
在線查看效果:cloud demo


正文開始啦~git

希臘神話講述了一個關於宙斯創造雲女神涅斐勒的故事。和其餘希臘神話同樣,這個故事極其怪異而且有點限制級。下面的表述則是一個比較簡短且含蓄的版本(限制級,非戰鬥人員請撤離)。github

涅斐勒(雲神),聽說是宙斯按照本身美麗妻子的形象創造的。傳說有個凡人碰見了涅斐勒,一見傾心愛上了她而且在一塊兒了,後來她們一塊兒睡了一個覺,而後奇怪的事情發生了,一朵雲生下了一個半人半馬的小孩,傳說這就半人馬的祖先。web

很難以想象對嗎?就我我的而言,我搞不懂。但慶幸的是,瀏覽器中建立雲的過程要簡單得多,也沒有那麼不可描述。算法

這是@袁川畫的雲demo

最近,我發現開發者 @袁川 已經用代碼實現了仿真的雲煙。對我來講,在瀏覽器中實現一直是個神話。瀏覽器

經過簡單掃一下這個demo裏面的代碼,咱們能夠現象,逼真而又獨特的雲朵是能夠經過使用cssbox-shadow和一個包含兩個元素的SVG過濾器<filter>去實現。svg

咱們想要的仿真效果是經過feTurbulencefeDisplacementMap之間的微妙混合來實現的。SVG過濾器功能強大、複雜,而且提供了使人很是興奮的功能(還包括奧斯卡獲獎算法)。然而,在它的底層,它們的複雜性可能有點嚇人!函數

譯者:奧斯卡獲獎算法,這麼牛逼?無知限制了個人想象。性能

雖然SVG的物理特性超出了本文的範疇,可是在MDNw3.org上有大量的文檔。一個免費的關於feTurbulencefeDisplacement很是有用的頁面(同時被做爲這本驚奇的書的一個章節)

對於本文,咱們專一學習如何使用SVG的過濾器實現驚人的效果。咱們不須要深刻研究其底層的算法,正如藝術家不須要了解塗漆的分子結構也能繪出使人驚歎的風景同樣。

先從一些基礎開始

CSS的box-shadow屬性有五個值須要搞懂:

box-shadow: <offsetX> <offsetY> <blurRadius> <spreadRadius> <color>;
複製代碼

讓咱們把這些值調高(可能比任何理智的開發人員調得都要高):

#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;
}
複製代碼

會獲得下面的效果圖:

你曾經應該也玩過或者看過影子木偶對嗎?像下面這樣:

譯者:看見這個,想起小時候本身也玩過, 兩個拇指叉在一塊能擺出鴿子的投影。

就像一隻手改變形狀能夠改變投影同樣,咱們改變HTML的「源形狀」也可使渲染在瀏覽器中的投影變形。box-shadow複製了原始尺寸和border-radius上的「漸變」特性,SVG過濾器則同時應用於元素及其陰影。

<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>)提供的過濾器。

咱們藉助SVG過濾器的ID,經過添加CSS規則將HTML元素(#cloud-circle)和SVG過濾器進行關聯:

#cloud-circle {
  filter: url(#filter);
  box-shadow: 200px 200px 50px 0px #fff;
}
複製代碼

嗯!很棒,就是這樣

在線查看demo

別急!咱們只是瞭解了皮毛,還有不少好東西要看。

嘗試使用feDisplacementMap的scale屬性

使用這一屬性進行一些非科學試驗能夠產生顯著的效果。如今,咱們保持feTurbulence的值不變,簡單調整feDisplacementMapscale屬性值。

隨着scale的增長(以30爲增量),咱們的源<div>變得扭曲,投射的陰影反應出天空中雲出現的隨機形式。

<feDisplacementMap in="SourceGraphic" scale="180"/>
複製代碼

在線查看demo

好了,咱們有進展了!讓咱們稍微改變顏色,以造成更具說服力的雲。

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;
}
複製代碼

如今,咱們愈來愈接近真實的雲朵效果了!

修改box-shadow的模糊度

下面一套圖片展現了box-shadow屬性的模糊度做用的效果,這裏,咱們以10px遞增模糊值:

隨着模糊值增長,雲朵也變得更加柔和。

爲了增長一點積雲的效果,咱們能夠稍微擴寬源<div>的寬度:

#cloud-circle {
  width: 500px; 
  height: 275px;
  background: #000;
  border-radius: 50%;
  filter: url(#filter);
  box-shadow: 200px 200px 60px 0px #fff;
}
複製代碼

很好,咱們的源元素開始礙事了😫

等等,咱們擴寬了源元素的寬度,但它如今遮擋在咱們雲層(白色陰影)的上方。讓咱們在更遠的位置從新投影,這樣咱們的雲就不會再被源圖像遮擋了(你能夠想象成把你的手往遠離牆的方向移動,這樣它就不會擋住你的影子木偶的視線了)。

這點咱們經過CSS定位能夠很好地實現。<body>是父元素,默認是靜態定位的,咱們給源<div>添加絕對定位。最初地,這也會從新定位咱們的陰影,所以咱們還須要增長陰影和元素之間的距離。

#cloud-circle {
  width: 500px;
  height: 275px;
  background: #000;
  border-radius: 50%;
  filter: url(#filter);
  box-shadow: 400px 400px 60px 0px #fff; /* 增長投影位移 */
  position: absolute;
  top: -320px;
  left: -320px;
}
複製代碼

是的,咱們已經實現了一個極具說服力的雲:這裏查看

瀏覽器上展現的已是一個至關完美的雲了,但我不肯定......這片雲真的能公正地描述雲女神涅斐勒嗎?我相信咱們還能夠作得更好!

經過層次傳達深度

這是咱們想要的效果:

從這張照片中雲層的深度、紋理和豐富性來看,宙斯必定是讀過藝術專業的。至少,他必定讀過《通用設計法則》,這本書闡述了一個強大而又普通的概念:

照明誤差在深度和天然度的解釋中起着重要做用,設計師能夠經過多種方式操縱照明誤差,利用明暗區域之間的對比度來改變深度的外觀。

這段話給了咱們一個提示,咱們能夠將不一樣形狀、大小和顏色的圖層堆疊在一塊兒,能夠實現像參考圖片中那樣具備高保真度的雲。咱們要作的也只是屢次調用SVG過濾器。

使用三個SVG過濾器繪製前中後三朵雲:

<svg width="0" height="0">
    <!-- 後層 -->
    <filter id="filter-back">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" />
      <feDisplacementMap in="SourceGraphic" scale="170" />
    </filter>
    <!-- 中層 -->
    <filter id="filter-mid">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" />
      <feDisplacementMap in="SourceGraphic" scale="150" />
    </filter>
    <!-- 前層 -->
    <filter id="filter-front">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" />
      <feDisplacementMap in="SourceGraphic" scale="100" />
    </filter>
</svg>
複製代碼

經過分層的應用,咱們有機會去探索feTurbulence並認識它的多樣性。咱們選擇了較爲平滑的類型:fractalNoise,對於numOctaves的值最高只調到了6。

上面這些意味着什麼?咱們來看一下baseFrequency這個屬性,下面幾張圖片是不一樣baseFrequency值下的效果:

值越低,圖像就越圓,越模糊。

從效果看,介於0.005~0.01的值比較符合咱們想要的積雲效果。

用numOctaves添加細節

增長numOctaves值容許咱們以更細的粒度去渲染圖像,這個過程須要大量的計算,所以須要注意:高值會嚴重影響性能。

numOctaves的值設置得越高,雲的效果就越精細。

幸運的是咱們不須要爲達到精細的效果而設置過高的值,介於4~5就夠了。

最後的效果

查看效果

用seed屬性進行無限變形

關於seed屬性有不少能夠說,但就咱們的目的而言,seed的做用能夠歸結爲:不一樣的值,不一樣的形狀

柏林噪音函數使用這個值做爲其隨機數生成器的起點。選擇不包含此屬性則將seed值默認爲0;當包含時,不管咱們設置何值,都不須要擔憂會影響性能。

不一樣的seed值,對應生成不一樣的形狀。

上面的GIF表明了seed做用的效果。請記住,每一個雲都是分層的複合雲(雖然我調整了每一層的屬性,但我保持了它們的seed值一致)。

仔細觀察上面這張參考圖片,我將3個雲層<div>堆砌在一個基礎的div上,經過反覆試驗不一樣的seed值,最終獲得了與圖中比較類似的形狀。以下圖:

在線查看demo

天空的極限

顯然,認爲咱們用<div>在瀏覽器上繪製的雲比涅斐勒高級是很荒謬的。

可是,咱們可以梳理出CSS和SVG過濾器的神祕感越多,咱們就越有能力去創造在視覺上使人驚歎的東西,而且高度保真於雷神的創做。那麼,咱們能夠作進一步的試驗了!

在這篇文章中,咱們剛剛涉足了一個充滿力量和複雜性的知識海洋,SVG過濾器一般看起來複雜地難以理解。

不過,就像A Single Div ProjectSmith's繪畫技術中的例子同樣,有趣和實驗性的方法總會給人驚豔的效果。

我但願這篇文章能讓你對web上進行攝影寫實開發感到興奮,歡迎下方評論交流你的想法~

相關文章
相關標籤/搜索