總聽人說光和影是孿生兄弟,有光就有影。其實否則,若是沒有光線強弱的對比,也就不會有陰影的存在。咱們老是依靠物體表面的反光來感知世界,假設全部物體都能徹底吸取光線,那世界將變得黑漆漆一片。css
所謂只有理解光,才能駕馭陰影。好的設計師每每都是用光高手,能經過複雜的光影向讀者傳達出物體的質感、空間感以及層次感texture。他們畫出來的設計稿都是漂漂亮亮的,這可苦了廣大前端同胞!在瀏覽器中,咱們只能用寥寥幾個 CSS 屬性,束手束腳地同時還千方百計地還原設計稿。畢竟,相比咱們用的 CSS 手槍,設計師們用的 AE、C4D 看起來就像大炮同樣!html
好在咱們能夠暫且假以性能爲由,繼續正大光明地怎麼簡單怎麼來(汗)。就像一提到光影效果,你們第一反應確定是操起 box-shadow、text-shaodw、drop-shadow 三件套直接開畫。就大部分場景來講,這些屬性還挺好用的,可用它實現多種效果,好比單側投影、空心投影和投影動畫。若涉及到彩色陰影、長投影或是倒影,就須要結合其它 CSS 屬性打輔助了。前端
彩色投影,可讓僞元素繼承父元素的背景,再加模糊濾鏡便可。這個思路也能夠用來製做毛玻璃效果frosted-glass。git
.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
陰影的另外一面是高光。畫高光的思路能夠直接套畫陰影的思路,只不過須要將投影的顏色改成半透明白色。web
另外一種方法是用背景漸變或僞元素模擬高光。若再配合 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查看細節)。
不一樣的高光和陰影細節會給人不一樣的感覺,比方說這裏有一張雪花電視效果圖,其高光像素和陰影像素的對比度要比磨砂金屬表面的大得多。
使用圖片的不便之處在於沒有辦法邊調整細節邊預覽,而且 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 指示用平行光做爲光源。
光源?弄個材質還要這麼複雜嘛?
先別急,光源其實也就幾種:點狀光、平行光、聚光。能夠簡單理解成電燈泡、太陽以及戲劇燈。因爲咱們還將在表面材質的話題中停留一下子,暫且只須要用到平行光。
大體瞭解了上面那段 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。效果以下圖,其中也有用到菲涅爾效應哦。
但願本文能對你有所幫助,我是仿生獅子,各位下期見~
想看看這篇文章是如何被創造的?你能從個人博客項目中找到答案;歡迎 Star & Follow;也請你們多來個人線上博客逛逛,排版超 Nice 哦~