用SVG过滤器制作超级英雄动画效果

271 阅读10分钟

几十年来,超级英雄的故事一直吸引着我们--从漫画书中的奇妙世界和人物,到电影中帮助他们获得生命的壮观视觉效果。在本教程中,我们将从中获得灵感,学习如何为网络创建和制作类似超级英雄的插图效果。当然,我们的浏览器中没有好莱坞大制片厂的超级强大的工具,但不用担心,SVG滤镜和遮罩将为我们提供帮助!我们将学习如何重新创建超级英雄的效果。💪

我们将学习如何重新创造这种很酷的变异效果。如果你不熟悉她的故事,拉文-达科姆,也就是众所周知的魔形女,是《X战警》系列电影中的一个变形者角色。她有一个天然的海军蓝皮肤,可以操纵她的身体,变成她想变成的任何一个人。这当然是一种有用的能力,可用于伪装逃跑,她经常这样做。因此,为了给我们的项目增加一个耐人寻味的背景故事,她被描绘成一张经典风格的通缉海报。

为了准备插图,我们将使用Inkscape 1.1,一个免费的开源矢量绘图编辑器,你可以在这里下载。你可以使用你喜欢的任何其他矢量绘图编辑器。只是要记住,步骤很可能会有所不同。你还需要有至少中级水平的HTML、CSS和JavaScript知识,并熟悉GSAP,我们将使用的动画库。

在我们开始之前,还有一件事

从目前来看,SVG动画过滤器在Chrome中的效果通常比其他浏览器好。请记住,它们也会大大降低页面的速度。SVG过滤器很方便,也很容易使用,但是如果对性能有顾虑的话,可以考虑使用WebGL,因为它是经过GPU优化的。Codrops有很多关于它的资源

如何为网络准备一个矢量插图

Inkscape是一个强大的矢量图形编辑器,充满了插图和数字艺术的高级功能。另外,它是一个方便的工具,可以用可视化的方式处理SVG,而不是从头开始写所有的代码😅 。在本教程中,我们不会真正详细介绍Inkscape的插画过程,但你可以从官方文档中了解更多信息

创建图画

好了,让我们开始吧。按照Inkscape的快速设置指南,它将让你进入一个空的画布界面。前往File > Document Properties ,以便设置页面大小和单位。使用像素而不是毫米或英寸是很重要的,因为在网络上使用像素要容易得多。

Screen capture showing Inkscape's main screen. Document properties are open on the left with units set to pixels. The document is blank.

Inkscape主屏幕上的文档属性设置为像素单位。

从这里开始,你需要做的就是使用Inkscape的工具创建你的插图,使用形状和曲线绘制一切,或者导入其他图像在屏幕上构图。特别是这幅插图,我在iPad上用Procreate画出线条,然后用File > Import 将它们作为PNG导入Inkscape。然后,我用Path > Trace Bitmap ,从PNG生成高质量的矢量图,并通过手动描摹线条后面的基本形状来着色。最后的结果是由4个不同的部分组成:背景、前景、自然蓝色形态的神秘人和转变为人类形态的神秘人。

 All the different parts of the final composition. From left to right, first is the background with a textured effect. Second, a drawing of a disguised mystique as a blonde woman. Third, mystique in her natural blue form. Fourth is the foreground, a poster with the words "Have you seen this woman?" at the top, a blank space in the middle and "Raven Darkhölme" at the bottom.

最终构图的不同部分。

导出一个优化的SVG

一旦你的插图完成了,就该导出SVG了。构图中的每一个部分都应该放在自己的文件中,因为它们将在页面上组合在一起。⚠ 注意。在网络上使用Inkscape或你喜欢的编辑器的原始格式并不是一个好主意。这些格式通常会在输出中添加浏览器无法解释的额外数据,最终只会使文件不必要地变大。

幸运的是,Inkscape有一个超级方便的File > Save as... > Optimized SVG 选项,可以产生一个小得多的SVG。在导出面板上,勾选SVG Output > Enable viewboxing ,以使图像更容易用CSS定位,这一点很重要。其余的默认选项都很好,但我鼓励你也用它们做一些试验。

Inkscape optimized svg output options panel. All settings are set to their respective default values, except for "Enable viewboxing", which is focused and checked.

优化的SVG输出选项。

如果你真的很想优化你的资产,使用SVGO是必须的。在超级整洁的网站SVGOMG上,它可以作为一个命令行工具和网络界面。再次,我建议你启用Prefer viewBox to width/height ,以使定位和尺寸更加灵活。如果你打算手动编辑导出的代码,我也推荐你使用Prettify markup ,它能使输出的代码更易读。

标记和风格

让我们从一个非常简单的HTML5页面结构开始,其中有index.htmlstyle.cssindex.js 文件和一个images 目录来存储所有导出的SVG。从一个非常简单的HTML5页面开始,我们在头部导入我们的样式表,在底部导入脚本。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mystique</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <svg class="picture" viewBox="0 0 196 296">
        <rect id="bg-color" x="0" y="0" width="100%" height="100%"
						fill="#4a5eb2"/>
        <image id="background" x="0" y="0" width="100%" height="100%"
            href="images/background.svg"/>
        <image id="transformed" x="0" y="0" width="100%" height="100%"
            href="images/transformed.svg"/>
        <image id="mystique" x="0" y="0" width="100%" height="100%"
            href="images/mystique.svg"/>
        <image id="foreground" x="0" y="0" width="100%" height="100%"
            href="images/foreground.svg"/>
    </svg>
		<script src="index.js"></script>
</body>
</html>

在正文中,我们有一个单一的SVG元素,viewBox=“0 0 196 296” 。SVG文件的viewBox参数定义了图片的裁剪区域,前两个数字是左上角的点坐标,最后两个数字分别是宽度和高度。我们必须使用我们在Inkscape中定义的相同尺寸。接下来,我们使用<image> 标签导入图片,每张图片都定位在左上角(x=“0” y=“0”),并填充整个视口(width=“100%” height=“100%”)。在所有的东西后面,我们有一个rect ,上面有我们想要的背景颜色。重要的是要给每个元素一个自己的ID,以便组织。

样式设计很简单,一个带有display: grid的全页面容器帮助我们用place-self: center ,使图片对准中心。为了将所有的东西集中在一起并最终完成构图,我们在背景上添加一个紫色的渐变和一个阴影。

html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;
}

body {
  display: grid;
  background: radial-gradient(at top, #5e4082, #3a124d);
  padding: 1rem;
  box-sizing: border-box;
}

.picture {
  place-self: center;
  max-width: 100%;
  height: 80vh;
  filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.5));
}

结果应该与下图相似。两张图片相互重叠,但这没关系,我们将使用遮罩来创造滑动效果。

Rendered result of the HTML page in the browser. Each part is layered on top of another, with blue mystique juxtaposed on top of blonde mystique.

这个页面在浏览器中应该是这样的。

设置SVG蒙版🎭

SVG遮罩是一种元素,它根据遮罩内的颜色值信息来定义被遮罩对象的透明度。如果遮罩的一个像素是白色的,那么被遮罩对象的相应像素将是可见的;如果是灰色的,它将是部分透明的;万一是黑色的,它将是完全透明的。遮罩区域外的一切也将是完全透明的。希望下面的图能帮助你更好地理解蒙版的工作原理。

An illustration of how SVG masks work. There are 3 pictures: the first one depicts the photograph of a bird against a blurred natural background; the second shows a masks, with the bird contour in white and the background in pitch black; the third shows the mask applied to the original image, the bird is perfectly cropped from the background, which is now transparent.

遮罩的工作原理是根据每个像素的颜色值来定义它们的透明度。图片来源。维基共享资源。

为了定义遮罩,我们在SVG的顶部添加一个<defs> 元素,里面有两个<mask> 标签。一个将用于遮蔽mystique(id="mask_mystique" ),另一个将用于她的变换版本(id="mask_transformed" )。每个遮罩只包含一个简单的白色矩形,其大小足以覆盖整个可见区域。然后,我们使用mask="url(#mask_id)" 属性,将遮罩应用于其相应的元素。

<svg class="picture" viewBox="0 0 196 296">
    <defs>
        <mask id="mask_mystique">
             <rect class="mask" x="0" y="-100%" width="100%" height="100%" fill="white"/>
	</mask>
	<mask id="mask_transformed">
             <rect class="mask" x="0" y="0" width="100%" height="100%" fill="white"/>
	</mask>
    </defs>
    <rect id="bg-color" x="0" y="0" width="100%" height="100%" fill="#4a5eb2"/>
    <image id="background" x="0" y="0" width="100%" height="100%" href="images/background.svg"/>
    <image id="transformed" x="0" y="0" width="100%" height="100%" mask="url(#mask_transformed)" href="images/transformed.svg"/>
    <image id="mystique" x="0" y="0" width="100%" height="100%" mask="url(#mask_mystique)" href="images/mystique.svg"/>
    <image id="foreground" x="0" y="0" width="100%" height="100%" href="images/foreground.svg"/>
</svg>

Mystique的遮罩被定位在y="-100%" ,这意味着它在视口之外和之上,因此,她将是完全透明的。同时,转变后的Mystique的遮罩以白色覆盖**整个视口,**这意味着她将是完全可见的。如果我们把两个面具都移到底部50%,产生的效果将是两个版本的Mystique都在屏幕上部分显示,就像下面的插图。

An image of a half transformed mystique. From the middle to the top, she's in her blue form, from the middle to the bottom, she's in her transformed, blonde form. There are labels indicating the position of the masks, mask_mystique_area at the top and mask_transformed_area at the bottom.

Mystique的面具被定位在y="-100%" ,这意味着它在视口之外和之上,因此,她将是完全透明的。同时,转变后的Mystique的遮罩以白色覆盖**整个视口,**这意味着她将完全可见。如果我们把两个面具都移到底部50%,产生的效果将是两个版本的Mystique都在屏幕上部分显示,就像下面的插图。

添加视觉效果✨

SVG滤镜是一种超级强大的工具,可以为我们无聊的简单SVG增加一些酷感。有很多不同的过滤器选项不在本教程的范围内,但如果你想阅读更多关于它们的内容,Codrops有很多教程详细解释了每一种过滤器。

现在,让我们继续讨论代码。在我们为遮罩创建的同一个<defs> 标签内,我们添加一个<filter id="distort">

<filter id="distort">
    <feTurbulence
        type="turbulence"
        baseFrequency="0.08"
        numOctaves="2"
        result="turbulence"
    />
    <feDisplacementMap
        in2="turbulence"
        in="SourceGraphic"
        scale="50"
    />
</filter>

滤波器是由基元组成的,这些基元也可以堆叠和组合在一起。为了创建 Mystique 的酷炫转换效果,我们要使用feTurbulence 基元,它可以生成一个 Perlin 噪声模式。feTurbulence 的属性type 控制生成的湍流类型,baseFrequency 控制其大小,numOctaves 控制其粗糙度。然后,我们用result="turbulence" 命名输出。

接下来我们要使用feTurbulence 的输出,并将其与SourceGraphic (应用了过滤器的对象的像素)结合成一个feDisplacementMap 基元。这个基元使用in2 的颜色信息对其in 参数的内容进行变形。scale 控制变形的长度。

📝 提示:我强烈建议你玩玩Yoksel的SVG过滤器游戏,如果你想看看这些参数中的每一个是如何影响基元的输出的🤗

我们接下来要做的就是把滤波器应用到两个蒙版上。由于遮罩的矩形将被动画化,为了创造流动的变换,我们要将滤波器添加到一个包装组<g> 。否则,滤镜的变形会随着遮罩的静态移动而移动,这样看起来就没有那么酷了😢

如果我们不添加组,动画会是什么样子。

作为本节的总结,下面是最终的SVG应该是什么样子。

<svg class="picture" viewBox="0 0 196 296">
    <defs>
        <filter id="distort">
            <feTurbulence
                type="turbulence"
                baseFrequency="0.08"
                numOctaves="2"
                result="turbulence"
            />
            <feDisplacementMap
                in2="turbulence"
                in="SourceGraphic"
                scale="50"
            />
        </filter>
        <mask id="mask_mystique">
            <g filter="url(#distort)">
                <rect class="mask" x="0" y="-100%" width="100%" height="100%" fill="white"/>
            </g>
        </mask>
        <mask id="mask_transformed">
            <g filter="url(#distort)">
                <rect class="mask" x="0" y="0" width="100%" height="100%" fill="white"/>
            </g>
        </mask>
    </defs>
    <rect id="bg-color" x="0" y="0" width="100%" height="100%" fill="#4a5eb2"/>
    <image id="background" x="0" y="0" width="100%" height="100%" href="images/background.svg"/>
    <image id="transformed" x="0" y="0" width="100%" height="100%" mask="url(#mask_transformed)" href="images/transformed.svg"/>
    <image id="mystique" x="0" y="0" width="100%" height="100%" mask="url(#mask_mystique)" href="images/mystique.svg"/>
    <image id="foreground" x="0" y="0" width="100%" height="100%" href="images/foreground.svg"/>
</svg>

用GSAP做动画

GSAP是一个了不起的用于网页动画的JavaScript库。它可以完美地处理从简单到复杂的动画,其精简的API可以顺利地完成工作。我们将在我们的项目中使用它来制作面具矩形的动画。如果你想知道更多关于如何使用GSAP的功能,请查看这里的官方文档。

import gsap from "gsap"

const tl = gsap.timeline({
  repeat: -1, // Makes animation repeat infinitely
  yoyo: true, // Animation will go back-and-forth like a yoyo
})

tl
  .to('.mask', {
    translateY: '100%', // Move .mask elements down by 100%
    duration: 3,
  })
  .to('#bg-color', {
    attr: {
      fill: '#ffd11b' // Change the "fill" attribute of #bg-color
    },
    duration: 2
  }, '<+=1') // Start 1s after the previous animation

看一下代码,首先我们导入GSAP并创建一个来回重复的时间线。接下来,我们通过在Y(垂直)轴上平移遮罩来制作动画,并将#bg-color 元素的颜色改为#ffd11b 黄色。结果应该看起来像介绍中的演示。

超级酷但是,加入一些互动性,使动画跟随鼠标移动呢?鼠标移动事件在网络上是很难处理的,因为它们被频繁触发,迫使浏览器计算动画的次数太多,😰 。我们可以使用lodash.throttle 来帮助我们确保鼠标移动处理程序不会以太快的速度被触发。如果你对它感到满意,你也可以使用本地的requestAnimationFrame 函数,它非常适合用于节制像这样昂贵的动画。

import gsap from "gsap"

// We'll use throttle to make sure the mousemove event
// doesn't trigger too often
import { throttle } from "lodash"

const tl = gsap.timeline({
  paused: true, // Start at a paused state
  defaults: {
    ease: 'none' // With no easing (linear)
  }
})

tl
  .to('.mask', {
    translateY: 296,
    duration: 3,
  })
  .to('#bg-color', {
    attr: {
      fill: '#ffd11b'
    },
    duration: 2
  }, '<+=1')

const $picture = document.querySelector('.picture')

// Mouse move handler
function handleMoveEvent(ev) {
  // Get the viewport rectangle of the picture
  const rect = $picture.getBoundingClientRect();

  // Compute the relative mouse position inside the rect
  const relPos = (ev.clientY - rect.top) / (rect.bottom - rect.top)

  // Use the computed value to control the animation progress
  tl.progress(relPos)
}

// Bind the mousemove event, with a throttle to ensure it only
// triggers once at every 60 milliseconds
$picture.addEventListener(
  'mousemove', throttle(handleMoveEvent, 60)
)

这应该是这个项目的最终结果,一个完整的、互动的、超酷的SVG超级英雄动画我希望你喜欢它,并且了解了一些关于SVG的超能力😆

The postSuperhero Animation Effect with SVG Filtersappeared first onCodrops.