Pure CSS to achieve the water drop animation button in Material Design

Pure CSS to achieve the water drop animation button in Material Design

Preface

You should often see this kind of special effect. It's very cool, isn't it?

This is the most common special effect in Google Material Design . There are also many ready-made js libraries on the market to simulate this special effect. However, it is often necessary to introduce a lot of js and css . In fact, in an existing project, you may just want to add such a button to enhance the user experience. These js libraries seem a bit too large. At the same time, because it is implemented by js , you often have to pay attention to loading issues.

So, is there any way to achieve this effect using css ?

Ideas

In fact, it is an animation, a perfect circle grows from small to large, which can be easily achieved using the animation in css3

Sample Code

@keyframes ripple{
    from {
        transform: scale(0);
        opacity: 1;
    }
    to {
        transform: scale(1);
        opacity: 0;
    }
}

The way to implement it with js is usually very simple, which is to add a class to the click element and then remove the class after the animation ends.

Sample Code

var btn = document.getElementById('btn');
btn.addeventlistener('click',function(){
  addClass(btn,'animate')
},false)
btn.addeventlistener('transitionend',function(){//Listen for the end of CSS3 animation removeClass(btn,'animate')
},false)

So how to trigger the animation through CSS?

CSS Implementation

The pseudo-classes that interact with the mouse in css are mainly

  • hover mouse over
  • :active mouse pressed
  • :focus mouse focus
  • :checked Mouse selected

In many cases, the effects on our pages are achieved through hover . Placing the mouse on the page triggers an effect, and leaving the page restores the effect. However, if you leave the page immediately after placing the mouse on the page, the animation will end immediately.

Let's try it first.

structure

This is the page structure and style we wrote

<style>
.btn{ 
  display: block; 
  width: 300px; 
  outline: 0; 
  overflow: hidden;  
  position: relative; 
  transition: .3s; 
  cursor: pointer; 
  user-select: none; 
  height: 100px; 
  text-align: center; 
  line-height: 100px; 
  font-size: 50px; 
  background: tomato; 
  color: #fff;  
  border-radius: 10px;
}
</style>
<a class="btn">button</a>

Very simple, just an ordinary button style

Next we add the perfect circle we need to the button.

We use pseudo elements to achieve this

.btn:after{
    content: '';
    position: absolute;
    width: 100%;
    padding-top: 100%;
    background: transparent;
    border-radius: 50%;
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%)
}

Let's remove overflow: hidden above and shrink the circle to see the effect.

Then, we write a scaling animation

@keyframes ripple{
    from {
        transform: translate(-50%,-50%) scale(0);
        /**Since we wrote the transformation attribute by default, we need to add translate(-50%,-50%) here, otherwise it will be replaced**/
        background: rgba(0,0,0,.25);
    }
    to {
        transform: translate(-50%,-50%) scale(1);
        background: transparent;
    }
}

Hover small interactive experience

Try passing the mouse over?

.btn:hover:after{
  animation: ripple 1s;
} 

The effect is good, but if you move the mouse too quickly, the circle that just expanded will shrink back immediately, which is a bit inconsistent.

But this is not the effect we want. What we hope is that it will be triggered once per click, rather than just putting it there and it will never be triggered again.

active try

In daily work, active is used quite frequently, usually for click effects, so why not give it a try?

.btn:active:after{
  animation: ripple 1s;
} 

The effect is also unsatisfactory, a bit like holding down the mouse, you have to keep holding the mouse to trigger it completely. For example, in the example above, the animation runs for 1s , so you must click on the button for 1s to see the complete animation effect. Otherwise, just like the above mouse leaves, the animation will shrink back immediately.

Focus Experience

If you need to give focus to any element, you can assign a tabindex attribute to it.

<a class="btn" tabindex="1">button</a>
.btn:focus:after{
  animation: ripple 1s;
} 

foucs can also be triggered, but it can only be triggered again after losing focus. The actual operation performance is that after clicking once, click the blank space outside again

Is there no solution?

Of course there are some, the last one is definitely the solution, hahaha

checked

checked cannot be triggered directly. It is triggered after the form element is selected. For this reason, we need to modify the page structure.

<label class="btn">
  <input type="checkbox"><span>button</span>
</label>

We have changed lable here and included the input[type=checkbox] tag, mainly to trigger input selection when the button is clicked.

Add some style

.btn>span:after{
  /**Change the selector**/
}
.btn>input[type=checkbox]{
  display: none
}
.btn>input[type=checkbox]:checked+span:after{
  animation: ripple 1s;
}

This can also trigger the animation, but the problem is that when you click again, it becomes unselected. How to trigger the animation?

In fact, it can be achieved with :not

.btn>input[type=checkbox]:not(:checked)+span:after{
  animation: ripple 1s;
}

At first glance, it seems quite clever, but if you think about it carefully, since animation is written on both the front and back sides, doesn’t it have anything to do with :checked ? It is better to go directly

.btn>input[type=checkbox]+span:after{
  animation: ripple 1s;
}

Infinite cycle...

This problem has troubled me for a long time, but God will not let down those who work hard. Later, I tried to trigger different animations in two states and they can be triggered separately, as follows

.btn>input[type=checkbox]:checked+span:after{
  animation: ripple1 1s;
}
.btn>input[type=checkbox]:not(:checked)+span:after{
  animation: ripple2 1s;
}

This should be easy to understand.

Now, here comes the point. Now, if we change the animation process in ripple1 and ripple2 to the same, they can be triggered separately. In other words, as long as the animation names are different, CSS will treat them as different animations.

This is very simple. We just need to set a default state, select a state, and then trigger animations with different names.

.btn>input[type=checkbox]+span:after{
  animation: ripple-in 1s;
}
.btn>input[type=checkbox]:checked+span:after{
  animation: ripple-out 1s;
}
@keyframes ripple-in{
    from {
        transform: translate(-50%,-50%) scale(0);
        background: rgba(0,0,0,.25);
    }
    to {
        transform: translate(-50%,-50%) scale(1);
        background: transparent;
    }
}
@keyframes ripple-out{/*only the name is different*/
    from {
        transform: translate(-50%,-50%) scale(0);
        background: rgba(0,0,0,.25);
    }
    to {
        transform: translate(-50%,-50%) scale(1);
        background: transparent;
    }
}

The effect is as shown at the beginning of the article, perfect

The complete demo is as follows

https://codepen.io/xboxyan/pen/Jmvyex/

Some shortcomings

Since the above animation style will be triggered by default, you will see the water drop animation on the button move once when the page loads, but it is not particularly obvious and acceptable.

Secondly, the actual effect is definitely to spread from the point where the mouse is clicked. Our CSS certainly cannot do this, and can only spread from the center, which is also a compromise. Here is an idea. You can use css variables and store the corresponding values ​​in style every time you click, so that it can also be used in css .

The above is the full content of this article. I hope it will be helpful for everyone’s study. I also hope that everyone will support 123WORDPRESS.COM.

<<:  Detailed explanation of the solution for real-time synchronization from MySQL to Oracle

>>:  How to configure Nginx load balancing

Recommend

Detailed explanation of the use of nohup /dev/null 2>&1

nohup command: If you are running a process and y...

Implementation of HTML command line interface

HTML Part Copy code The code is as follows: <!D...

Detailed analysis of mysql MDL metadata lock

Preface: When you execute a SQL statement in MySQ...

mysql create database, add users, user authorization practical method

1. Create a MySQL database 1. Create database syn...

Analysis of the principle of Vue nextTick

Table of contents Event Loop miscroTask (microtas...

MySQL database index order by sorting detailed explanation

Table of contents The cause of the incident Anato...

Front-end AI cutting tips (experience)

AI image cutting needs to be coordinated with PS....

How to forget the password of Jenkins in Linux

1.Jenkins installation steps: https://www.jb51.ne...

How React Hooks Work

Table of contents 1. React Hooks vs. Pure Functio...

Solution to forgetting the root password of MySQL 5.7 and 8.0 database

Note: To crack the root password in MySQL5.7, you...

Detailed explanation of the usage of the ESCAPE keyword in MySQL

MySQL escape Escape means the original semantics ...

Adobe Brackets simple use graphic tutorial

Adobe Brackets is an open source, simple and powe...

Solution to the failure of docker windows10 shared directory mounting

cause When executing the docker script, an error ...

Excel export always fails in docker environment

Excel export always fails in the docker environme...