[转]更优雅地绘制阴影

Box-shadow虽然是一个css3的属性,但由于浏览器支持不错,且用它来营造一种立体感、层次感着实方便,这让它成为了互联网上随处可见的css3特效。不过我感觉想写好阴影不是一件容易的事情。至少我常常摸索半天,写出来的阴影却总让人很难受。 上周在知乎上看到了一个问答,很受启发:如何理解 Material Design 中卡片的两层阴影,于是特意去看了Meterial Design的设计准则(中文翻译),觉得其中的一些设计思想和细节追求很值得我们去借签。

本文标题是“更优雅地绘制阴影”,但其实我并不懂“优雅”是什么,只是觉得别人的做法比我的好,比我的优雅。那么就来看看别人是怎么理解阴影的。

Meterial Design把App的部件、面板、模态框等都抽象成了卡片。对于一个个上面,除了有x/y轴坐标的定义,还增加了z轴。处于不同层级的卡片,有着相对独立的交互逻辑。可以说,卡片和层级就是一个app页面的交互隐喻。而层次的视觉定义就是通过阴影来完成。下面搬运两张图:

以上的阴影定义虽然是AI的格式,但很容易就能转换成前端的语言:

  1. .z-index-1{
  2. box-shadow: 0 1px 1.5px rgba(0,0,0,0.12), 0 1px 1px rgba(0,0,0,0.24);
  3. }
  4. .z-index-2{
  5. box-shadow: 0 3px 3px 0 rgba(0,0,0,0.16), 0 3px 3px 0 rgba(0,0,0,0.23);
  6. }
  7. .z-index-3{
  8. box-shadow: 0 10px 10px rgba(0,0,0,0.19), 0 6px 3px rgba(0,0,0,0.23);
  9. }
  10. .z-index-4{
  11. box-shadow: 0 14px 14px rgba(0,0,0,0.25), 0 10px 5px rgba(0,0,0,0.22);
  12. }
  13. .z-index-5{
  14. box-shadow: 0 19px 19px rgba(0,0,0,0.30), 0 15px 6px rgba(0,0,0,0.22);
  15. }

看看效果确实比我自己实现的感觉要优雅。

先插几句话, box-shadow 属性很有意思,它允许有若干重属性值,效果是相互叠加。张鑫旭有一篇博客谈这个问题(传送门),写得很好,大家可以参考一下。

这样的阴影效果大家自行感受,我觉得不管怎么样,好过我自己编造、瞎调出来的。但为什么要使用双重阴影?官方解释是用两个光源来模拟现实场景,一个是关键光,一个是环境光,所以会产生两个阴影。另外@jordanfc迟方的回答很棒,但很多是结论性的东西,我们需要更多一点思考。

思考一:box-shadow绘制阴影时,参照光源到底是什么样子的?

先说明:本文重点探究外阴影的阴影浓度和blur参数之间的关系,认为光源距离物体的距离远大于物体尺寸,忽略spread参数。首先考虑在box-shadow中,阴影颜色允许有半透明色,例如rgba(0, 0, 0, 0.6),这其实代表了存在了40%的均匀分布的环境光,这部分光我们可以不作考虑,因为它相当于常量,对整个阴影衰减的模型没有影响,同样,阴影的颜色也没有影响,可以不考虑。对于上面的阴影色彩,我们探究的是,阴影浓度是如何从0.6(仅仅定义了衰变的起点)到0的。

在网页中,对一个元素定义阴影后,我们发现,这个元素各个方向上的阴影是相同的。这说明什么呢?如果一个人站在这个阴影里,我们在元素外面画一个框,框与元素边界的距离是固定的,那么这个人在这个框的任何一个地方,看到的光源面积是相等的。所以我们能得出一个结论,光源应该是一个面光源,且形状与元素的形状是匹配的。为什么说是匹配,因为除了元素是圆形外,其他形状,我还想象不出光源应该是什么样。有可能对于绝大多数情况,能产生box-shadow属性效果的光源,在现实生活中根本不存在。(Kidding me?)

思考二:如果这样的光源存在,更真实的阴影是什么样的

我们看一个物体的阴影,只会直观地感受阴影的形态是否合理,并不会逆推光源是否合理,因为现实中的光照情形太多复杂,有太多的不确定。那么如果上述的光源客观存在,那么阴影是什么样的呢?

阴影的浓度和这个位置所能看到的光源面积反相关,这个应该不用解释。光源的形态不好确定,我们不妨降低一个维度,考虑一维情形下的规律:

假设阴影模糊半径为Blur,观察点距离物体的垂直投影边界为x(0<=x<=Blur),降维到一维情形下,光源长度为L,能照到观察点的光源长度为l,那么有:

推广到二维,假设光源面积为S,能照到观察点的光源面积为s,则有(不定积分,有一定的憶测成份)

a、b、c为常数,S也是常数,所以简化一下:

A、B为常数,A+B=1。当x=0时,s=0,这时阴影最浓,为0pacity;当x=Blur时,s=S,这时阴影消失。那么任意x时,阴影浓度o为:

由以上两式,可以解出:

我写到这里已经写不下去了,因为我算出来的结果跟@jordanfc迟方的回答中给出的图像是反的。贴上@jordanfc迟方的图:

他给出的是一个凹函数图像,我解出来的是一个凸函数,我又对图像没什么研究,是我错了吗?不知道。不过他对于阴影叠加的解释应该是错的。

思考三:多重阴影怎么叠加

先看看@jordanfc迟方的理解:

似乎能自圆其说,但是对吗?

考虑两个阴影的叠加,我们混合的都是xx%的黑色,所以这种情况下,无需考虑rgb通道,只考虑alpha通道即可。在上一篇文章《带Alpha通道的色彩叠加问题》中,我们详细推导了透明度混合的方法,现在就用起来!

在Meterial Design中的阴影加入y轴偏移,不妨先处理没有y轴偏移的情况。就以数据比较简单的z-depth-2来进行计算。首先分别写出两个阴影的衰变函数:

由  我们可以推导出:

是一个凸函数,跟我推导的结果很类似。既然有方程了,不妨把图像画出来看看:

混合后有阴影曲线曲率很小,但看得出跟@jordanfc迟方所描述的完全不一样。我们可以简单验证一下。由于混合后的曲线曲率很小,可以忽略,我们就把这个二次曲线当成一条直线,方程为:

翻译成css就是

  1. box-shadow: 0 3px 3px 0 rgba(0,0,0,0.3532);

对比看一下效果,能看出有差异吗?

如果加上了x、y偏移效果会怎么样呢?其实很简单,只要把刚刚的O1(x)和O2(x)改成分段函数,这样计算混合阴影会复杂很多。最后就以z-depth=4的阴影图像给本文收个尾吧:

博客地址:http://www.zhouhua.info/2015/03/24/shadow/

时间: 2024-11-07 06:58:55

[转]更优雅地绘制阴影的相关文章

少年,是时候换种更优雅的方式部署你的php代码了

让我们来回忆下上次你是怎么发布你的代码的: 1. 先把线上的代码用ftp备份下来 2. 上传修改了的文件 3. 测试一下功能是否正常 4. 网站500了,赶紧用备份替换回去 5. 替换错了/替换漏了 6. 一台服务器发布成功 7. 登录每一台执行一遍发布操作 8. 加班搞定 9. 老板发飙 ... 尤其现在的互联网行业,讲究快速迭代,小步快跑.像bug修复或者小功能的修改几乎每天都发版本,大功能的版本迭代每周也差不多会有一次.相信不少同行们像我上面说的这样发布自己的代码吧.或者可能先进一点,直接

iOS:quartz2D绘图(给图形绘制阴影)

quartz2D既可以绘制原始图形,也可以给原始图形绘制阴影. 绘制阴影时,需要的一些参数:上下文.阴影偏移量.阴影模糊系数 注意:在对绘制的图形做了绘制阴影处理前,需要先对上下文进行保存,绘制阴影成功后,还要对上下文进行复位.目的是为了不影响后面的绘图操作. 举例的阴影绘制实例如下: 1.自定义一个视图类DemoView,并将控制器的视图关联该自定义类,同时在该定义类中重写- (void)drawRect:(CGRect)rect,将绘制无阴影图形和绘制阴影图形的调用方法写在里面.     

框架基础:ajax设计方案(五)--- 集成promise规范,更优雅的书写代码

距离上一篇博客书写,又过去了大概几个月了,这段时间暂时离开了这个行业,让大脑休息一下.一个人旅行,一个人休息,正好也去完成一个目标 --- 拥有自己的驾照.当然,也把自己晒的黑漆马虎的.不过这一段时间虽然在技术上没有学太多东西,但是在心态上给了自己一个沉淀的机会,感觉自己变得更加沉稳和成熟,感觉这就是自己需要找到的自己,回归自我.好了,废话不多说了,虽然技术上没有学一些新的东西,但是欠的东西还是要补回来的.正如这篇博客,前端Promise规范的实现与ajax技术的集成,当时github上一个用户

使用 Promises 编写更优雅的 JavaScript 代码

你可能已经无意中听说过 Promises,很多人都在讨论它,使用它,但你不知道为什么它们如此特别.难道你不能使用回调么?有什么了特别的?在本文中,我们一起来看看 Promises 是什么以及如何使用它们写出更优雅的 JavaScript 代码. 您可能感兴趣的相关文章 开发中可能会用到的几个 jQuery 提示和技巧 精心挑选的优秀jQuery Ajax分页插件和教程 推荐几款很好用的 JavaScript 文件上传插件 精心挑选的优秀 jQuery 文本特效插件和教程 精心挑选12款优秀 jQ

绘制阴影引发的 iOS 绘图性能问题总结

转自:http://blog.devdlh.com/blog/2013/03/18/performance-problerm-caused-by-shadowpath/ 绘制阴影引发的 iOS 绘图性能问题总结 MAR 18TH, 2013 | COMMENTS 在 iOS 开发中,通过设置 layer 的 shadowColor.shadowOpacity.shadowOffset.shadowRadius 几个属性可以很方便的为 UIView 添加阴影效果.但是前段时间碰到一个问题,在添加了

HTML5 绘制阴影

代码: <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>绘制阴影</title> <script> function init() { var canvas=document.getElementById('canvas'); var ctx=canvas.getContext("2

HTML之canvas 8 绘制阴影-绘制文字

绘制阴影 Context.shadowOffetX:阴影横向位移量 Context.shadowOffetY:阴影纵向位移量 Context.shadowColor:阴影颜色 Context.shadowBlur阴影的模糊范围 绘制文字 Context.fillStyle=’#00F’; Context.font=”bold 30px sans-serif”; Context.fillText(“hello world”,0,0); Context.strokeText(“hello world

C#中一种替换switch语句更优雅的写法

今天在项目中遇到了使用switch语句判断条件,但问题是条件比较多,大概有几十个条件,满屏幕的case判断,是否有更优雅的写法替代switch语句呢? 假设有这样的一个场景:商场经常会根据情况采取不同的打折方案,如果打折方案比较少,可以考虑使用switch语句作判断.但如果有几十甚至几百种打折方案的时候,用switch语句就不够优雅. 先来一个打折接口. public interface IValueProcessor { decimal DaZhe(short policy,decimal o

CSS 黑魔法小技巧,让你少写不必要的JS,代码更优雅

首页 登录注册 CSS 黑魔法小技巧,让你少写不必要的JS,代码更优雅 阅读 8113 收藏 927 2017-09-26 原文链接:github.com 腾讯云容器服务CSS,立即免费体验容器集群吧!cloud.tencent.com 之前不久,由于自己平时涉猎还算广泛,总结了一篇博客:这些JavaScript编程黑科技,装逼指南,高逼格代码,让你惊叹不已,没想到受到了大家的欢迎,有人希望能博主还能整理个 CSS 的一些黑魔法小技巧,无奈我 CSS 一直很渣,没什么干货,最近写了一个 Chro