一、源起

今年的开发者大会上,Google 发表了 Android L 和跨屏统一 UI 风格—— Material Design。

这个承载着 Google 统一大愿的设计语言,恰恰给一味扁平化的 Designers 敲了一记闷棍:

一切都仿佛变成了色块加文字。哪个是按钮?哪个不是?哪些是重要的信息?哪些不是?当扁平化的风格让信息层级也扁平化,影响了「信息的正确沟通」这个最终目的时,我们知道这样的扁平化已经跳得太远。

所以 Google 选择了往回跳一小步。在扁平化的基础上,保留了物理世界的阴影和动作。

Material Design 等于是强调了物理定律的扁平化设计。 Google is trying to bringing physicalityinto Flat Design.

——Material Design:向拟物回跳一下的扁平化设计 - 笔戈科技 - 知乎专栏

偶然有一天,后端同学给我发了 Polymer Project 这个网站,第一次看到 Material Design 的前端组件化实现的我惊艳不已,尤其是 Button Click 时的水波纹效果 的那种交互效果,简直让人感动到想哭 (笑)。

于是按捺不住激动,便开始考虑自己实(chao)现(xi)一个类似的效果。

二、成果

国际惯例,先晒成果:

See the Pen Android L Ripple Effect by Dolphin Wood (@idiotWu) on CodePen.

三、设计思路

1. 得到波纹坐标

分析 Material Design 的 Button Ripple Effect,不难发现波纹的发出点(或者说中心点)正是鼠标点按的位置。所以第一步就是得到这个坐标:

// 博主制图课没好好上……

如图所示,波纹的中心应该与鼠标位置重合,所以波纹的坐标就变为:

top:鼠标垂直位置 - 波纹高度 / 2;

left: 鼠标水平位置 - 波纹宽度 / 2;

代码实现如下:

// 为了代码简洁,文中使用 jQuery 库

$('body').on('click', function (e) {
    var x = e.pageX;
    var y = e.pageY;
    var $ripple = $('<span class="ripple"></span>');
    $ripple.appendTo(this).css({
        left: x - $ripple.width() / 2,
        top: y - $ripple.height() / 2
    });
});

丧心病狂的 Demo

但是这样还不足以完成我们的目的,于是:

2. 将波纹定位到按钮内部

要使波纹产生在按钮内部,这时波纹的定位就是应该相对按钮的 position: absolute,所以这时的坐标应为:

top:鼠标垂直位置 - 按钮垂直偏移 - 波纹高度 / 2;

left: 鼠标水平位置 - 按钮水平偏移 - 波纹宽度 / 2;

代码实现如下:

$('button').on('click', function (e) {
    var offset = $(this).offset();
    var x = e.pageX;
    var y = e.pageY;
    var $ripple = $('<span class="ripple"></span>');
    $ripple.appendTo(this).css({
        left: x - offset.left - $ripple.width() / 2,
        top: y - offset.top - $ripple.height() / 2
    });
});

3. 波纹效果

这种事情当然是交给 CSS3 Animation 嘛!

@keyframes ripple {
  to {
    transform: scale(2);
    opacity: 0;
  }
}
.ripple {
  position: absolute;
  background: rgba(0,0,0,.15);
  border-radius: 100%;
  transform: scale(0);
  pointer-events: none;
  animation: ripple .75s ease-out;
}

如您所见,就是这么简单!

4. 尽善尽美

刚才的代码,已经能实现我们需要的波纹效果了,但是实际中我们还应该考虑两个问题:

  1. 重复添加 ripple 元素;

  2. 页面滚动的情况 // 注:jQuery 的 .offset() 方法已经包含了滚动条偏移量

于是最后就有了您在 Codepen Embed 里看到的代码了。

(全文完)