🌟 CSS 幻術 | 有關光影效果的黑魔法

前言

總聽人說光和影是孿生兄弟,有光就有影。其實否則,若是沒有光線強弱的對比,也就不會有陰影的存在。咱們老是依靠物體表面的反光來感知世界,假設全部物體都能徹底吸取光線,那世界將變得黑漆漆一片。css

所謂只有理解光,才能駕馭陰影。好的設計師每每都是用光高手,能經過複雜的光影向讀者傳達出物體的質感、空間感以及層次感texture。他們畫出來的設計稿都是漂漂亮亮的,這可苦了廣大前端同胞!在瀏覽器中,咱們只能用寥寥幾個 CSS 屬性,束手束腳地同時還千方百計地還原設計稿。畢竟,相比咱們用的 CSS 手槍,設計師們用的 AE、C4D 看起來就像大炮同樣!html

常見光影效果

好在咱們能夠暫且假以性能爲由,繼續正大光明地怎麼簡單怎麼來(汗)。就像一提到光影效果,你們第一反應確定是操起 box-shadow、text-shaodw、drop-shadow 三件套直接開畫。就大部分場景來講,這些屬性還挺好用的,可用它實現多種效果,好比單側投影、空心投影和投影動畫。若涉及到彩色陰影、長投影或是倒影,就須要結合其它 CSS 屬性打輔助了。前端

彩色投影,可讓僞元素繼承父元素的背景,再加模糊濾鏡便可。這個思路也能夠用來製做毛玻璃效果frosted-glassgit

彩色投影

.avator {
  position: relative;
  background: 'xxx';
}
.avator::after {
  content: "";
  position: absolute;
  top: 10%;
  width: 100%;
  height: 100%;
  /* 僞元素繼承父元素背景 */
  background: inherit;
  /* 再加一些稀奇古怪的濾鏡,調一調參數 */
  filter: blur(10px) brightness(80%) opacity(.8);
  z-index: -1;
}
複製代碼

製做倒影可使用 -webkit-box-reflect 屬性box-reflect。兼容性還不錯,除了火狐和 IE,其他瀏覽器都能用。另外一種方法則是用僞元素將父元素複製一份,再 transform 倒轉一下位置。github

https://codepen.io/TheDutchCoder/pen/IKqpA

陰影的另外一面是高光。畫高光的思路能夠直接套畫陰影的思路,只不過須要將投影的顏色改成半透明白色。web

https://codepen.io/Lionad/pen/rNWMVGb

另外一種方法是用背景漸變或僞元素模擬高光。若再配合 CSS 動畫,能夠輕鬆實現掃光等效果。瀏覽器

掃光動畫

body:before {
  content: "";
  position: absolute;
  top: 0;
  width: 200vw;
  height: 35px;
  background-color: rgba(255, 255, 255, 0.4);
  transform: rotate(45deg);
  animation: scan-light 2s ease-in infinite;
}
@keyframes scan-light {
  from {
    right: -100vw;
  }
  to {
    right: 40vw;
  }
}
複製代碼

進階光影效果

紋理

以上提到的幾種繪製光影的方法,主要用來傳達物體的形狀及位置。比方說,倒影和漸變高光能夠用來傳達物體的質感,展現物體光滑到足以發生鏡面反射的表面。固然,這是理想狀況。現實中的物體不多有平滑的表面,就算肉眼可見的光滑表面,微觀上而言也是坑坑窪窪不堪入目。markdown

物體的微觀表面

一旦咱們開始使用 CSS 去模擬高級光影,首先碰到的難題就是如何處理磨砂表面。如下介紹一種簡單實現磨砂表面的思路,在尋常場景用用仍是闊以的。框架

物體表面是什麼樣,咱們就給它貼什麼樣的圖片,這種方法叫作材質貼圖。咱們用一個簡單的材質貼圖爲例,先用 PS 弄一張純色的背景,而後分別隨機填充一些稍微亮一點高光和稍微暗一點的像素點linegradient-material,就能得到相似下圖結果。svg

材質圖片

咱們再把這張材質平鋪爲文檔背景,就能夠獲得相似磨砂金屬般的表面紋理(因爲圖片壓縮的緣由,效果可能很差,可直接前往Codepen查看細節)。

https://codepen.io/Lionad/pen/mdWWxdg

不一樣的高光和陰影細節會給人不一樣的感覺,比方說這裏有一張雪花電視效果圖,其高光像素和陰影像素的對比度要比磨砂金屬表面的大得多。

https://codepen.io/joeyhoer/pen/CojIk

使用圖片的不便之處在於沒有辦法邊調整細節邊預覽,而且 PS 可能超出前端的技術棧範圍了。好在咱們還有 SVG 這個神器why-svg。如下是一套「標準的」材質生成代碼,能夠用來生成很是多種類的材質。

<svg width="0" height="0">
  <filter id="surface">
    <feTurbulence type="fractalNoise" baseFrequency='0.03 0.06' numOctaves="30" />
    <feDiffuseLighting lighting-color='#ffe8d5' surfaceScale='2'>
      <feDistantLight elevation='10' />
    </feDiffuseLighting>
  </filter>
</svg>
<style> body { margin: 0; width: 100vw; height: 100vh; overflow: hidden; filter: url(#surface); } </style>
複製代碼

其中,feDiffuseLighting 是一種噪音濾鏡,能夠建立出隨機的材質圖片。feDiffuseLighting 是光源濾鏡。feDistantLight 指示用平行光做爲光源。

光源?弄個材質還要這麼複雜嘛?

先別急,光源其實也就幾種:點狀光、平行光、聚光。能夠簡單理解成電燈泡、太陽以及戲劇燈。因爲咱們還將在表面材質的話題中停留一下子,暫且只須要用到平行光。

https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/feDiffuseLighting

大體瞭解了上面那段 SVG 是什麼意思,咱們就開始愉快地調參數啦。

先試試下降燈光到表面的距離(減少 elevation)以增長高光面和陰影面的對比度。得到了如下看起來像是某種土壤的紋理

土壤紋理

接下來拉高燈光,調整光照顏色(lighting-color),再把紋理弄粗糙一些(減少 baseFrequency),得到了相似大理石的紋理(也許有點像白色的牛皮紙)。

大理石紋理

增長 baseFrequency、調整表面基準高度 surfaceScale 得到平滑紋理,再調整燈光高度下降高光和陰影的對比度,獲得了白石灰牆壁紋理

白石灰牆壁

<svg width="0" height="0">
  <filter id="surface">
    <feTurbulence type="fractalNoise" baseFrequency='.95' numOctaves="80" result='noise' />
    <feDiffuseLighting in='noise' lighting-color='#fff' surfaceScale='1.4' result="grind">
      <feDistantLight azimuth='500' elevation='50' />
    </feDiffuseLighting>
    <feGaussianBlur in="grind" stdDeviation=".6"/>
  </filter>
</svg>
複製代碼

各位應該發現了白石灰牆壁的代碼相比「標準模板」增長了一個高斯模糊濾鏡(feGaussianBlur)。原則上濾鏡無限疊加,能夠作出很是多好玩的效果。比方說,有大佬用 SVG 畫雲朵。對,你沒聽錯。如下是用 SVG 畫的雲朵紋理cloud

雲朵材質

菲涅爾效應

說完了粗糙的表面怎麼畫,咱們再看看畫光滑表面又有哪些原則。提及水和金屬這種有相對光滑的表面的材質,不得不提到菲涅爾效應。

一句話介紹菲涅爾效應:若是你站在湖邊低頭看腳下的水,你會發現水是透明的,反射不是特別強烈,能看到水底;若是你看遠處的湖面,你會看見山和天空的倒影。

菲涅爾效應

每種材質都有各自的菲涅爾值,這是根據其折射率決定的,代表了會有多少光線被物體吸取,又有多少光線從物體表面反彈fresnel。如下用 chaosgroup.com 的案例作說明。

圖中有一個粗糙的球體,右側圖片中的球體邊緣反射了天光而顯得邊緣發光,左側圖片中球體邊緣則沒有此現象。球體表面粗糙,菲涅爾效應會變得很是微弱,因此左圖是正確結果;若是球體相似金屬或水面,表面光滑,那麼正確結果應當相似右圖。

瞭解菲涅爾效應以後,咱們就能夠根據經驗憑空畫一些高菲涅爾效應的物體。下圖是 Oscar Salazar 用 CSS 畫的水滴。他用 box-shadow 給水滴的下緣增長了大量的透明白色陰影來模擬菲涅爾效應。

水滴效果

若是對比了現實中的水滴,你會發現 Oscar Salazar 畫的並不「真」。可是由於水滴的放大做用,再加上光影效果,十分抓人眼球,給人「神似」的感受,因此並不會以爲畫得有問題。

現實中的水滴

下圖是 Envato Tuts+ 畫的毛玻璃效果。左邊是原版,右邊是增長了菲涅爾效應後的修改版本。修改後的版本看起來像是邊緣平滑的玻璃版,而不是塑料板兒一塊draw-not-sure

場景實戰

限於技術,還有不少種類的光影效果文中沒有提到,之後有機會的話出個續集吧(咕咕咕預約中)。這裏咱們用一個 CSS 繪製的書籍封面效果做爲結尾,也順便串聯一下上文提到的技術。素材只給兩張書籍封面圖片,點此下載第一張點此下第二張refferer,目標是實現如下效果compressed

《乞力馬扎羅山的雪》

《八百萬種死法》

搭出框架

首先,觀察圖像,背景是一張紙,上面有一本書;光源在右上角,大概是平行光,光源高度離紙面不遠。咱們先用 HTML 搭出框架。

<div class="display-container">
  <!-- 紙背景材質層 -->
  <div class="paper" />
  <!-- 書的封面 -->
  <div class="book">
    <!-- 封面的紙的材質層 -->
    <div class="paper" />
    <!-- 用一張圖片自動撐開封面高度 -->
    <img class="corner" src="xxx" />
  </div>
</div>
複製代碼

處理紋理

而後咱們用 SVG 調出相似紙面的紋理效果,打光,而後設爲背景。

紙紋理

紙紋理打光後

<svg width="0" height="0">
  <filter id="surface">
    <feTurbulence type="fractalNoise" baseFrequency='.95 .95' numOctaves="80" result='noise' />
    <feDiffuseLighting in='noise' lighting-color='#004F85' surfaceScale='.8' result="grind">
      <feDistantLight azimuth='500' elevation='50' />
    </feDiffuseLighting>
    <feGaussianBlur in="grind" stdDeviation=".5"/>
  </filter>
</svg>
<div class="paper"></div>
<style> body { width: 100vw; height: 100vh; overflow: hidden; } .paper { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .paper::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; filter: url(#surface); } .paper::after { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: radial-gradient(ellipse at 100% 0%, rgba(255,255,255,0.25), rgba(255,255,255,0.18) 50%, rgba(255,255,255,0.15) 70%, rgba(0,0,0,.1)); } </style>
複製代碼

封面細節

緊接着開始繪製書籍封面,謹記有三個部分要處理:材質、高光和陰影。處理完以後結果以下

爲添加圖片的封面

有一些小細節要注意。

  • 封面摺痕的處理
  • 模仿閉塞陰影
  • 防止邊緣過於銳利

先來講說摺痕的處理。若是你手頭有一本實體書,那再好不過了fold-mark。試着用閃光燈打光,看看摺痕上的光影,應該能發現摺痕不過就是一道亮面和一道暗面的組合。咱們能夠用漸變來模擬這條摺痕。

封面摺痕

.book-cover .book::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 2;
  background-repeat: no-repeat;
  background-image:
    /* 1. 這條漸變是比較明顯的那道摺痕 */
    linear-gradient(to right, rgba(0,0,0,0.1) 0.3%, rgba(255,255,255,0.09) 1.1%, transparent 1.3%),
    /* 2. 這條一像素的漸變是封面最左側的摺痕(沒有暗面) */
    linear-gradient(to right, rgba(0,0,0,0.2) 0, rgba(255,255,255,0.08) 0%, transparent 0.5%);
  background-size: 50% 100%, 50% 100%;
  background-position: 0% top, 9% top;
}
複製代碼

而後再說說閉塞陰影。閉塞陰影的概念很是簡單,指兩個物體靠得比較近,遮住了光線;靠得越近的地方,陰影就越黑occlusion-shadow。對應到 CSS,drop-shadow 產生的陰影很「實」,但不能疊加;box-shadow 產生的陰影可調節陰影範圍,但會向四周擴散。咱們能夠經過疊加 drop-shadow 和 box-shadow 的方式來模擬出更真實的陰影效果。

.book-cover .book {
  position: relative;
  /* 因爲陰影對視覺中心有影響,因此把書總體向右上方挪一些 */
  margin-top: -1vh;
  margin-right: -1vh;
  width: 32%;
  max-width: 600px;
  font-size: 0;
  box-shadow: 
    -55px 40px 30px 0 rgb(0 0 0 / 10%), 
    -27px 25px 35px -5px rgb(0 0 0 / 20%),
    -10px 10px 15px 5px rgb(0 0 0 / 10%), 
    -12px 12px 10px 0 rgb(0 0 0 / 20%),
    -7px 7px 8px 0 rgb(0 0 0 / 10%),
    -5px 5px 5px 0 rgb(0 0 0 / 20%),
    -2px 2px 3px 0 rgb(0 0 0 / 30%);
  filter: drop-shadow(-20px 20px 15px rgba(0, 0, 0, .65));
}
複製代碼

實現效果以下圖。位置一指閉塞陰影,離書越近則陰影越濃重;位置二是 box-shadow 向外擴散的效果,和光源位置相背,違反了人的認知經驗,須要避免。

陰影效果

再是關於如何防止邊緣過於銳化。還未處理前,書籍的邊緣相似如下這張圖。因爲圖片被壓縮,什麼細節都看不出來了,這裏直接介紹一下防止邊緣銳化的方案吧。把兩張圖片疊一塊兒,下面那張圖片模糊一像素,上面那張圖片 border-radius 設置 2 像素,搞定。

封面邊緣

成品展現

最後,把全部代碼整合到一塊兒,調調參數,改改細節,就完成啦。嘿嘿,再放一張效果圖。能夠到 Codepen 查看最終效果

《羅生門》

啥,你想作一本能翻頁的書?

那得去康康 turn.js 的實現turnjs。效果以下圖,其中也有用到菲涅爾效應哦。

turn.js

閱讀更多

但願本文能對你有所幫助,我是仿生獅子,各位下期見~

想看看這篇文章是如何被創造的?你能從個人博客項目中找到答案;歡迎 Star & Follow;也請你們多來個人線上博客逛逛,排版超 Nice 哦~


  1. 人話就是:物體的材質、形狀以及位置。
  2. How to create a frosted glass effect using CSS?
  3. 《-webkit-box-reflect 屬性簡介及元素鏡像倒影實現》
  4. 其實用漸變+隨機的方法也能生成相似磨砂金屬的材質貼圖,這樣就能夠不須要引入外部圖片了,但過於麻煩,很差調參,還不如 base64 來得方便。
  5. 請原諒我,我老是潛意識裏把 SVG 看成 CSS 的超集,只由於能夠用它來畫畫並且在畫畫方面能夠吊打 CSS
  6. 雲朵效果優化(大佬優化大佬
  7. 《理解光澤度的菲涅爾效應》
  8. 「看起來像」和「現實」並不等同。就像許多畫家爲了營造氣氛或達到想要的效果,會打破光影的物理限制。這是經驗性的總結,固然,也見仁見智了。
  9. 因爲 OSS 設置了防盜鏈,在第三方網頁應該打不開這個連接。不過我開放了空 refferer 的訪問,可使用瀏覽器直接打開圖片再保存。
  10. 因爲圖片壓縮,演示的效果會打折。各位能夠實操以體驗完整效果及完整樂趣
  11. 別說你的書仍是新的。
  12. 對應遊戲畫面設置中的「環境光遮蔽」可能好理解一些。
  13. CSS 也能實現翻頁效果,只不過效果沒那麼好,見:The Mad Magazine Fold-In Effect in CSS
相關文章
相關標籤/搜索