Serenader

Learning by sharing

使用 CSS 实现 Material Design 的 Ripple Click Effect

什么是 Google Material Design ?

Click to run embed content:
https://www.youtube.com/embed/Q8TXgCzxEnw?feature=oembed

Ripple Click Effect in Web

Material Design 确实很不错。风格清新动画流畅,很吸引人。其中最吸引我的是它的 Ripple Click Effect。自己在想,是否可以通过 CSS 和 JavaScript 来实现这种效果呢?答案当然是可以的!

HOW ?

从上面的视频可以看出,当用户点击一个按钮的时候,按钮上方会有一段短暂的波纹动画,波纹从用户点击的那个地方开始,半径从零开始扩散变大,而且波纹的颜色也在渐渐的变淡,最后消失。
把这个需求放在 Web 设计中,要如何设计呢?

点击时的坐标

要得到用户点击时的坐标并不难。我们可以使用 JavasScript 直接调用 event.pageX 和 event.pageY 来得到当前鼠标的位置。

波纹

实现波纹效果就比较麻烦一点。但是,其实波纹效果也可以简化成两个部分:
  • 大小逐渐变大
  • 是圆形的
我们把它分解成两部分,再使用 CSS 分别实现这两部分的效果,最后叠加起来,就实现了这个波纹效果了!
大小变大,我们可以利用 CSS3 动画里面的 transform: scale(2) 来实现。至于圆形的话,很简单,我们可以使用一个块级元素来实现,将它的宽度和高度设置成一致,然后使用 border-radius: 100% 就可以实现圆形的效果了。

两者结合在一起

我们所需要实现的效果是点击后在该坐标上产生一个波纹动画,动画可以使用 transform 来实现,而对于动画的位置的话,我们可以灵活使用绝对定位和相对定位来实现。具体思路如下:
  1. 一开始的时候,隐藏或者移除波纹动画的元素。
  2. 当用户点击时,将波纹动画的元素插进点击的元素的上方。
  3. 计算波纹动画元素的宽度和高度应该为多少。
  4. 计算出鼠标此时的坐标。
  5. 计算出波纹动画元素的位置,相对于被点击的元素应该偏移多少。
  6. 为波纹动画元素添加动画效果。

SHOW ME THE CODE

废话说了那么多,还是代码来得实在:
<div class="demo">
	<h1>Ripple Click Effect</h1>
	<ul>
		<li><a>Dashboard</a></li>
		<li><a>My Account</a></li>
		<li><a>Direct Messages</a></li>
		<li><a>Chat Rooms</a></li>
		<li><a>Settings</a></li>
		<li><a>Logout</a></li>
	</ul>
</div>
以上是 HTML 的代码,没什么特别的,就是很普通的一个无序列表。
.demo {
	margin: 0;
    padding: 20px 0;
    background: #789;
}
.demo h1 {
    text-align: center;
    color: #FFF;
    text-shadow: 1px 2px 0 rgba(0,0,0,0.5);
}

.demo ul {
    margin: 0 auto;
    padding: 0;
    width: 250px;
    border-top: 5px solid #678;
    background: #FFF;
    box-shadow: 0 0 10px rgba(0,0,0,0.5);
}

.demo ul li {
    list-style-type: none; 
    
    /* 使用相对定位,目的是为了给波纹元素作为偏移参考。 */
    position: relative;
    
    /* 溢出部分隐藏起来,这样才能保证波纹元素无论如何变化都始终在该 li 元素里面。 */
    overflow: hidden;
}

.demo ul li a {

	/* 使用相对定位,目的是为了给波纹元素作为偏移参考。 */
    position: relative;
    
    /* 将按钮设置成块级元素。 */
    display: block;
    padding: 10px 30px;
    color: #789;
    
    /* 由于在HTML中我们没有设置 href 属性,故不会显示可点击的鼠标。设置该 CSS 规则使得强制显示可点击鼠标手势。 */
    cursor: pointer;
    
    /* 避免多次点击按钮之后浏览器自动选中按钮中的文字。 */
    user-select: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
}

.demo .ink {
	/* 使用绝对定位,使得波纹元素位于点击时的鼠标坐标。 */
    position: absolute;
    
    /* 设置成块级元素才能正确设置高度 */
    display: block;
    
    /* 设置成100%以实现圆形效果 */
    border-radius: 100%;
    
    /* 默认状态下缩小为0,相当于不显示出来。 */
    transform: scale(0);
    -ms-transform: scale(0);
    -webkit-transform: scale(0);
    
    background: rgba(100,100,100,0.4);
}

.demo .animate {
	/* 使用CSS3的动画效果来实现波纹动画 */
    -webkit-animation: ripple 0.4s linear;
    -moz-animation: ripple 0.4s linear;
}

@-webkit-keyframes ripple {
	/* 动画执行过程的一些变化。此处是将波纹元素的大小变为 250%,透明度变为0 */
    100% {
        transform: scale(2.5);
        -ms-transform: scale(2.5);
        -webkit-transform: scale(2.5);
        opacity: 0;
    }
}
以上是 CSS 代码。里面有相应的注释。
$('a', $('.demo')).on('click', function (event) {
    var parent = $(this).parent();
    
    if (parent.find('.ink').length === 0) {
    	// 当 li 元素里面尚没有波纹元素时,则插入一个新的波纹元素进去。
        parent.prepend($('<span>').addClass('ink'));
    }
    
    var ink = parent.find('.ink');
    
    // 每次点击之后先移除上一次的动画。即移除 animate 类。
    ink.removeClass('animate');
    
    if (!ink.width() && !ink.height()) {
    	// 当波纹元素尚未设置大小时,为波纹元素设置长度和宽度。其值大小为 li 元素高和宽的最大值。这样才能保证波纹动画执行的过程中能够覆盖整个按钮元素。
        var d = Math.max(parent.width(), parent.height());
        ink.css({
            width: d + 'px',
            height: d + 'px'
        });
    }
    
    
    // 计算波纹元素的位置
    var x = event.pageX - parent.offset().left - ink.width() / 2;
    var y = event.pageY - parent.offset().top - ink.height() / 2;
    
    // 设置波纹元素的位置,并且添加类,开始执行动画。
    ink.css({
        top: y + 'px',
        left: x + 'px'
    }).addClass('animate');
});
以上则是 JavaScript 代码。整个项目的代码非常少,而且很容易移植到其他项目中。

最后几句话

Materail Design 里面的动画都非常棒,目前也有一些 Web 开发者开发出了类似的库了,比如:
这几个库实现的效果都非常棒,相信不久以后 Material Design 会在互联网上开始蔓延开来的!

Reference