打造炫酷溶解过渡效果

Avatar of Yehonatan Daniv
Yehonatan Daniv

DigitalOcean 为您旅程的各个阶段提供云产品。立即开始使用 $200 免费信用额度!

我们将创建一个令人印象深刻的图像之间过渡效果,我敢说,这种效果非常简单,可以轻松地实现并应用于任何网站。我们将使用 kampos 库,因为它非常擅长做我们需要的精确工作。我们还会探讨一些调整结果的可能方法,以便您可以根据自己的需求对其进行个性化调整,并将其调整到您正在创建的体验和印象。

只需浏览一下 Awwwards 过渡效果合集,您就会感受到实现沉浸式效果,例如将一个媒体项目转换为另一个媒体项目,是多么受欢迎。这些示例中的许多都使用 WebGL 来完成此工作。另一个共同点是使用 **纹理映射** 来实现位移或溶解效果(或两者兼而有之)。

要实现这些效果,您需要 **两个** 要进行过渡的媒体源,即 **从** 和 **到**,再加上一个 **映射**,或者每个像素的值网格,该网格决定何时以及媒体以何种程度从一个图像切换到另一个图像。该映射可以是预制的图像,也可以是可绘制的 <canvas>,例如 噪声。通过应用噪声作为映射来使用溶解过渡效果,绝对是那些可以增强沉浸式网络体验的事情之一。这就是我们追求的目标。

设置场景

在我们开始使用重型机械之前,我们需要一个简单的 DOM 场景。两个图像(或者如果您愿意,也可以是视频),以及使它们加载并准备进行操作所需的最少 JavaScript 代码。

<main>
  <section>
    <figure>
      <canvas id="target">
        <img id="source-from" src="path/to/first.jpg" alt="My first image" />
        <img id="source-to" data-src="path/to/second.jpg" alt="My second image" />
      </canvas>
    <figure>
  </section>
</main>

这将为我们提供一些最小的 DOM 以进行操作和显示我们的场景。舞台已准备就绪;现在让我们邀请我们的主要演员,这两张图像

// Notify when our images are ready
function loadImage (src) {
  return new Promise(resolve => {
    const img = new Image();
    img.onload = function () {
      resolve(this);
    };
    img.src = src;
  });
}
// Get the image URLs
const imageFromSrc = document.querySelector('#source-from').src;
const imageToSrc = document.querySelector('#source-to').dataset.src;
// Load images  and keep their promises so we know when to start
const promisedImages = [
  loadImage(imageFromSrc),
  loadImage(imageToSrc)
];

创建溶解映射

场景已设置,图像已获取,让我们开始变魔法吧!首先,我们通过创建一些噪声来创建所需的效果。我们将在湍流效果中使用 经典的 Perlin 噪声,这种效果将不同尺度的噪声叠加在一起,一个叠加在另一个之上,并将噪声渲染到 <canvas> 上,以灰度显示

const turbulence = kampos.effects.turbulence({ noise: kampos.noise.perlinNoise });

这种效果类似于 SVG feTurbulence 滤镜效果。在 Bence Szabó 的 “使用 SVG 滤镜创建图案” 中有一些很好的示例。

其次,我们设置湍流效果的初始参数。这些参数可以在以后进行调整,以便为每种情况获取所需的特定视觉效果

// Depending of course on the size of the target canvas
const WIDTH = 854;
const HEIGHT = 480;
const CELL_FACTOR = 2;
const AMPLITUDE = CELL_FACTOR / WIDTH;

turbulence.frequency = {x: AMPLITUDE, y: AMPLITUDE};
turbulence.octaves = 1;
turbulence.isFractal = true;

这段代码为我们提供了类似液体或带状的噪声纹理。生成的过渡看起来像是第一个图像正在沉入第二个图像中。CELL_FACTOR 值可以增加,以创建具有更小带状的更密集纹理,而 octaves=1 则使噪声保持带状。请注意,我们还将振幅归一化到至少媒体的较长边,以便纹理可以很好地拉伸到我们的图像上。

接下来,我们渲染溶解映射。为了能够看到结果,我们将暂时使用 DOM 中现有的画布,仅供参考

const mapTarget = document.querySelector('#target'); // instead of document.createElement('canvas');
mapTarget.width = WIDTH;
mapTarget.height = HEIGHT;

const dissolveMap = new kampos.Kampos({
  target: mapTarget,
  effects: [turbulence],
  noSource: true
});
dissolveMap.draw();

暂停

我们将在这里暂停并检查更改上述参数如何影响视觉效果。现在,让我们调整一些噪声配置,以获得更像烟雾而不是液体的效果,例如

const CELL_FACTOR = 4; // instead of 2

以及

turbulence.octaves = 8; // instead of 1

现在,我们拥有一个更密集的图案,它叠加了八个级别(而不是一个级别),从而提供了更多的细节

太棒了!现在回到原始值,并转向我们的主要功能……

创建过渡效果

是时候创建过渡效果了

const dissolve = kampos.transitions.dissolve();
dissolve.map = mapTarget;
dissolve.high = 0.03; // for liquid-like effect

请注意 high 的上述值?这对于获得类似液体的效果至关重要。过渡使用阶跃函数来确定是显示第一个媒体还是第二个媒体。在此阶跃期间,过渡以平滑的方式完成,因此我们获得的是软边而不是锯齿状边。但是,我们将阶跃的低端边保持在 0.0(默认值)。您可以想象从 0.00.03 的过渡非常突然,导致从一个媒体到另一个媒体的快速变化。将其视为裁剪。

另一方面,如果范围是 0.00.5,我们将获得更广泛的“透明度”范围,或者两个图像的混合,就像我们使用部分不透明度获得的效果一样,并且我们将获得烟雾状或“云雾”效果。我们将在稍后尝试一下。

在我们继续之前,必须记住用从 DOM 创建的新画布替换我们从文档中获得的画布,如下所示

const mapTarget = document.createElement('canvas');

连接它,然后……开始吧!

我们快完成了!让我们创建我们的合成器实例

const target = document.querySelector('#target');
const hippo = new kampos.Kampos({target, effects: [dissolve]});

最后,获取图像并播放过渡效果

Promise.all(promisedImages).then(([fromImage, toImage]) => {
  hippo.setSource({media: fromImage, width, height});
  dissolve.to = toImage;
  hippo.play(time => {
    // a sin() to play in a loop
    dissolve.progress = Math.abs(Math.sin(time * 4e-4)); // multiply time by a factor to slow it down a bit
  });
});

太棒了!

特殊效果

好的,我们获得了那种带状效果。我们可以尝试稍微调整一下参数,以获得截然不同的效果。例如,可能更像烟雾的效果

const CELL_FACTOR = 4;
turbulence.octaves = 8;

为了获得更平滑的过渡,我们将提高过渡阶跃函数的较高边缘

dissolve.high = 0.3;

现在我们有了这个

额外特殊效果

最后,我们再来一个反转,让噪声本身也动起来!首先,我们需要确保 kampos 会在每一帧都更新溶解映射纹理,这是它默认情况下不会做的事情

dissolve.textures[1].update = true;

然后,在每一帧中,我们想要推进湍流的 time 属性,并重新绘制它。我们还会减慢过渡速度,以便在过渡发生时看到噪声变化

hippo.play(time => {
  turbulence.time = time * 2;
  dissolveMap.draw();
  // Notice that the time factor is smaller here
  dissolve.progress = Math.abs(Math.sin(time * 2e-4));
});

最后我们得到这个

就是这样!

退出……舞台右方

这只是我们使用 kampos 实现媒体过渡效果的一个示例。现在就取决于您将这些成分混合在一起,以最大限度地发挥其作用。以下是一些让您开始的想法

  • 在网站/部分背景之间进行过渡
  • 在图像轮播的背景之间进行过渡
  • 响应点击或悬停更改背景
  • 在视频开始播放时从视频中删除自定义海报图像

无论您做什么,请务必在评论中告诉我们。