译文出自: 闪电矿工翻译组原文地址:Drawing Realistic Clouds with SVG and CSScss
原文做者:Beau Jacksonhtml
仓库原文连接:Drawing Realistic Clouds with SVG and CSSgit
译者:sichenguogithub
希腊神话中有这样一个故事是讲述宙斯创造出来一个云女神涅斐勒,而且相似大多数的希腊神话同样的,这个故事很是的奇异且限制级。下面一个简短克制的版本。算法
咱们可以知道的是: 涅斐勒是由宙斯以他本身美丽的妻子的形象创造的。一个凡人碰见涅斐勒,陷入爱河,而且他们一块儿有了一个孩子,确切的说是一个半人半马的婴儿。数组
很怪诞对吧,值得庆幸的是,在浏览器中建立云的过程要简单得多,并且风险要小得多。浏览器
(Demo)ide
最近,我发现开发者Yuan Chuan 已经实现了用代码生成逼真的云。对我来讲,浏览器中的云这个概念一直如同希腊神话中的那边神秘。svg
让咱们来看一下这个’画笔‘吧 (点这里),能够见到的是做者经过使用 box-shadow
做为包含两个滤镜的 <filter>
元素的补充实现了这个使人惊叹的‘云图’!函数
想要绘制出兼顾写实和精致的云图,须要搭配使用feTurbulence
和feDisplacementMap
这两个滤镜。这两个滤镜不只能够具备强大且复杂的功能,而且还能够提供一些使人兴奋的特性(其中包含奥斯卡获奖算法))!固然,这些功能在浏览器‘引擎盖’下有着使人生畏的复杂性。
虽然这些 SVG 滤镜的物理特性超出了本文的范围,但 MDN 和 w3.org 上提供了大量文档供学习参考。另外还有 feTurbulence
和 feDisplacement
介绍。
对于本文,咱们将专一于学习使用这些 SVG 滤镜来实现使人惊奇的效果。滤镜背后的实现算法并不在咱们的研究范围内,就像艺术家虽然能够绘制出美丽的景观但却不用懂得油漆的分子结构。
而咱们须要作的只是密切关注一小部分 SVG 属性,这些属性使得咱们能够在浏览器中绘制逼真的云图。经过学习这些属性可让咱们在项目中按照本身的意愿更好的制做出特定的滤镜效果。
CSS 规则 box-shadow
的五个值得关注的属性:
box-shadow: <offsetX> <offsetY> <blurRadius> <spreadRadius> <color>;
让咱们来增大这些值(可能会高于正常的开发者会作的),这样在视图的右下方就会有阴影出现。 !(Demo)
#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; }
你确定表演或者见过过皮影戏吧?Credit: Double-M
相似于经过改变手的形状来改变阴影的方式,咱们的 HTML 中的“源形状”能够经过移动或者变形来同步影响其在浏览器中呈现的阴影的形状。box-shadow
复制原始图形的圆角效果。SVG 滤镜对于元素都以及元素的 shadow
的都会生效。
<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>
)。咱们的源<div>
和它的阴影都被滤波器独立地扭曲。
咱们经过 ID
选择器(#cloud-circle
) 将下面的 CSS
规则添加到目标元素上:
#cloud-circle { filter: url(#filter); box-shadow: 200px 200px 50px 0px #fff; }
好吧,添加上 SVG 滤镜依旧没能给咱们带来什么惊喜。
别担忧!这才仅仅只是开始,更有趣的还在后面。
使用这一属性进行的一些实验能够产生显著的效果。暂时,让咱们保持它的 feTurbulence
不变,只调整 DisplacementMap
的 scale
属性。
随着 scale
的增长(每次增长 30 ),咱们的源 <div>
开始扭曲变形且它的投下阴影更接近天空中云随机的形状。
<feDisplacementMap in="SourceGraphic" scale="180" />
The scale attribute incremented by values of 30. (Demo)
好的,经过改变 scale
,好像终于接近了正确云形状的的数值范围!让咱们稍微改变下颜色,以便看起来更像一朵 云彩 ☁️。
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; }
如今咱们的图形愈来愈接近真实的云效果了!
下面的一组图像显示了 blur 对 box-shadow 效果的影响。下面的图形中 blur 的数组依次递增。
The cloud becomes "softer" as the blur value increases.
为了使咱们的云带有一些积云效果,能够稍微扩大 <div>
的尺寸。
#cloud-circle { width: 500px; height: 275px; background: #000; border-radius: 50%; filter: url(#filter); box-shadow: 200px 200px 60px 0px #fff; }
好的,可是如今的源
div
元素正在成为障碍。😫
等等!咱们已经扩大了源元素的尺寸,可是它如今已经开始影响咱们的“云(白色阴影)”了。 很简单,只须要相距更远的地方投影遍能够解决这个遮挡问题。
而经过经过一些 CSS 定位很好地实现源元素的隐层。<body>
元素是云 div
元素的父元素,默认状况下是静态定位的。 将苏设置为就绝对定位就能够将不须要展现的 #cloud-circle
隐藏。
#cloud-circle { width: 500px; height: 275px; background: #000; border-radius: 50%; filter: url(#filter); box-shadow: 400px 400px 60px 0px #fff; /* Increase shadow offset */ position: absolute; /* Take the parent out of the document flow */ top: -320px; /* Move a little down */ left: -320px; /* Move a little right */ }
很棒,这样看起来咱们的云图已经初步成型了。
平摊在屏幕之上的云彩,嗯,这应该是一个对咱们绘制出的云图一个很好的描述,老是感受还缺乏点什么,咱们应该还能够作的会更好的!
这就是咱们想要的:
Credit: 图源:pcdazero
从这张照片中云的深度,质感和丰富程度来看,有一件事是清楚的:宙斯是上过艺术学校。至少,他确定是阅读过通用组件设计原则的,这个原则阐述了一个通用性的概念:
[...]照明误差在深度和天然的解释中起着重要做用,而且能够由设计师以各类方式操纵......使用明暗区域之间的对比度使得外观具备景深。
这段话为咱们提供了一个对云图进行优化更加真实的提示。经过将不一样形状,大小和颜色的图层堆叠在一块儿,咱们能够在参考图像制造对比度中以来渲染云图。须要的只是使用 filter
制造多个图层。
<svg width="0" height="0"> <!-- Back Layer --> <filter id="filter-back"> <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" /> <feDisplacementMap in="SourceGraphic" scale="170" /> </filter> <!-- Middle Layer --> <filter id="filter-mid"> <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" /> <feDisplacementMap in="SourceGraphic" scale="150" /> </filter> <!-- Front Layer --> <filter id="filter-front"> <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" /> <feDisplacementMap in="SourceGraphic" scale="100" /> </filter> </svg>
再加上图层能够提供更多的角度去探索 feTurbulence
以及 实现更多的功能。 咱们选择使用更加平滑的 type
:fractalNoise
,而后将 numOctaves
设置为 6。
<feTurbulence type="fractalNoise" baseFrequency="n" numOctaves="6" />
如今,让咱们将注意力集中在 baseFrequency
属性上。如下是咱们在逐渐增长 baseFrequency
值的时获得的结果 :
值取两端的值时,图形都会趋向于圆形。区别就是越小,更模糊。值越大,图形就更加显得生硬。
像 turbulence
,噪音,频率和 octave
这样的词可能看起来很奇怪甚至使人困惑。但不要惧怕!将滤镜的效果类比为声波实际上很是准确。咱们能够将低频率(baseFrequency
=0.001)等同于低噪声和低频率,更高的 (baseFrequency
=0.1) 值则与更清晰的音调相对应。
咱们能够看到,咱们对积云效果的对应的 baseFrequency
值大概位于 0.005~0.01 范围之间。
增大 numOctaves
的值能够以极其细致的细节渲染图像。可是这须要大量计算,所以须要注意的是:更细致的渲染也可能带来的还有性能问题,因此如非必要,请将此值尽可能控制在必定范围内。
The higher the value we put into numOctaves the more granular detail give to our cloud.
好消息就是,在此例中,咱们的云图须要的细节并不须要使用太高的 numOctaves
值,如上图所示,numOctaves
为 4 或者 5 就能够获得不错的效果。
至于 seed
属性还能够再深刻研究一下。可是,就咱们的目的而言, seed
的做用能够对形状形成影响。
Perlin Noise 函数(上文提到
)会使用此值做为其随机数生成器的初始值。若是不设置该属性将默认 seed
为零。跟 baseFrequency 的区别就是,不管咱们给出什么价值 seed
,都不须要担忧性能损失。
不一样的 seed 值产生不一样的形状
上面的 GIF 展现了不一样 seed
值时的效果。须要注意的是,每朵云都是分层的复合云。(虽然我调整了每一个图层的属性,但我保持各自的 seed
值分布均匀。)
Credit: Brockenhexe
在这里,仔细观察参考图像,我将 3 个‘云’层(不透明度不一样)叠加到一个 div
上。经过反复试验和调整 seed
参数值,最终获得的下面这个相似于照片中云的形状的效果图。
固然,若是咱们认为咱们绘制的这个云彩要比宙斯创造的女神还要漂亮,确定就是狂妄自大了。
可是,随着咱们对 CSS
和 SVG
滤镜的更多的了解,咱们就越有能力创造出在视觉上使人惊叹的图形,好比下面这些:
Animated Reflecting mist
在这篇文章中咱们沉浸在 SVG
强大功能和复杂性的海洋中,虽然 SVG
滤镜一般看起来比较复杂且难以理解。
然而就好像A Single Div project这个项目和Diana Smith's painting techniques这个例子同样,有趣的和有创意的方法老是能够获得使人惊艳的效果。
我相信不少开发者都有能力制做出一个云彩出来,可是一个能够轻松制做出云彩的工具会更受欢迎。所以我开发了一个小工具作这件事。