总听人说光和影是孪生兄弟,有光就有影。其实否则,若是没有光线强弱的对比,也就不会有阴影的存在。咱们老是依靠物体表面的反光来感知世界,假设全部物体都能彻底吸取光线,那世界将变得黑漆漆一片。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 哦~