你尚未登录,仅允许查看本站部分内容。请登录使用邀请码注册
稀土掘金

怎么用 Vue 实现 Material Design的涟漪动效 1个回复 专栏 @ CSS

稀土掘金 发布于 2 年前

「掘金·发现」是稀土圈微信公众号的一个新栏目。正如大家所知,稀土掘金致力于挖掘最优质的互联网技术,为用户带去最优质的阅读体验。我们做这个栏目的出发点,也正是如此。掘金社区当中有趣而有用的内容,我们呈现给你。

在本期栏目中,来自我们稀土掘金的 Awe 会用个人实践经历,告诉大家如何使用 Vue 来实现 Material Design 中点击按钮产生水纹扩散的效果。

第一次看到 Material Design Lite 的时候只能用惊艳来表达,原来按钮、复选框、输入框等控件还可以这样玩。

有兴趣的朋友可以前往 Material Design Lite 官网了解更多详情。

说了这么多,还是先上最终效果图看看怎么样吧:

01.gif

去年的时候,我把 MDL 搬到了 Vue 里,写了一个简单的博客。这里的动效全部都是在 Vue 渲染出 DOM 之后调用 MDL 的方法来实现的。

比如这段代码就是首页异步获取的文章列表数据之后调 componentHandler.upgradeAllRegistered()

asyncData: function(resolve, reject) {
    this.loadPost(0,  (tmp)  => {
        store.actions.hideLoading();
        resolve({
            posts: tmp
        })
        this.site.skip = 10;
         this.$nextTick(function() {
            componentHandler.upgradeAllRegistered();
        })
    })
}

02.gif

上面提到的是一个相对简单轻松的用法,但是总给人感觉不够优雅,你总得去调用那个黑盒般的方法。

好在,我是一个喜欢折腾的人,还是自己来实现一下这个效果吧。

Material Design 里特别吸引我的一个效果就是点击按钮之后,会有水波的涟漪从点击的中心向外扩散出去的效果。

03.gif

于是,我马上打开了 Chrome 开发者工具去看看它的 DOM 结构(为了显示效果,我删掉了 Button 元素的一部分 Class)

<button class="mdl-button">
<i class="material-icons">add</i>
<span class="mdl-button__ripple-container">
    <span class="mdl-ripple is-animating" style="width: 160.392px; height: 160.392px; transform: translate(-50%, -50%) translate(30px, 48px);">            
    </span>
</span>
</button>

看来以上代码块就是实现上文所说水波扩散效果的部分了。

通过观察代码可以发现,这一效果的实现原理就是通过动态改变它的中心位置,接着将中心部分从小到大扩散的动画。

先写出来我们要做的 Button 的 DOM:


BUTTON

我们在 Button 被点击之后给 Class 中的 .animate 属性加上 Ripple 的 CSS 3 动画代码, 它就会开始扩散了。

.animate {
    animation: ripple 0.65s linear;
}
@keyframes ripple {
    100% {opacity: 0; transform: scale(2.5);}
}

reppleClick (e) {
    this.repple_button.animate = true
    let button = e.target
    let ripple = button.querySelector('.__cov-ripple')
    if (ripple) {
        //水波得是一个直径大于button最长边的正圆
        let d = Math.max(button.offsetHeight, button.offsetWidth)
        //设置它中心在鼠标点击的位置
        let x = e.layerX - ripple.offsetWidth / 2
        let y = e.layerY - ripple.offsetHeight / 2
        ripple.setAttribute('style', 'height: ' + d + 'px; width: ' + d + 'px; top: ' + y + 'px; left: ' + x + 'px;')
}
    this.$nextTick(() => {
    //在动画结束后移除animate
    setTimeout(() => {
        this.repple_button.animate = false
        }, 660)
    })
}

完成之后就是下面这个效果啦:

04.gif

完整代码

<template>
    <button @click="reppleClick" :class="{active: repple_button.toggle}">
<slot></slot>
</button>
</template>
<script>
export default {
    data () {
    return {
    repple_button: {
    animate: false,
    toggle: false
    }
}
    },
    methods: {
reppleClick (e) {
this.repple_button.animate = true
    let button = e.target
    let ripple = button.querySelector('.__cov-ripple')
    if (ripple) {
    let d = Math.max(button.offsetHeight, button.offsetWidth)
    let x = e.layerX - ripple.offsetWidth / 2
    let y = e.layerY - ripple.offsetHeight / 2
    ripple.setAttribute('style', 'height: ' + d + 'px; width: ' + d + 'px; top: ' + y + 'px; left: ' + x + 'px;')
    }
    this.$nextTick(() => {
    setTimeout(() => {
        this.repple_button.animate = false
    }, 660)
    })
}
    }
}
</script>
<style>
.__cov-button-ripple {
    background: transparent;
    border: none;
    border-radius: 2px;
    color: #000;
    position: relative;
    height: 36px;
    min-width: 64px;
    padding: 0 16px;
    display: inline-block;
    font-family: Roboto,Helvetica,Arial,sans-serif;
    font-size: 14px;
    font-weight: 500;
    text-transform: uppercase;
    line-height: 1;
    letter-spacing: 0;
    overflow: hidden;
    will-change: box-shadow,transform;
    -webkit-transition: box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);
    transition: box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);
    outline: none;
    cursor: pointer;
    text-decoration: none;
    text-align: center;
    line-height: 36px;
    vertical-align: middle;
    min-width: 96px;
}
.__cov-button-ripple:hover {
    background-color: hsla(0,0%,62%,.2);
}
.__cov-ripple {
    display: block; 
    position: absolute;
    background: hsla(0, 0%, 65%, 0.66);
    border-radius: 100%;
    transform: scale(0);
}
 .__cov-ripple.animate {
     animation: ripple 0.65s linear;
}

@keyframes ripple {
    100% {opacity: 0; transform: scale(2.5);}
}
</style>

用法

<template>
    <div>
<div>资料设定</div>
<div>
    某些文字
</div>
<div>
    <!-- covbutton 在这里-->
    <cov-button @click="close">OK</cov-button>
</div>
</div>    
</template>

<script>
import covButton from './button.vue'
export default {
    components: {
    covButton
}
}
登录后回复,如无账号,请使用邀请码注册