使用 CSS 和 jQuery 来做一个墨水晕开的效果

阅读 3388
收藏 92
2016-04-19
原文链接:github.com

一个用 CSS 动画实现的墨水晕开过渡效果。

我最近遇到有几个网站使用墨水晕开作为过渡效果。 一个很好的例子是 Sevenhills website。起初我以为他们使用 HTML canvas 来实现(允许透明度), 然后我查看源代码发现他们并没有使用视频,而是一个 PNG 雪碧图。

通过用一个 PNG 雪碧图和 CSS 中的 steps() 定时方法,我们能创建视频效果并使用它们作为过渡。 在我们的方法中, 我们使用这种手段去触发一个模态窗口,但你也能使用它作为两个页面之间的过渡效果。

创建这些效果的过程很简单,让我来给你详细分解:

首先,你需要一个有填充效果的视频和一个透明区域。 然后你需要把这个视频导出为 PNG 序列。我们使用 After Effects 导出这个队列(确保导出 alpha 通道)。

ae-01

因为我们的视频由25帧组成,导出 25 张 PNG 图片资源。 只是为了给你更好设置组成的更多信息, 我们创建了一个宽高为 640x360px 帧率为 25,时长为 1 秒的视频。

ae-02

最后乏味的部分:你需要创建一个将所有帧包含在同一行的 PNG 图片。我们手动在 Photoshop 中将所有帧组合在一个 16000×360 像素的图片中。

png-sequence-preview

为了将序列变成一个视频,我们只需要平移这个 PNG 雪碧图,然后使用 steps() 方法定义帧的数目。

你想学习更多关于 CSS 变换和动画的相关内容吗?查看我们的 课程 ;)

现在让我们进入代码!

创建结构

HTML 结构 由三个元素组成:一个 main.cd-main-content 容纳页面主要内容,一个 div.cd-modal 容纳一个模态窗口和一个 div.cd-transition-layer 包含过渡层。

Ink Transition Effect

Start Effect

My Modal Content

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad modi repellendus, optio eveniet eligendi molestiae? Fugiat, temporibus!

Close

增加样式

.cd-modal 窗口最初的CSS属性 visibility: hidden, height: 100% 和 width: 100% 并且使用固定定位。 当用户点击 a.cd-modal-trigger,模态窗口变为可见,并且它的透明度变为 1 (使用 .visible 类)。

.cd-modal {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 3;
  height: 100%;
  width: 100%;
  opacity: 0;
  visibility: hidden;
}
.cd-modal.visible {
  opacity: 1;
  visibility: visible;
}

div.cd-transition-layer 元素用来创建墨水过渡效果:visibility: hidden,height: 100% 和 width: 100% 并且使用固定定位。

.cd-transition-layer {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 2;
  height: 100%;
  width: 100%;
  opacity: 0;
  visibility: hidden;
  overflow: hidden;
}

它的子元素 div.bg-layer 使用 ink.png 雪碧图作为背景, background-size: 100%, height: 100% 和 width: 2500% (ink.png 雪碧图 由 25 帧组成);它的 left/top/translate 值设置为最初 ink.png 雪碧图第一帧在 div.cd-transition-layer居中:

.cd-transition-layer .bg-layer {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translateY(-50%) translateX(-2%);
  height: 100%;
  /* our sprite is composed of 25 frames */
  width: 2500%;
  background: url(../img/ink.png) no-repeat 0 0;
  background-size: 100% 100%;
}

你可能使用以下方式在父元素中居中一个元素:

position: absolute;
left: 50%;
top: 50%;
transform: translateY(-50%) translateX(-50%);

在我们的例子中,虽然我们想要居中 ink.png 雪碧图的第一帧,因为  div.bg-layer  宽度为父元素宽度的 25 倍,我们可以使用 translateX(-(50/25)%)。

为了创建墨水动画,我们改变 div.bg-layer 的 translate 值; 我们定义 cd-sequence 关键帧规则:

@keyframes cd-sequence {
  0% {
    transform: translateY(-50%) translateX(-2%);
  }
  100% {
    transform: translateY(-50%) translateX(-98%);
  }
}

这样,在动画的最后,ink.png 雪碧图将在 div.cd-transition-layer 元素内呈现。

记住:因为我们有25帧,展示最后一帧你需要把 translate 设置为 .bg-layer of -100% * (25 – 1) = -96%;但另外,基于它的父元素居中, 你需要额外增加 -2%。

当用户点击 a.cd-modal-trigger.visible 添加到  .cd-transition-layer 上而显示它,当 .opening 类来触发墨水动画:

.cd-transition-layer.visible {
  opacity: 1;
  visibility: visible;
}
.cd-transition-layer.opening .bg-layer {
  animation: cd-sprite 0.8s steps(24);
  animation-fill-mode: forwards;
}

然后我们使用 steps() 方法: 因为不想不断地修改 translate 值,而是通过固定的步调来改变以一次显示一帧; 步数比我们的帧数少一。

事件处理

当用户点击 a.cd-modal-trigger.modal-close 打开/关闭 模态窗口,我们使用 jQuery 增加/移除类。

另外,为了不修改帧的宽高比, 我们改变 .bg-layer 的尺寸。 在 style.css 文件中,我们设置 .bg-layer 高度和宽度使帧的宽高等于一个视口宽高。视口和帧可能拥有不同的宽高比而导致帧的扭曲。  setLayerDimensions() 方法防止这种情况的发生:

var frameProportion = 1.78, //png frame aspect ratio
    frames = 25, //number of png frames
    resize = false;

//set transitionBackground dimentions
setLayerDimensions();
$(window).on('resize', function(){
    if( !resize ) {
        resize = true;
        (!window.requestAnimationFrame) ? setTimeout(setLayerDimensions, 300) : window.requestAnimationFrame(setLayerDimensions);
    }
});

function setLayerDimensions() {
    var windowWidth = $(window).width(),
        windowHeight = $(window).height(),
        layerHeight, layerWidth;

    if( windowWidth/windowHeight > frameProportion ) {
        layerWidth = windowWidth;
        layerHeight = layerWidth/frameProportion;
    } else {
        layerHeight = windowHeight;
        layerWidth = layerHeight*frameProportion;
    }

    transitionBackground.css({
        'width': layerWidth*frames+'px',
        'height': layerHeight+'px',
    });

    resize = false;
}
评论