【译】来,用 SVG & CSS 给你画一朵真实的云

原文:Drawing Realistic Clouds with SVG and CSScss

这是做者最终实现的效果: html

......哦,不,应该是这张:
在线查看效果:cloud demo


正文开始啦~git

希腊神话讲述了一个关于宙斯创造云女神涅斐勒的故事。和其余希腊神话同样,这个故事极其怪异而且有点限制级。下面的表述则是一个比较简短且含蓄的版本(限制级,非战斗人员请撤离)。github

涅斐勒(云神),听说是宙斯按照本身美丽妻子的形象创造的。传说有个凡人碰见了涅斐勒,一见倾心爱上了她而且在一块儿了,后来她们一块儿睡了一个觉,而后奇怪的事情发生了,一朵云生下了一个半人半马的小孩,传说这就半人马的祖先。web

很难以想象对吗?就我我的而言,我搞不懂。但庆幸的是,浏览器中建立云的过程要简单得多,也没有那么不可描述。算法

这是@袁川画的云demo

最近,我发现开发者 @袁川 已经用代码实现了仿真的云烟。对我来讲,在浏览器中实现一直是个神话。浏览器

经过简单扫一下这个demo里面的代码,咱们能够现象,逼真而又独特的云朵是能够经过使用cssbox-shadow和一个包含两个元素的SVG过滤器<filter>去实现。svg

咱们想要的仿真效果是经过feTurbulencefeDisplacementMap之间的微妙混合来实现的。SVG过滤器功能强大、复杂,而且提供了使人很是兴奋的功能(还包括奥斯卡获奖算法)。然而,在它的底层,它们的复杂性可能有点吓人!函数

译者:奥斯卡获奖算法,这么牛逼?无知限制了个人想象。性能

虽然SVG的物理特性超出了本文的范畴,可是在MDNw3.org上有大量的文档。一个免费的关于feTurbulencefeDisplacement很是有用的页面(同时被做为这本惊奇的书的一个章节)

对于本文,咱们专一学习如何使用SVG的过滤器实现惊人的效果。咱们不须要深刻研究其底层的算法,正如艺术家不须要了解涂漆的分子结构也能绘出使人惊叹的风景同样。

先从一些基础开始

CSS的box-shadow属性有五个值须要搞懂:

box-shadow: <offsetX> <offsetY> <blurRadius> <spreadRadius> <color>;
复制代码

让咱们把这些值调高(可能比任何理智的开发人员调得都要高):

#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;
}
复制代码

会获得下面的效果图:

你曾经应该也玩过或者看过影子木偶对吗?像下面这样:

译者:看见这个,想起小时候本身也玩过, 两个拇指叉在一块能摆出鸽子的投影。

就像一只手改变形状能够改变投影同样,咱们改变HTML的“源形状”也可使渲染在浏览器中的投影变形。box-shadow复制了原始尺寸和border-radius上的“渐变”特性,SVG过滤器则同时应用于元素及其阴影。

<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>)提供的过滤器。

咱们借助SVG过滤器的ID,经过添加CSS规则将HTML元素(#cloud-circle)和SVG过滤器进行关联:

#cloud-circle {
  filter: url(#filter);
  box-shadow: 200px 200px 50px 0px #fff;
}
复制代码

嗯!很棒,就是这样

在线查看demo

别急!咱们只是了解了皮毛,还有不少好东西要看。

尝试使用feDisplacementMap的scale属性

使用这一属性进行一些非科学试验能够产生显著的效果。如今,咱们保持feTurbulence的值不变,简单调整feDisplacementMapscale属性值。

随着scale的增长(以30为增量),咱们的源<div>变得扭曲,投射的阴影反应出天空中云出现的随机形式。

<feDisplacementMap in="SourceGraphic" scale="180"/>
复制代码

在线查看demo

好了,咱们有进展了!让咱们稍微改变颜色,以造成更具说服力的云。

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;
}
复制代码

如今,咱们愈来愈接近真实的云朵效果了!

修改box-shadow的模糊度

下面一套图片展现了box-shadow属性的模糊度做用的效果,这里,咱们以10px递增模糊值:

随着模糊值增长,云朵也变得更加柔和。

为了增长一点积云的效果,咱们能够稍微扩宽源<div>的宽度:

#cloud-circle {
  width: 500px; 
  height: 275px;
  background: #000;
  border-radius: 50%;
  filter: url(#filter);
  box-shadow: 200px 200px 60px 0px #fff;
}
复制代码

很好,咱们的源元素开始碍事了😫

等等,咱们扩宽了源元素的宽度,但它如今遮挡在咱们云层(白色阴影)的上方。让咱们在更远的位置从新投影,这样咱们的云就不会再被源图像遮挡了(你能够想象成把你的手往远离墙的方向移动,这样它就不会挡住你的影子木偶的视线了)。

这点咱们经过CSS定位能够很好地实现。<body>是父元素,默认是静态定位的,咱们给源<div>添加绝对定位。最初地,这也会从新定位咱们的阴影,所以咱们还须要增长阴影和元素之间的距离。

#cloud-circle {
  width: 500px;
  height: 275px;
  background: #000;
  border-radius: 50%;
  filter: url(#filter);
  box-shadow: 400px 400px 60px 0px #fff; /* 增长投影位移 */
  position: absolute;
  top: -320px;
  left: -320px;
}
复制代码

是的,咱们已经实现了一个极具说服力的云:这里查看

浏览器上展现的已是一个至关完美的云了,但我不肯定......这片云真的能公正地描述云女神涅斐勒吗?我相信咱们还能够作得更好!

经过层次传达深度

这是咱们想要的效果:

从这张照片中云层的深度、纹理和丰富性来看,宙斯必定是读过艺术专业的。至少,他必定读过《通用设计法则》,这本书阐述了一个强大而又普通的概念:

照明误差在深度和天然度的解释中起着重要做用,设计师能够经过多种方式操纵照明误差,利用明暗区域之间的对比度来改变深度的外观。

这段话给了咱们一个提示,咱们能够将不一样形状、大小和颜色的图层堆叠在一块儿,能够实现像参考图片中那样具备高保真度的云。咱们要作的也只是屡次调用SVG过滤器。

使用三个SVG过滤器绘制前中后三朵云:

<svg width="0" height="0">
    <!-- 后层 -->
    <filter id="filter-back">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" />
      <feDisplacementMap in="SourceGraphic" scale="170" />
    </filter>
    <!-- 中层 -->
    <filter id="filter-mid">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" />
      <feDisplacementMap in="SourceGraphic" scale="150" />
    </filter>
    <!-- 前层 -->
    <filter id="filter-front">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" />
      <feDisplacementMap in="SourceGraphic" scale="100" />
    </filter>
</svg>
复制代码

经过分层的应用,咱们有机会去探索feTurbulence并认识它的多样性。咱们选择了较为平滑的类型:fractalNoise,对于numOctaves的值最高只调到了6。

上面这些意味着什么?咱们来看一下baseFrequency这个属性,下面几张图片是不一样baseFrequency值下的效果:

值越低,图像就越圆,越模糊。

从效果看,介于0.005~0.01的值比较符合咱们想要的积云效果。

用numOctaves添加细节

增长numOctaves值容许咱们以更细的粒度去渲染图像,这个过程须要大量的计算,所以须要注意:高值会严重影响性能。

numOctaves的值设置得越高,云的效果就越精细。

幸运的是咱们不须要为达到精细的效果而设置过高的值,介于4~5就够了。

最后的效果

查看效果

用seed属性进行无限变形

关于seed属性有不少能够说,但就咱们的目的而言,seed的做用能够归结为:不一样的值,不一样的形状

柏林噪音函数使用这个值做为其随机数生成器的起点。选择不包含此属性则将seed值默认为0;当包含时,不管咱们设置何值,都不须要担忧会影响性能。

不一样的seed值,对应生成不一样的形状。

上面的GIF表明了seed做用的效果。请记住,每一个云都是分层的复合云(虽然我调整了每一层的属性,但我保持了它们的seed值一致)。

仔细观察上面这张参考图片,我将3个云层<div>堆砌在一个基础的div上,经过反复试验不一样的seed值,最终获得了与图中比较类似的形状。以下图:

在线查看demo

天空的极限

显然,认为咱们用<div>在浏览器上绘制的云比涅斐勒高级是很荒谬的。

可是,咱们可以梳理出CSS和SVG过滤器的神秘感越多,咱们就越有能力去创造在视觉上使人惊叹的东西,而且高度保真于雷神的创做。那么,咱们能够作进一步的试验了!

在这篇文章中,咱们刚刚涉足了一个充满力量和复杂性的知识海洋,SVG过滤器一般看起来复杂地难以理解。

不过,就像A Single Div ProjectSmith's绘画技术中的例子同样,有趣和实验性的方法总会给人惊艳的效果。

我但愿这篇文章能让你对web上进行摄影写实开发感到兴奋,欢迎下方评论交流你的想法~

相关文章
相关标签/搜索