转载

手把手教你做一个SVG Lava Lamp

运气爆棚的我总是时不时地能创建出大家都喜欢的案例。 SVG Lava Lamp 就是其中之一——因为很多人问我关于这个灯的文字,所以我决定写这篇文章来给大家解释解释。

一眼看去,这篇教程好像会有很多很多的代码——其实不是这样的。我只是在不停地粘贴相同的代码,所以代码量会慢慢增加,看起来也就好像代码量很大了。

还有标题看起来也挺好笑的——虽然我喜欢开玩笑,但也不会影响到后边各小节的内容,它们其实还是挺简单的。如果觉得它们倒了你的胃口,或者你的母语不是英语的话(无法理解我),我还是赶紧先在这里道个歉吧哈哈。

如果你在找Adobe Illustrator这款软件(或相关链接)的话,可以戳 这里 下载。

希望这听起来不会很随意,在制作这个案例的时候并没有考虑那么多的东西——可能就是2到3个小时,部分原因是我有Sara Farnsworth的 非常棒的shot 作为参考——它非常棒而且给了我很多灵感~

闲聊到此为止

好了我不继续闲聊了 (:зゝ∠)

创建这个动画的主要难点在于浏览器之外的绘图程序。实际的JavaScript动画大约只有15-20行代码。

你需要一个像 Adobe Illustrator (AI)这样的矢量绘图软件。但是它实在是贵到令人发指!!所以我们还有其他的选择,如 Sketch [$99]和 InkScape [免费!]。但是我用的是AI,因为我已经用习惯了╮(╯▽╰)╭。

除此之外我们还需要GSAP的 TweenMax 库,这个倒是免费的。

好剑出鞘!!

首先,创建一个 600x600 的画板。我也不知道为什么我总是用这个尺寸,不过对我来说正好合适。

现在来绘制玻璃瓶的形状——我们会使用这个形状作为实际的玻璃以及光斑的遮罩(实际上是clipPath,它和SVG中的 mask遮罩 还是有一些不同的)。玻璃的颜色并不要紧,因为我们会在后边的代码中删掉它。后面再详细介绍。

手把手教你做一个SVG Lava Lamp

现在绘制顶部和底部的部分——注意到在顶部和底部,都有一个非常暗的从橙色到黑色的渐变。这可以创建一个熔岩光溢出的错觉。

手把手教你做一个SVG Lava Lamp

我们还需要绘制所有的蜡光斑,然后给它们填充一个径向渐变——只需要画四到五个不同尺寸和形状的就ok。使用椭圆工具先画出一个圆,然后应用一个AI通道滤镜( Filter > Warp > Arc Lower ),然后做一些弯曲和变形的设置(记得打开预览哟~)。

手把手教你做一个SVG Lava Lamp

一旦画好了光斑,然后确保较暗的渐变的那部分比较靠近顶部——因为这个灯的光源是在下面的。现在把它们都放置在熔岩灯的顶部周围(如果你见过真正的熔岩灯,就会注意到光斑是在顶部的牙轮里面消失的)。

不要忘了还要画一个更大的更平的光斑放置在底部,这个光斑的顶部只比顶部的碗座高出一点点。虽然这不是一个真正的熔岩灯,但是所有的蜡融化和诗意的漂浮有助于粘稠的效果,所以我们添加 SVG模糊 和 颜色矩阵 滤镜,结合起来才创建出粘稠的效果。

手把手教你做一个SVG Lava Lamp

噢我差点忘了!我画了一个桌子(底部的黑色矩形)在那里——否则它看起来就像是漂浮在半空中的。

最后我做了一个带径向渐变的矩形,放置在最底层,作为一个背景阴影。我一开始尝试过在玻璃上应用一个SVG glow滤镜,但是效果非常糟糕,而且从形状上看并没有得到我想要的效果,所以这是一套美观合适的解决方案。

手把手教你做一个SVG Lava Lamp

现在,如果你碰巧参加了我九月份在英国Brighton的“ 为什么要有创意的原因 ”的座谈会,首先感谢你的到来——会议非常好玩!你可能还记得我一直在强调一个非常重要的技巧,在从AI的画板上复制和粘贴你的图形到一个文本编辑器中(或CodePen等等)的时候。我会在这里再次强调,因为这真的非常重要。

一个非常有用的技巧

你可以在AI中把你的SVG保存成 .svg 格式的文件,但是有一点非常烦人的就是它会输出所有图层,不论它是否可见。它还倾向于生成内联环路,这点我非常不喜欢。

通常我都是挑选好需要的层,然后把它们复制粘贴到我的文本编辑器中。这种方法存在的问题是,当你选择好然后复制粘贴到一个编辑器中时,你粘贴的是一整个SVG的高度和宽度,还是你选中的图层的高度和宽度呢。

当你在AI中选中元素,然后复制粘贴到编辑器中,你粘贴的是一整个SVG的高度和宽度,还是你选中的图层的高度和宽度呢。

也就是说,如果你的玻璃灯图片是在 600x600 尺寸的画板上,包括顶部和底部的图像,你想要创建一个 600x600 的SVG,还要让这些元素准确地放置在你希望它出现的位置,你可能会想通过选中这些元素,并将其复制/粘贴到编辑器中就可以得到你想要的结果。但是事实并非如此,因为它并不知道自己粘贴的上下文(画板);所以它选择的是对应图层的高度和宽度,并将其作为结果SVG的高度和宽度。

手把手教你做一个SVG Lava Lamp

注意变换面板——它显示了你选中图层的宽度是 131.304px ,高度是 304px 。当你将其复制粘贴到一个HTML页面中时,它的SVG如下:

手把手教你做一个SVG Lava Lamp

(我刚刚意识到我没有画好——底座顶部的线竟然没有对齐!!!)

高亮显示SVG的宽度( 131.3 )和高度( 304 )——生成的SVG和选中图层是相同的尺寸,也就是说你失去了 600x600 的画板,它们现在并没有按照我们希望的放置在中心的位置。

在AI中画一个和你的画板一样尺寸的矩形,然后在将画板上的内容复制到编辑器时,和其它的元素一起选中它。

这个简单的解决方案(我知道能够写成一个教程的东西从来都不是简单的,但是在这种特定情况下,它真的很简单!)就是在AI中绘制一个矩形,和你的画板一样的尺寸,然后每次复制内容到编辑器时一起选中它。从现在起,我把它当做一个“上下文”图层。你可以在后面从SVG代码中丢弃这个上下文图层,但是在复制/粘贴的过程中这真的是必不可少的,这样生成的SVG才可以保持 600x600 的画板尺寸(因为上下文图层和画板是一样的大小)。对吧?

所以现在是...

手把手教你做一个SVG Lava Lamp

...然后在HTML中生成...

手把手教你做一个SVG Lava Lamp

现在看起来好像好点了。

所以,现在你需要添加蜡光斑,你可以把它们全部选中,然后添加上下文图层(保持按住 shift 键然后选中它)——这样它们才可以保留在正确的位置。

我习惯使用Sublime Text作为“中间人”来整理我的SVG,然后挑选我需要的部分。这不是绝对必要的,但是我喜欢Sublime的“查找/替换/正则表达式”功能,所以用来帮我执行清理任务是非常合适的。你可以很方便地把它们都粘贴到CodePen的HTML面板中。

有关图层的一个简短的说明——我给所有图层都命名了,即使我不打算给所有图层都添加动画。图层名在SVG中会变成它们自身的 id (尽管有AI自己加上的恼人的数字和下划线╭(╯^╰)╮)。给图层命名使得它们之后放到在页面中更容易被识别。

手把手教你做一个SVG Lava Lamp

正如我上面提到的,AI会给每个图层添加id——如果是添加类名的话就更好一些了——这样的话你就可以有几个通过类名联系起来的图层了。尽管这是一个方法,在我前面提到的座谈会上,我提到了一个点,把SVG数据粘贴到sublime中,然后修改所有的 id= 变成 class= 。这其实有一点危险,因为引用的渐变、滤镜、clipPath(还有很多其它的内容)都需要通过 id 来索引,所以我现在暂时没有把 id 换成 class 。如果我真的需要使用 class 给元素分组,我会加上 class 名,但是还是保留 id

制作过程,用大白话好好讲解

我们已经准备好要把图形放到页面上了。开始前,我想要用简单的英语解释一下动画的工作原理。我在创建任何东西或者写任何代码之前,都会做很多准备工作——我会先自己弄清楚视觉上的效果要如何用技术实现。

  • 使用玻璃(主灯体)有两个用途。首先,我们在视觉上将它显示为一个低透明度的图形(这样看起来才像玻璃)。接着我们使用相同的图形作为包裹后面那组蜡光斑的 clipPath
  • 我们会把蜡光斑组成一组,然后应用几个滤镜(这样可以应用于组中的所有成员上)。丑丑的粘稠效果使用的是高斯模糊滤镜, alpha 对比度提升到使用颜色矩阵滤镜(我不会去考虑粘稠效果太多的细节,因为这是在这篇文章范围之外的内容,但是这里有篇 文章 解释得非常好,使用了几个GIF图片)。
  • 我们给光斑添加动画,使得它可以上下循环(通过一个 TweenMax 实例),每个光斑都有一个随机时长(在特定参数内)。在重复它们自身的循环之前,都有一段随机等待时间。
  • 我们给主要的 TimelineMax 实例添加 TweenMax 补间,但是让它们在不同的时间开始。
  • 最后我们设置( TimelineMax.seek(100); ),调到主时间轴约 100s 的位置,使得熔岩灯在每次加载的时候都是进行时的状态。

复制、粘贴、整理

最后我们可以把它从AI中导出,然后放到文本编辑器中来整理。

我们可以选择我们需要的图层,然后把它们复制过来。这块是没有问题的,除了最后你会得到大量的代码段,而且看起来都非常相似,而且可能很快会变得非常混乱,如果你不是一次性完成的话。我习惯复制一段一段的内容,然后再把内容整合到一起。

所以,首先把所有图层都隐藏。然后显示 bg 层,选中它(确保它是放在最底层,以显示实际的图形层)——这是一个带有渐变的矩形,放置在熔岩灯的后面,处在SVG文件的最底层。在这里我们不需要选中 600X600 的上下文图层(用于保持SVG的尺寸),因为这个 bg 图层的尺寸就是 600X600 。现在把它复制到文本编辑器中,结果如下:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In  --> <svg version="1.1"   xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"   x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> </defs> <radialGradient id="bg_3_" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse">  <stop  offset="7.142857e-002" style="stop-color:#471A19"/>  <stop  offset="0.3107" style="stop-color:#290F0E"/>  <stop  offset="0.553" style="stop-color:#120706"/>  <stop  offset="0.7828" style="stop-color:#050202"/>  <stop  offset="0.9847" style="stop-color:#000000"/> </radialGradient> <rect id="bg_2_" fill="url(#bg_3_)" width="600" height="600"/> </svg>  

这样我们的第一次复制/粘贴就完成了,创建了一个 600X600 的SVG文件,因为我们粘贴的图形就是600X600大小的。现在开始我们给这个SVG添加新的图形。

AI提供的id名 bg_2 真的非常难看而且笨拙。把 _2 删掉。它还会生成id为 id_3 的渐变。我们把它修改成像 bgGrad 这样可读性强的名字——我们还需要把 <rect> 的填充属性改为 fill="url(#bgGrad)"

我希望保留 <defs></defs> 标签中定义的渐变、滤镜、 clipPaths 和遮罩。这里通常用来防止那些暂时不让它可视化的内容。所以我们把渐变标签移动一下——大概像这样:

<svg version="1.1"   xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"   x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs>  <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse">   <stop  offset="7.142857e-002" style="stop-color:#471A19"/>   <stop  offset="0.3107" style="stop-color:#290F0E"/>   <stop  offset="0.553" style="stop-color:#120706"/>   <stop  offset="0.7828" style="stop-color:#050202"/>   <stop  offset="0.9847" style="stop-color:#000000"/>  </radialGradient> </defs> <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> </svg>  

好的我们赢啦(这听起来好像有点自满哈哈,sorry)。

现在在codepen中创建一个案例,然后把这段代码从文本编辑器中复制到HTML面板中。这里我假设你是有自己的codepen账号的——如果你没有的话,我可能就不想跟你做朋友了。

我不会给你展示这看起来是如何的,因为这和最后的代码片段是一样的。

现在回到AI里,隐藏 bg 图层——我们接下来都不需要它了。尽管我们现在需要一个 600X600 的上下文图层(就是那个可怕的紫色图层)。我前面提到这个图层非常非常重要了吧?什么?没有?蹲墙角反省去,都不好好看文章!

我们接下来添加玻璃。选中 glassShape 图层,记得一起选中上下文图层。然后把它复制/粘贴到sublime中。不要担心粘贴过去的最后一项是啥——我们使用编辑器只是作为一个中间件。

所以它现在是这样的:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In  --> <svg version="1.1"  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"  x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> </defs> <rect id="context_1_" fill="#523CBF" width="600" height="600"/> <path id="glassShape_1_" fill="#F21458" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18     C227.6,352.9,262,174,262,174z"/> </svg>  

glassShape 上的 _1 删掉,把 fill 属性也删掉(我后面会解释为什么)。现在只复制路径数据到案例中——像这样选中:

<path id="glassShape" fill="#F21458" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18     C227.6,352.9,262,174,262,174z"/> 

你可能还记得,我们有两处地方要使用这个——玻璃图形和 clipPath (给蜡光斑添加遮罩)。

所以在codepen中我们需要在 <defs> 标签内添加一个 <clipPath> 标签,然后把这个玻璃图形放进去。我们还需要给 clipPath 一个 id (起名 glassMask )。你的codepen里HTML面板的SVG代码应该如下:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In  --> <svg version="1.1"      xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"      x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs>     <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse">       <stop  offset="7.142857e-002" style="stop-color:#471A19"/>       <stop  offset="0.3107" style="stop-color:#290F0E"/>       <stop  offset="0.553" style="stop-color:#120706"/>       <stop  offset="0.7828" style="stop-color:#050202"/>       <stop  offset="0.9847" style="stop-color:#000000"/>     </radialGradient>    <clipPath id="glassMask">     <path id="glassShape" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18     C227.6,352.9,262,174,262,174z"/>       </clipPath>  </defs>  <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> </svg> 

在案例的预览区域是看不到任何内容的,除了背景的渐变。这是因为我们刚才把玻璃图形添加到 <defs> 中了,这块是不可见的。为了使 glassShape 可见,我们使用一个 <use> 标签把它添加到渐变背景的下方。我们把 opacity 也调低了,这样背景可以穿透显示。

这里还有一点值得一提的是,尽管 glassShape 已经放置在 <clipPath> 标签中了,我们还是可以在其它地方引用,让它多次出场都是没问题的。

现在我们的代码如下:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In  --> <svg version="1.1"      xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"      x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs>     <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse">       <stop  offset="7.142857e-002" style="stop-color:#471A19"/>       <stop  offset="0.3107" style="stop-color:#290F0E"/>       <stop  offset="0.553" style="stop-color:#120706"/>       <stop  offset="0.7828" style="stop-color:#050202"/>       <stop  offset="0.9847" style="stop-color:#000000"/>     </radialGradient>    <clipPath id="glassMask">     <path id="glassShape" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18     C227.6,352.9,262,174,262,174z"/>       </clipPath>  </defs>  <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> <use xlink:href="#glassShape" fill="#EB7619" opacity="0.1"/> </svg> 

注意我给 <use> 实例添加了 fill="#EB7619"

我们之前把初始的 glassShape 上的 fill 属性删除了,这样我们可以在 <use> 实例中添加这个样式。如果我们保留了初始 glassShapefill 属性,我们就不能在后面的 <use> 实例中重写这个属性了。

现在你的预览面板中的玻璃和背景应该显示如下:

手把手教你做一个SVG Lava Lamp

还醒着吗朋友?对的,有人开了扇窗。

接下来我们把桌子摆上,还有熔岩灯的底部和顶部。选中它们,记得包括上下文图层,然后把它们复制到你的文本编辑器中。

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In  --> <svg version="1.1"   xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"   x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> </defs> <rect id="context_1_" fill="#523CBF" width="600" height="600"/> <linearGradient id="lampTop_2_" gradientUnits="userSpaceOnUse" x1="292" y1="135" x2="292" y2="174">  <stop  offset="1.530612e-002" style="stop-color:#000000"/>  <stop  offset="0.233" style="stop-color:#050202"/>  <stop  offset="0.4808" style="stop-color:#120706"/>  <stop  offset="0.7421" style="stop-color:#290F0E"/>  <stop  offset="1" style="stop-color:#471A19"/> </linearGradient> <polygon id="lampTop_1_" fill="url(#lampTop_2_)" points="269,135 262,174 322,174 316,135 "/> <linearGradient id="lampBot_2_" gradientUnits="userSpaceOnUse" x1="292.375" y1="470" x2="292.375" y2="379">  <stop  offset="5.102041e-003" style="stop-color:#000000"/>  <stop  offset="0.2251" style="stop-color:#050202"/>  <stop  offset="0.4754" style="stop-color:#120706"/>  <stop  offset="0.7394" style="stop-color:#290F0E"/>  <stop  offset="1" style="stop-color:#471A19"/> </linearGradient> <path id="lampBot_1_" fill="url(#lampBot_2_)" d="M226.8,379c2.6,43,23.9,54.6,28.3,60.2c3.3,5.4-10,30.8-10,30.8h95.5  c0,0-16.5-25.1-14.5-30.8s26-15.2,32-60.2C328,379,240.3,379,226.8,379z"/> <rect id="tableTop_1_" y="470" width="600" height="130"/> </svg>  

再次,我们把 lampToplampBottableTop 里的 _1 都删掉。不要担心重命名渐变引用(例如: fill="url(#lampBot_2_)" )——重命名渐变和 fill 引用,可以使它工作得更好。

现在,把渐变标签复制/粘贴到 <defs> 标签中,在codepen的HTML面板。代码如下:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In  --> <svg version="1.1"      xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"      x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs>     <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse">       <stop  offset="7.142857e-002" style="stop-color:#471A19"/>       <stop  offset="0.3107" style="stop-color:#290F0E"/>       <stop  offset="0.553" style="stop-color:#120706"/>       <stop  offset="0.7828" style="stop-color:#050202"/>       <stop  offset="0.9847" style="stop-color:#000000"/>     </radialGradient>    <clipPath id="glassMask">     <path id="glassShape"  d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18     C227.6,352.9,262,174,262,174z"/>       </clipPath>    <linearGradient id="lampTop_2_" gradientUnits="userSpaceOnUse" x1="292" y1="135" x2="292" y2="174">     <stop  offset="1.530612e-002" style="stop-color:#000000"/>     <stop  offset="0.233" style="stop-color:#050202"/>     <stop  offset="0.4808" style="stop-color:#120706"/>     <stop  offset="0.7421" style="stop-color:#290F0E"/>     <stop  offset="1" style="stop-color:#471A19"/>   </linearGradient>   <linearGradient id="lampBot_2_" gradientUnits="userSpaceOnUse" x1="292.375" y1="470" x2="292.375" y2="379">     <stop  offset="5.102041e-003" style="stop-color:#000000"/>     <stop  offset="0.2251" style="stop-color:#050202"/>     <stop  offset="0.4754" style="stop-color:#120706"/>     <stop  offset="0.7394" style="stop-color:#290F0E"/>     <stop  offset="1" style="stop-color:#471A19"/>   </linearGradient>    </defs>  <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> <use xlink:href="#glassShape" fill="#EB7619" opacity="0.1"/> </svg> 

现在复制 lampToplampBottableTop ,然后把它们粘贴到玻璃图形的 <use> 标签的后面。

代码如下:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In  --> <svg version="1.1"      xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"      x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs>     <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse">       <stop  offset="7.142857e-002" style="stop-color:#471A19"/>       <stop  offset="0.3107" style="stop-color:#290F0E"/>       <stop  offset="0.553" style="stop-color:#120706"/>       <stop  offset="0.7828" style="stop-color:#050202"/>       <stop  offset="0.9847" style="stop-color:#000000"/>     </radialGradient>    <clipPath id="glassMask">     <path id="glassShape"  d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18     C227.6,352.9,262,174,262,174z"/>       </clipPath>    <linearGradient id="lampTop_2_" gradientUnits="userSpaceOnUse" x1="292" y1="135" x2="292" y2="174">     <stop  offset="1.530612e-002" style="stop-color:#000000"/>     <stop  offset="0.233" style="stop-color:#050202"/>     <stop  offset="0.4808" style="stop-color:#120706"/>     <stop  offset="0.7421" style="stop-color:#290F0E"/>     <stop  offset="1" style="stop-color:#471A19"/>   </linearGradient>   <linearGradient id="lampBot_2_" gradientUnits="userSpaceOnUse" x1="292.375" y1="470" x2="292.375" y2="379">     <stop  offset="5.102041e-003" style="stop-color:#000000"/>     <stop  offset="0.2251" style="stop-color:#050202"/>     <stop  offset="0.4754" style="stop-color:#120706"/>     <stop  offset="0.7394" style="stop-color:#290F0E"/>     <stop  offset="1" style="stop-color:#471A19"/>   </linearGradient>    </defs>  <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> <use xlink:href="#glassShape" fill="#EB7619" opacity="0.1"/> <polygon id="lampTop" fill="url(#lampTop_2_)" points="269,135 262,174 322,174 316,135 "/> <path id="lampBot" fill="url(#lampBot_2_)" d="M226.8,379c2.6,43,23.9,54.6,28.3,60.2c3.3,5.4-10,30.8-10,30.8h95.5     c0,0-16.5-25.1-14.5-30.8s26-15.2,32-60.2C328,379,240.3,379,226.8,379z"/> <rect id="tableTop" y="470" width="600" height="130"/>   </svg> 

你的预览面板展示的内容应该是长这样的:

手把手教你做一个SVG Lava Lamp

终于论到光斑了!

回到AI,隐藏我们刚才复制过的图层(除了上下文图层)。

显示所有蜡光斑图层,包括底部那个( botBlob ),选中它们包括上下文图层。

把它们复制/粘贴到你的中间件编辑器中。如下:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In  --> <svg version="1.1"   xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"   x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> </defs> <rect id="context_1_" fill="#523CBF" width="600" height="600"/> <radialGradient id="blob0_2_" cx="292" cy="171.5" r="56.5354" gradientUnits="userSpaceOnUse">  <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>  <stop  offset="0.1922" style="stop-color:#FA9712"/>  <stop  offset="0.3992" style="stop-color:#ED8A14"/>  <stop  offset="0.6186" style="stop-color:#D67316"/>  <stop  offset="0.8449" style="stop-color:#B65419"/>  <stop  offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <path id="blob0_1_" fill="url(#blob0_2_)" d="M326.2,149.5c-5,19.2-21.4,29.2-37.8,26.6c-16.5-2.9-33.4-12.9-37.1-26.6  c-3.8-13.6,12.5-32.1,37.8-34.9C314.4,111.8,331.3,130.4,326.2,149.5z"/> <radialGradient id="blob1_2_" cx="297" cy="167.5" r="37.2156" gradientUnits="userSpaceOnUse">  <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>  <stop  offset="0.1922" style="stop-color:#FA9712"/>  <stop  offset="0.3992" style="stop-color:#ED8A14"/>  <stop  offset="0.6186" style="stop-color:#D67316"/>  <stop  offset="0.8449" style="stop-color:#B65419"/>  <stop  offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <path id="blob1_1_" fill="url(#blob1_2_)" d="M320.5,146.4c-4.4,10.1-16.4,20.2-26.8,25.3c-10.4,5.2-22.4-2.9-26.8-15.2  c-4.4-11.6,7.6-20.4,26.8-25.3C312.9,126.3,324.9,135.6,320.5,146.4z"/> <radialGradient id="blob2_2_" cx="294" cy="157" r="23" gradientUnits="userSpaceOnUse">  <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>  <stop  offset="0.1922" style="stop-color:#FA9712"/>  <stop  offset="0.3992" style="stop-color:#ED8A14"/>  <stop  offset="0.6186" style="stop-color:#D67316"/>  <stop  offset="0.8449" style="stop-color:#B65419"/>  <stop  offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <path id="blob2_1_" fill="url(#blob2_2_)" d="M278,147.7c2.7-7.1,9.4-15.7,15.4-16.4c5.9-0.4,12.6,8.5,15.4,16.9  c2.7,8.4-4.2,14.9-15.4,14.2C282.2,161.5,275.3,154.8,278,147.7z"/> <radialGradient id="botBlob_2_" cx="284.5" cy="421.5" r="53.521" gradientTransform="matrix(-2.802637e-002 -0.9996 5.9976 -0.1682 -2235.533 776.7669)" gradientUnits="userSpaceOnUse">  <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>  <stop  offset="0.1922" style="stop-color:#FA9712"/>  <stop  offset="0.3992" style="stop-color:#ED8A14"/>  <stop  offset="0.6186" style="stop-color:#D67316"/>  <stop  offset="0.8449" style="stop-color:#B65419"/>  <stop  offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <path id="botBlob_1_" fill="url(#botBlob_2_)" d="M354,381.2c6.8,3.4,5.4,7.4-5.6,10.4c-10.7,3.1-31.1,5.1-54.4,8.4  c-23.3,3.3-43.7,0.8-54.4-2.4c-11-3.4-12.4-7.6-5.6-13.8c6.8-7,18.9-14.6,29.6-17.4c11-3.3,20.6-1.8,30.4-1.4  c9.8,0.4,19.4,5.1,30.4,8.3C335.1,376.8,347.2,378.6,354,381.2z"/> <radialGradient id="blob3_2_" cx="291.9382" cy="167.4587" r="41.0767" gradientUnits="userSpaceOnUse">  <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>  <stop  offset="0.1922" style="stop-color:#FA9712"/>  <stop  offset="0.3992" style="stop-color:#ED8A14"/>  <stop  offset="0.6186" style="stop-color:#D67316"/>  <stop  offset="0.8449" style="stop-color:#B65419"/>  <stop  offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <path id="blob3_1_" fill="url(#blob3_2_)" d="M312.7,147.3c-2.1,16.4-15.3,27.2-23.2,25.3c-8.1-1.8-12.6-13-14.8-24.9  c-1.9-11.8,2.7-22.7,14.8-25.3C301.5,119.6,314.7,130.8,312.7,147.3z"/> <radialGradient id="blob4_2_" cx="306.5" cy="155" r="14.109" gradientUnits="userSpaceOnUse">  <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>  <stop  offset="0.1922" style="stop-color:#FA9712"/>  <stop  offset="0.3992" style="stop-color:#ED8A14"/>  <stop  offset="0.6186" style="stop-color:#D67316"/>  <stop  offset="0.8449" style="stop-color:#B65419"/>  <stop  offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <path id="blob4_1_" fill="url(#blob4_2_)" d="M317.8,147.4c-1,8.2-9.8,10.3-13.8,9.3c-4-0.9-6.5-3-7.6-8.9c-1-5.9,2.3-8.5,8.4-9.8  C310.8,136.6,318.8,139.1,317.8,147.4z"/> </svg>  

天呐!乱成狗了!

好吧我们需要把它整理一下。先把每条路径的id中的 _1 删掉(例如:把 <path id="blob4_1_" 变成 <path id="blob4" )。

然后,和之前一样,选择所有渐变标签,复制/粘贴到HTML面板的 <defs> 标签内。不需要给它们的id重命名和 fill 引用。

接下来选择光斑路径(应为六个),然后复制/粘贴到codepen中的HTML面板,放置在最后的路径下方(应该是 tableTop 矩形)。

这块真的很无聊,但愿你没有精神崩溃,完成的代码应该如下所示:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In  --> <svg version="1.1"      xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"      x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs>     <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse">       <stop  offset="7.142857e-002" style="stop-color:#471A19"/>       <stop  offset="0.3107" style="stop-color:#290F0E"/>       <stop  offset="0.553" style="stop-color:#120706"/>       <stop  offset="0.7828" style="stop-color:#050202"/>       <stop  offset="0.9847" style="stop-color:#000000"/>     </radialGradient>    <clipPath id="glassMask">     <path id="glassShape"  d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18     C227.6,352.9,262,174,262,174z"/>       </clipPath>   <!-- LAMP GRADIENTS-->   <linearGradient id="lampTop_2_" gradientUnits="userSpaceOnUse" x1="292" y1="135" x2="292" y2="174">     <stop  offset="1.530612e-002" style="stop-color:#000000"/>     <stop  offset="0.233" style="stop-color:#050202"/>     <stop  offset="0.4808" style="stop-color:#120706"/>     <stop  offset="0.7421" style="stop-color:#290F0E"/>     <stop  offset="1" style="stop-color:#471A19"/>   </linearGradient>   <linearGradient id="lampBot_2_" gradientUnits="userSpaceOnUse" x1="292.375" y1="470" x2="292.375" y2="379">     <stop  offset="5.102041e-003" style="stop-color:#000000"/>     <stop  offset="0.2251" style="stop-color:#050202"/>     <stop  offset="0.4754" style="stop-color:#120706"/>     <stop  offset="0.7394" style="stop-color:#290F0E"/>     <stop  offset="1" style="stop-color:#471A19"/>   </linearGradient>   <!--END LAMP GRADIENTS-->    <!-- BLOB GRADIENTS -->   <radialGradient id="blob0_2_" cx="292" cy="171.5" r="56.5354" gradientUnits="userSpaceOnUse">     <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>     <stop  offset="0.1922" style="stop-color:#FA9712"/>     <stop  offset="0.3992" style="stop-color:#ED8A14"/>     <stop  offset="0.6186" style="stop-color:#D67316"/>     <stop  offset="0.8449" style="stop-color:#B65419"/>     <stop  offset="1" style="stop-color:#9C3A1C"/>   </radialGradient>   <radialGradient id="blob1_2_" cx="297" cy="167.5" r="37.2156" gradientUnits="userSpaceOnUse">     <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>     <stop  offset="0.1922" style="stop-color:#FA9712"/>     <stop  offset="0.3992" style="stop-color:#ED8A14"/>     <stop  offset="0.6186" style="stop-color:#D67316"/>     <stop  offset="0.8449" style="stop-color:#B65419"/>     <stop  offset="1" style="stop-color:#9C3A1C"/>   </radialGradient>   <radialGradient id="blob2_2_" cx="294" cy="157" r="23" gradientUnits="userSpaceOnUse">     <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>     <stop  offset="0.1922" style="stop-color:#FA9712"/>     <stop  offset="0.3992" style="stop-color:#ED8A14"/>     <stop  offset="0.6186" style="stop-color:#D67316"/>     <stop  offset="0.8449" style="stop-color:#B65419"/>     <stop  offset="1" style="stop-color:#9C3A1C"/>   </radialGradient>   <radialGradient id="botBlob_2_" cx="284.5" cy="421.5" r="53.521" gradientTransform="matrix(-2.802637e-002 -0.9996 5.9976 -0.1682 -2235.533 776.7669)" gradientUnits="userSpaceOnUse">     <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>     <stop  offset="0.1922" style="stop-color:#FA9712"/>     <stop  offset="0.3992" style="stop-color:#ED8A14"/>     <stop  offset="0.6186" style="stop-color:#D67316"/>     <stop  offset="0.8449" style="stop-color:#B65419"/>     <stop  offset="1" style="stop-color:#9C3A1C"/>   </radialGradient>   <radialGradient id="blob3_2_" cx="291.9382" cy="167.4587" r="41.0767" gradientUnits="userSpaceOnUse">     <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>     <stop  offset="0.1922" style="stop-color:#FA9712"/>     <stop  offset="0.3992" style="stop-color:#ED8A14"/>     <stop  offset="0.6186" style="stop-color:#D67316"/>     <stop  offset="0.8449" style="stop-color:#B65419"/>     <stop  offset="1" style="stop-color:#9C3A1C"/>   </radialGradient>   <radialGradient id="blob4_2_" cx="306.5" cy="155" r="14.109" gradientUnits="userSpaceOnUse">     <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>     <stop  offset="0.1922" style="stop-color:#FA9712"/>     <stop  offset="0.3992" style="stop-color:#ED8A14"/>     <stop  offset="0.6186" style="stop-color:#D67316"/>     <stop  offset="0.8449" style="stop-color:#B65419"/>     <stop  offset="1" style="stop-color:#9C3A1C"/>   </radialGradient>   <!-- END BLOB GRADIENTS-->  </defs>  <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> <use xlink:href="#glassShape" fill="#EB7619" opacity="0.1"/> <polygon id="lampTop" fill="url(#lampTop_2_)" points="269,135 262,174 322,174 316,135 "/> <path id="lampBot" fill="url(#lampBot_2_)" d="M226.8,379c2.6,43,23.9,54.6,28.3,60.2c3.3,5.4-10,30.8-10,30.8h95.5     c0,0-16.5-25.1-14.5-30.8s26-15.2,32-60.2C328,379,240.3,379,226.8,379z"/> <rect id="tableTop" y="470" width="600" height="130"/>  <path id="blob0" fill="url(#blob0_2_)" d="M326.2,149.5c-5,19.2-21.4,29.2-37.8,26.6c-16.5-2.9-33.4-12.9-37.1-26.6     c-3.8-13.6,12.5-32.1,37.8-34.9C314.4,111.8,331.3,130.4,326.2,149.5z"/>  <path id="blob1" fill="url(#blob1_2_)" d="M320.5,146.4c-4.4,10.1-16.4,20.2-26.8,25.3c-10.4,5.2-22.4-2.9-26.8-15.2     c-4.4-11.6,7.6-20.4,26.8-25.3C312.9,126.3,324.9,135.6,320.5,146.4z"/>  <path id="blob2" fill="url(#blob2_2_)" d="M278,147.7c2.7-7.1,9.4-15.7,15.4-16.4c5.9-0.4,12.6,8.5,15.4,16.9     c2.7,8.4-4.2,14.9-15.4,14.2C282.2,161.5,275.3,154.8,278,147.7z"/>  <path id="botBlob" fill="url(#botBlob_2_)" d="M354,381.2c6.8,3.4,5.4,7.4-5.6,10.4c-10.7,3.1-31.1,5.1-54.4,8.4     c-23.3,3.3-43.7,0.8-54.4-2.4c-11-3.4-12.4-7.6-5.6-13.8c6.8-7,18.9-14.6,29.6-17.4c11-3.3,20.6-1.8,30.4-1.4     c9.8,0.4,19.4,5.1,30.4,8.3C335.1,376.8,347.2,378.6,354,381.2z"/>  <path id="blob3" fill="url(#blob3_2_)" d="M312.7,147.3c-2.1,16.4-15.3,27.2-23.2,25.3c-8.1-1.8-12.6-13-14.8-24.9     c-1.9-11.8,2.7-22.7,14.8-25.3C301.5,119.6,314.7,130.8,312.7,147.3z"/>  <path id="blob4" fill="url(#blob4_2_)" d="M317.8,147.4c-1,8.2-9.8,10.3-13.8,9.3c-4-0.9-6.5-3-7.6-8.9c-1-5.9,2.3-8.5,8.4-9.8     C310.8,136.6,318.8,139.1,317.8,147.4z"/>   </svg> 

注意我刚才在渐变块之间的 <defs> 添加了注释,来解释谁引用了谁。

你的codepen预览面板应该如下所示——如果它正常的话:

手把手教你做一个SVG Lava Lamp

给光斑分组、添加遮罩

还记得我们把 glassShape 放到 clipPath 中了吗?好了我们现在要使用它来裁剪所有的光斑,这样它们看起来就是在熔岩灯内的,但是首先我们需要把所有的光斑放到一组。这样我们就可以一次性地裁剪整个分组了。

所以,在codepen中,找到 idblob0 的路径,在它上面添加一个 <g> 标签。

找到最后一个光斑( blob4 ),然后添加一个闭合标签 </g> 。这样给光斑分组就完成啦。

现在回到开始的那个 <g> ,然后添加裁剪路径属性,如下:

<g clip-path="url(#glassMask)"> 

该裁剪路径引用了我们之前在 <defs> 中写的 <clipPath> 标签。

你在codepen面板中的代码应该如下:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In  --> <svg version="1.1"      xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"      x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs>     <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse">       <stop  offset="7.142857e-002" style="stop-color:#471A19"/>       <stop  offset="0.3107" style="stop-color:#290F0E"/>       <stop  offset="0.553" style="stop-color:#120706"/>       <stop  offset="0.7828" style="stop-color:#050202"/>       <stop  offset="0.9847" style="stop-color:#000000"/>     </radialGradient>    <clipPath id="glassMask">     <path id="glassShape"  d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18     C227.6,352.9,262,174,262,174z"/>       </clipPath>   <!-- LAMP GRADIENTS-->   <linearGradient id="lampTop_2_" gradientUnits="userSpaceOnUse" x1="292" y1="135" x2="292" y2="174">     <stop  offset="1.530612e-002" style="stop-color:#000000"/>     <stop  offset="0.233" style="stop-color:#050202"/>     <stop  offset="0.4808" style="stop-color:#120706"/>     <stop  offset="0.7421" style="stop-color:#290F0E"/>     <stop  offset="1" style="stop-color:#471A19"/>   </linearGradient>   <linearGradient id="lampBot_2_" gradientUnits="userSpaceOnUse" x1="292.375" y1="470" x2="292.375" y2="379">     <stop  offset="5.102041e-003" style="stop-color:#000000"/>     <stop  offset="0.2251" style="stop-color:#050202"/>     <stop  offset="0.4754" style="stop-color:#120706"/>     <stop  offset="0.7394" style="stop-color:#290F0E"/>     <stop  offset="1" style="stop-color:#471A19"/>   </linearGradient>   <!--END LAMP GRADIENTS-->    <!-- BLOB GRADIENTS -->   <radialGradient id="blob0_2_" cx="292" cy="171.5" r="56.5354" gradientUnits="userSpaceOnUse">     <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>     <stop  offset="0.1922" style="stop-color:#FA9712"/>     <stop  offset="0.3992" style="stop-color:#ED8A14"/>     <stop  offset="0.6186" style="stop-color:#D67316"/>     <stop  offset="0.8449" style="stop-color:#B65419"/>     <stop  offset="1" style="stop-color:#9C3A1C"/>   </radialGradient>   <radialGradient id="blob1_2_" cx="297" cy="167.5" r="37.2156" gradientUnits="userSpaceOnUse">     <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>     <stop  offset="0.1922" style="stop-color:#FA9712"/>     <stop  offset="0.3992" style="stop-color:#ED8A14"/>     <stop  offset="0.6186" style="stop-color:#D67316"/>     <stop  offset="0.8449" style="stop-color:#B65419"/>     <stop  offset="1" style="stop-color:#9C3A1C"/>   </radialGradient>   <radialGradient id="blob2_2_" cx="294" cy="157" r="23" gradientUnits="userSpaceOnUse">     <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>     <stop  offset="0.1922" style="stop-color:#FA9712"/>     <stop  offset="0.3992" style="stop-color:#ED8A14"/>     <stop  offset="0.6186" style="stop-color:#D67316"/>     <stop  offset="0.8449" style="stop-color:#B65419"/>     <stop  offset="1" style="stop-color:#9C3A1C"/>   </radialGradient>   <radialGradient id="botBlob_2_" cx="284.5" cy="421.5" r="53.521" gradientTransform="matrix(-2.802637e-002 -0.9996 5.9976 -0.1682 -2235.533 776.7669)" gradientUnits="userSpaceOnUse">     <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>     <stop  offset="0.1922" style="stop-color:#FA9712"/>     <stop  offset="0.3992" style="stop-color:#ED8A14"/>     <stop  offset="0.6186" style="stop-color:#D67316"/>     <stop  offset="0.8449" style="stop-color:#B65419"/>     <stop  offset="1" style="stop-color:#9C3A1C"/>   </radialGradient>   <radialGradient id="blob3_2_" cx="291.9382" cy="167.4587" r="41.0767" gradientUnits="userSpaceOnUse">     <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>     <stop  offset="0.1922" style="stop-color:#FA9712"/>     <stop  offset="0.3992" style="stop-color:#ED8A14"/>     <stop  offset="0.6186" style="stop-color:#D67316"/>     <stop  offset="0.8449" style="stop-color:#B65419"/>     <stop  offset="1" style="stop-color:#9C3A1C"/>   </radialGradient>   <radialGradient id="blob4_2_" cx="306.5" cy="155" r="14.109" gradientUnits="userSpaceOnUse">     <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>     <stop  offset="0.1922" style="stop-color:#FA9712"/>     <stop  offset="0.3992" style="stop-color:#ED8A14"/>     <stop  offset="0.6186" style="stop-color:#D67316"/>     <stop  offset="0.8449" style="stop-color:#B65419"/>     <stop  offset="1" style="stop-color:#9C3A1C"/>   </radialGradient>   <!-- END BLOB GRADIENTS-->  </defs>  <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> <use xlink:href="#glassShape" fill="#EB7619" opacity="0.1"/> <polygon id="lampTop" fill="url(#lampTop_2_)" points="269,135 262,174 322,174 316,135 "/> <path id="lampBot" fill="url(#lampBot_2_)" d="M226.8,379c2.6,43,23.9,54.6,28.3,60.2c3.3,5.4-10,30.8-10,30.8h95.5     c0,0-16.5-25.1-14.5-30.8s26-15.2,32-60.2C328,379,240.3,379,226.8,379z"/> <rect id="tableTop" y="470" width="600" height="130"/>   <g clip-path="url(#glassMask)">     <path id="blob0" fill="url(#blob0_2_)" d="M326.2,149.5c-5,19.2-21.4,29.2-37.8,26.6c-16.5-2.9-33.4-12.9-37.1-26.6     c-3.8-13.6,12.5-32.1,37.8-34.9C314.4,111.8,331.3,130.4,326.2,149.5z"/>    <path id="blob1" fill="url(#blob1_2_)" d="M320.5,146.4c-4.4,10.1-16.4,20.2-26.8,25.3c-10.4,5.2-22.4-2.9-26.8-15.2     c-4.4-11.6,7.6-20.4,26.8-25.3C312.9,126.3,324.9,135.6,320.5,146.4z"/>    <path id="blob2" fill="url(#blob2_2_)" d="M278,147.7c2.7-7.1,9.4-15.7,15.4-16.4c5.9-0.4,12.6,8.5,15.4,16.9     c2.7,8.4-4.2,14.9-15.4,14.2C282.2,161.5,275.3,154.8,278,147.7z"/>    <path id="botBlob" fill="url(#botBlob_2_)" d="M354,381.2c6.8,3.4,5.4,7.4-5.6,10.4c-10.7,3.1-31.1,5.1-54.4,8.4     c-23.3,3.3-43.7,0.8-54.4-2.4c-11-3.4-12.4-7.6-5.6-13.8c6.8-7,18.9-14.6,29.6-17.4c11-3.3,20.6-1.8,30.4-1.4     c9.8,0.4,19.4,5.1,30.4,8.3C335.1,376.8,347.2,378.6,354,381.2z"/>    <path id="blob3" fill="url(#blob3_2_)" d="M312.7,147.3c-2.1,16.4-15.3,27.2-23.2,25.3c-8.1-1.8-12.6-13-14.8-24.9     c-1.9-11.8,2.7-22.7,14.8-25.3C301.5,119.6,314.7,130.8,312.7,147.3z"/>    <path id="blob4" fill="url(#blob4_2_)" d="M317.8,147.4c-1,8.2-9.8,10.3-13.8,9.3c-4-0.9-6.5-3-7.6-8.9c-1-5.9,2.3-8.5,8.4-9.8     C310.8,136.6,318.8,139.1,317.8,147.4z"/>     </g> </svg> 

你的codepen预览面板加上裁剪的光斑之后,现在应该是长这样的:

手把手教你做一个SVG Lava Lamp

为了创建粘稠效果的滤镜,需要添加一段JS。之后,我们会重新检查一下SVG代码,

老大,是时候让我的光斑动起来了

(这些标题真是越来越接奇怪了 (:зゝ∠)

现在是时候写一些实际的JS代码了。我们会使用到GreenSock (GSAP) TweenMax库,不仅是因为它有我们需要的所有内容,如TimelineMax,还因为它真的非常棒~简单粗暴。

在codepen的JS面板中,点击那个小齿轮图标,如果你比较懒的话,可以直接从Quick Add下拉菜单中选择添加。唉,这是TweenMax(1.16.1)的旧版本了,虽然用起来完全没有问题,不过我比较喜欢追求更新到最新最新的东西,所以我建议粘贴下面的这个最新版本:

https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js

JS设置面板如下所示:

手把手教你做一个SVG Lava Lamp

最后的JS代码

我们将要写的代码真的是非常非常的简短——大概 20 行。我把它们都贴上来,然后一行一行来讲解。

function randomBetween(min,max) {     return Math.floor(Math.random()*(max-min+1)+min); }  var tl = new TimelineMax();  for(var i = 0; i < 5; i++){    var t = TweenMax.to(document.querySelector('#blob' + i), randomBetween(14, 50), {     y:260,     repeat:-1,     repeatDelay:randomBetween(1, 3),     yoyo:true,     ease:Linear.easeNone   })    tl.add(t, (i+1)/0.6) }  tl.seek(100); 

JS代码讲解

function randomBetween(min,max) {     return Math.floor(Math.random()*(max-min+1)+min); } 

所以首先,我们要写的是一个 randomBetween 函数,用于生成随机时间。传入一个最小值和最大值,然后返回它们之间的随机值。我们使用这个函数来添加一些随机的持续时间、重复延迟。

var tl = new TimelineMax(); 

接下来是GSAP的 TimelineMax 实例。这个时间轴是我们给每个光斑添加的单独的TweenMax补间。

for(var i = 0; i < 5; i++){    var t = TweenMax.to(document.querySelector('#blob' + i), randomBetween(14, 50), {     y:260,     repeat:-1,     repeatDelay:randomBetween(1, 3),     yoyo:true,     ease:Linear.easeNone   })    tl.add(t, (i+1)/0.6) }  tl.seek(100); 

在这里我们遍历这五个光斑(不包括底部的 botBlob ,因为这个是不移动的,就固定在底部),创建五个TweenMax实例,使用 document.querySelector 来获得每个光斑的id引用。

y:260 告诉每个光斑在Y轴方向移动到260的位置(即,向下移动)。

这凸显了关于GSAP的一个非常重要的点,它处理SVG路径的初始 XY 位置的方式。就GSAP而言,当你让一条路径从它的初始位置移动(即从AI中复制过来,粘贴的位置),它将会默认 XY 的位置为( 0,0 ),不论它是处在SVG画布的哪个位置。所以尽管光斑看起来是, X 差不多为 250Y 差不多为 120 ,它们都会被认为是在( 0,0 )的位置。这也就是说每个光斑都会相对它的初始粘贴位置向下移 260px

GSAP默认路径的 XY 的位置为( 0,0 ),不论它是显示在SVG画布上的哪个位置。

在该补间,我们也使用了 repeat:-1 来产生永动的up and down动画,我们还控制了每次重复之间的延迟。这是另一个随机值 repeatDelay:randomBetween(1, 3) 。我们加入了 yoyo:true ,这样它可以向下、然后向上、然后向下、然后向上,像一个悠悠球,而不是下到 260 的位置,然后跳回它的初始位置,然后又下去了。

最后我们使用了一个linear ease(不是ease in或者ease out),因为这看起来更自然。

在循环中我们还是给主时间轴TimelineMax实例( t1 )添加了TweenMax补间实例( t ),每个实例都按时添加了一个相等的距离,除了时间轴。

时间轴现在是无限长的,会循环重复到天荒地老。如果你在时间轴的起点处开始,它会需要一小会时间来让每个光斑开始移动,因为光斑并没有被告知要在同一时间都飘起来。它们的起始时间是错开的。

所以为了解决这个问题,我们告知时间轴一开始就跳转到约 100s 的地方,这样就确保了所有的光斑都是移动进行时:

tl.seek(100); 

以上就是需要的所有内容了。你的pen应该是如下所示的(希望没有出错):

给我点粘稠感啊!夫人!现在就要!

当你制作lego小汽车的时候,指导书总是会告诉你要把车轮留到最后。我偷用了这个想法,只是把“轮子”换成“粘稠”。

在codepen的HTML面板中,粘贴如下 <filter> 标签的内容到 <defs> 标签中(哪个位置随便你)。

 <filter id="goo">       <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur" />       <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 21 -9" result="cm" />     </filter> 

在我们把它应用到光斑小分组之前,这都是看不到的。找一下包裹光斑小分队的 <g> 标签在哪里,给它添加如下的属性。

filter="url(#goo)"  

现在你的标签应该看起来是长这样的:

<g clip-path="url(#glassMask)" filter="url(#goo)" > 

你的熔岩灯现在应该是有光斑的啦。

最后的SVG代码,臃肿肥胖的一大块,噢~

<svg version="1.1"   xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"   x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs>   <filter id="goo">    <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur" />    <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 21 -9" result="cm" />  </filter>   <clipPath id="glassMask"> <path id="glassShape" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18  C227.6,352.9,262,174,262,174z"/>     </clipPath> <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse">  <stop  offset="7.142857e-002" style="stop-color:#471A19"/>  <stop  offset="0.3107" style="stop-color:#290F0E"/>  <stop  offset="0.553" style="stop-color:#120706"/>  <stop  offset="0.7828" style="stop-color:#050202"/>  <stop  offset="0.9847" style="stop-color:#000000"/> </radialGradient> <radialGradient id="blob3Grad" cx="291.9382" cy="167.4587" r="41.0767" gradientUnits="userSpaceOnUse">  <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>  <stop  offset="0.1922" style="stop-color:#FA9712"/>  <stop  offset="0.3992" style="stop-color:#ED8A14"/>  <stop  offset="0.6186" style="stop-color:#D67316"/>  <stop  offset="0.8449" style="stop-color:#B65419"/>  <stop  offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="botBlob" cx="284.5" cy="421.5" r="53.521" gradientTransform="matrix(-2.802637e-002 -0.9996 5.9976 -0.1682 -2235.533 776.7669)" gradientUnits="userSpaceOnUse">  <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>  <stop  offset="0.1922" style="stop-color:#FA9712"/>  <stop  offset="0.3992" style="stop-color:#ED8A14"/>  <stop  offset="0.6186" style="stop-color:#D67316"/>  <stop  offset="0.8449" style="stop-color:#B65419"/>  <stop  offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob2_2_" cx="294" cy="157" r="23" gradientUnits="userSpaceOnUse">  <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>  <stop  offset="0.1922" style="stop-color:#FA9712"/>  <stop  offset="0.3992" style="stop-color:#ED8A14"/>  <stop  offset="0.6186" style="stop-color:#D67316"/>  <stop  offset="0.8449" style="stop-color:#B65419"/>  <stop  offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob1_2_" cx="297" cy="167.5" r="37.2156" gradientUnits="userSpaceOnUse">  <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>  <stop  offset="0.1922" style="stop-color:#FA9712"/>  <stop  offset="0.3992" style="stop-color:#ED8A14"/>  <stop  offset="0.6186" style="stop-color:#D67316"/>  <stop  offset="0.8449" style="stop-color:#B65419"/>  <stop  offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob0_2_" cx="292" cy="171.5" r="56.5354" gradientUnits="userSpaceOnUse">  <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>  <stop  offset="0.1922" style="stop-color:#FA9712"/>  <stop  offset="0.3992" style="stop-color:#ED8A14"/>  <stop  offset="0.6186" style="stop-color:#D67316"/>  <stop  offset="0.8449" style="stop-color:#B65419"/>  <stop  offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob4_2_" cx="306.5" cy="155" r="14.109" gradientUnits="userSpaceOnUse">  <stop  offset="1.020408e-002" style="stop-color:#FF9C12"/>  <stop  offset="0.1922" style="stop-color:#FA9712"/>  <stop  offset="0.3992" style="stop-color:#ED8A14"/>  <stop  offset="0.6186" style="stop-color:#D67316"/>  <stop  offset="0.8449" style="stop-color:#B65419"/>  <stop  offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <linearGradient id="lampTop_2_" gradientUnits="userSpaceOnUse" x1="292" y1="135" x2="292" y2="174">  <stop  offset="1.530612e-002" style="stop-color:#000000"/>  <stop  offset="0.233" style="stop-color:#050202"/>  <stop  offset="0.4808" style="stop-color:#120706"/>  <stop  offset="0.7421" style="stop-color:#290F0E"/>  <stop  offset="1" style="stop-color:#471A19"/> </linearGradient> <linearGradient id="lampBot_2_" gradientUnits="userSpaceOnUse" x1="292.375" y1="439" x2="292.375" y2="379">  <stop  offset="5.102041e-003" style="stop-color:#000000"/>  <stop  offset="0.2251" style="stop-color:#050202"/>  <stop  offset="0.4754" style="stop-color:#120706"/>  <stop  offset="0.7394" style="stop-color:#290F0E"/>  <stop  offset="1" style="stop-color:#471A19"/> </linearGradient> </defs> <rect fill="url(#bgGrad)" width="600" height="600"/>   <use xlink:href="#glassShape" fill="#EB7619" opacity="0.1"/>   <g class="blobGroup" filter="url(#goo)" clip-path="url(#glassMask)" > <path class="blob0 blob" fill="url(#blob0_2_)" d="M326.2,149.5c-5,19.2-21.4,29.2-37.8,26.6c-16.5-2.9-33.4-12.9-37.1-26.6  c-3.8-13.6,12.5-32.1,37.8-34.9C314.4,111.8,331.3,130.4,326.2,149.5z"/> <path class="blob1 blob" fill="url(#blob1_2_)" d="M320.5,146.4c-4.4,10.1-16.4,20.2-26.8,25.3c-10.4,5.2-22.4-2.9-26.8-15.2  c-4.4-11.6,7.6-20.4,26.8-25.3C312.9,126.3,324.9,135.6,320.5,146.4z"/> <path class="blob2 blob" fill="url(#blob2_2_)" d="M278,147.7c2.7-7.1,9.4-15.7,15.4-16.4c5.9-0.4,12.6,8.5,15.4,16.9  c2.7,8.4-4.2,14.9-15.4,14.2C282.2,161.5,275.3,154.8,278,147.7z"/> <path class="botBlob" fill="url(#botBlob)" d="M354,381.2c6.8,3.4,5.4,7.4-5.6,10.4c-10.7,3.1-31.1,5.1-54.4,8.4  c-23.3,3.3-43.7,0.8-54.4-2.4c-11-3.4-12.4-7.6-5.6-13.8c6.8-7,18.9-14.6,29.6-17.4c11-3.3,20.6-1.8,30.4-1.4  c9.8,0.4,19.4,5.1,30.4,8.3C335.1,376.8,347.2,378.6,354,381.2z"/> <path class="blob3 blob" fill="url(#blob3Grad)" d="M312.7,147.3c-2.1,16.4-15.3,27.2-23.2,25.3c-8.1-1.8-12.6-13-14.8-24.9  c-1.9-11.8,2.7-22.7,14.8-25.3C301.5,119.6,314.7,130.8,312.7,147.3z"  /> <path class="blob4 blob" fill="url(#blob4_2_)" d="M317.8,147.4c-1,8.2-9.8,10.3-13.8,9.3c-4-0.9-6.5-3-7.6-8.9c-1-5.9,2.3-8.5,8.4-9.8  C310.8,136.6,318.8,139.1,317.8,147.4z"/>      </g> <polygon id="lampTop" fill="url(#lampTop_2_)" points="269,135 262,174 322,174 316,135 "/>   <path id="lampBot" fill="url(#lampBot_2_)" d="M226.8,379.5c2.6,42.8,23.9,54.2,28.3,59.8c3.3,5.4-10,30.8-10,30.8h95.5  c0,0-16.5-25-14.5-30.8s26-15.6,32-60.1C328,379.1,240.3,379.5,226.8,379.5z"/>  <rect y="470" width="600" height="130"/> </svg>  

我自己过了一遍这个教程,然后创建了如下的pen(这是我那个原始版本的微微简略一点的版本,但是它们长得是一模一样的)。

出于让这些pen在这里看起来更好看的目的,我给 body 加了一点CSS样式,把滚动条去掉了。

body {   background-color:#000;   overflow: hidden; } 

作为一点点额外的补充,如果你对光斑移动的时间不满意,可以尝试在 seek(); 一行之后添加一个 TimelineMax.timeScale(); ,如下:

tl.timeScale(12); 

这会使得光斑以它们正常速度的 12 倍跑起来,颤抖吧骚年!

所以现在你完成这个熔岩灯了——我们已经讲解了相当多的内容了,从AI的提示和小技巧,到SVG滤镜和分组、使用 <use> 重用元素,到改变整个GSAP动画的时间。值得欣慰的是代码非常少,因为GSAP真的强大到没朋友。

你可以点击 这里 下载熔岩灯的AI格式文件。

现在休息一下,然后去准备你的新熔岩灯吧——你可以的!

扩展阅读

  • Seven Stages of SVG
  • My First SVG Banner Ad

本文根据 @Chris Gannon 的《 How To Make An SVG Lava Lamp 》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处: http://codepen.io/chrisgannon/post/how-to-make-an-svg-lava-lamp 。

手把手教你做一个SVG Lava Lamp

彦子

在校学生,本科计算机专业。逗比一枚,热爱前端热爱生活,喜欢CSS喜欢JavaScript喜欢SVG,爱玩PS玩AI玩啊逗比的软件。努力向上,厚积薄发。

正文到此结束
Loading...