I am trying to get a wipe up animation to make a circle look like it\'s filling with water. I\'ve run into two errors, and haven\'t been able to even tackle
This can be achieved with a single div and a ::before pseudo element:
The #banner
is given border-radius: 50%
to create a circle and overflow: hidden
to clip its children inside it
The ::before
pseudo element is animated to 100% height and the animation is paused at 100% using the forwards value. It begins at the bottom with the use of bottom: 0
The background images would be applied in place of the black and blue backgrounds on #banner
and #banner::before
Compatibility: IE10+ and all modern browsers. The -webkit-
prefixed property is most likely no longer necessary for your keyframe animations. Check the browser compatibility chart over here on caniuse.com
I have added the cubic-bezier(.2,.6,.8,.4)
which is explained in @ChrisSpittles answer. It provides a neat effect!
#banner {
width: 300px;
height: 300px;
position: relative;
background: #000;
border-radius: 50%;
overflow: hidden;
}
#banner::before {
content: '';
position: absolute;
background: #04ACFF;
width: 100%;
bottom: 0;
animation: wipe 5s cubic-bezier(.2,.6,.8,.4) forwards;
}
@keyframes wipe {
0% {
height: 0;
}
100% {
height: 100%;
}
}
<div id="banner">
</div>
Here the working working codpen of Water filling in a div on Hover
HTML
<div class="dot">
</div>
CSS
.dot {
border: 1px;
border-style: solid;
width: 100px;
height: 100px;
border-radius: 50%;
border-color: black;
color: black;
padding: 5px;
background-size: 200% 200%;
background-image:
linear-gradient(to top, #76daff 50%, transparent 50%);
transition: background-position 3000ms, color 3000ms ease, border-color 3000ms ease;
}
.dot:hover {
color: white;
border-color: black;
background-image:
linear-gradient(to top, #76daff 51%, transparent 50%);
background-position: 0 100%;
transition: background-position 3000ms, color 3000ms ease, border-color 3000ms ease;
}
https://codepen.io/ajitkumar96/pen/pOYbQm?editors=1100
div{
width: 200px;
height: 200px;
background: #ccc;
border-radius: 50%;
overflow: hidden;
position: relative;
z-index: 9;
}
div:before{
content: '';
position: absolute; top: 100%; left: 0;
width: 100%;
height: 100%;
background: #00BFFF;
-webkit-animation: animtop 5s forwards, animtop2 2s forwards;
animation: animtop 5s forwards, animtop2 2s forwards;
}
@-webkit-keyframes animtop{
0%{top: 100%;}
75%{top: 0}
}
@keyframes animtop{
0%{top: 100%;}
100%{top: 25%}
}
@-webkit-keyframes animtop2{
75%{top: 25%;}
100%{top: 0}
}
@keyframes animtop2{
75%{top: 25%;}
100%{top: 0}
}
<div></div>
I think this accomplishes your first goal:
#banner div:nth-child(2) {
-webkit-animation: wipe 6s;
-webkit-animation-delay: 0s;
-webkit-animation-direction: up;
-webkit-mask-size: 300px 3000px;
-webkit-mask-position: 300px 300px;
-webkit-mask-image: -webkit-gradient(linear, left bottom, left top,
color-stop(0.00, rgba(0,0,0,0)),
color-stop(0.25, rgba(0,0,0,0)),
color-stop(0.27, rgba(0,0,0,0)),
color-stop(0.80, rgba(0,0,0,1)),
color-stop(1.00, rgba(0,0,0,1)));
}
@-webkit-keyframes wipe {
0% {
-webkit-mask-position: 300px 300px;
}
100% {
-webkit-mask-position: 0 0;
}
}
Pure CSS, no JavaScript, no SVG. Switch overflow: hidden
off in .shape
to see how simple it works.
Made it using CSS animation. You can fined fill-up and animate @keyframes
If you wish to use JavaScript for loading filling - you can access .wave
element. The top: 50%;
is 0% and top: -75%
is 100% of loading. You can play with this digits as you want.
.shape {
width: 180px;
height: 180px;
border-radius: 50%;
overflow: hidden;
position: relative;
}
.shape:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
box-shadow: inset 0px 0px 30px 0px rgba(0, 0, 0, 0.3);
overflow: hidden;
z-index: 3;
}
.wave {
position: absolute;
top: 50%;
left: 0;
width: 200%;
height: 200%;
transform: translate(-25%, 0);
background: #4973ff;
animation: fill-up 10s ease infinite;
}
@keyframes fill-up {
to {
top: -75%;
}
}
.wave:before,
.wave:after {
content: '';
position: absolute;
width: 110%;
height: 100%;
top: 0;
left: 50%;
transform: translate(-50%, -75%);
background: #000;
}
.wave:before {
border-radius: 45%;
background: rgba(179, 241, 255, 1);
animation: animate 3s linear infinite;
}
.wave:after {
border-radius: 40%;
background: rgba(179, 241, 255, 0.5);
animation: animate 3s linear infinite;
}
@keyframes animate {
0% {
transform: translate(-50%, -75%) rotate(0deg);
}
100% {
transform: translate(-50%, -75%) rotate(360deg);
}
}
<div class="shape">
<div class="wave">
</div>
</div>
Here are four different versions to supplement @misterManSam's brilliant answer.
Explanation
If you filled up a circular bowl full of liquid, it would fill faster at the bottom and top than it would in the middle (because there is more area to cover in the wider middle section). So, with that crude explanation in mind, the animation needs to: start fast, slow in the middle, and then finish fast when the bowl narrows again at the top.
To do this we can use a CSS3 easing function: cubic-bezier(.2,.6,.8,.4)
.
Have a look at the example below.
(If you want to tweak the easing here is a great resource: http://cubic-bezier.com/#.2,.6,.8,.4)
Example:
#banner {
width: 150px;
height: 150px;
position: relative;
background: #000;
border-radius: 50%;
overflow: hidden;
}
#banner::before {
content: '';
position: absolute;
background: #04ACFF;
width: 100%;
bottom: 0;
animation: wipe 5s cubic-bezier(.2,.6,.8,.4) forwards;
}
@keyframes wipe {
0% {
height: 0;
}
100% {
height: 100%;
}
}
<div id="banner">
</div>
Let's take this one step further? What if we wanted to add a wavy surface on the "water" using CSS? We can do this using the amazing SVG. I created a wavy SVG image in Adobe Illustrator and then animated that to travel from left to right on a loop with a separate CSS animation and voila:
Example
#banner {
border-radius: 50%;
width: 150px;
height: 150px;
background: #000;
overflow: hidden;
backface-visibility: hidden;
transform: translate3d(0, 0, 0);
}
#banner .fill {
animation-name: fillAction;
animation-iteration-count: 1;
animation-timing-function: cubic-bezier(.2, .6, .8, .4);
animation-duration: 4s;
animation-fill-mode: forwards;
}
#banner #waveShape {
animation-name: waveAction;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-duration: 0.5s;
width:300px;
height: 150px;
fill: #04ACFF;
}
@keyframes fillAction {
0% {
transform: translate(0, 150px);
}
100% {
transform: translate(0, -5px);
}
}
@keyframes waveAction {
0% {
transform: translate(-150px, 0);
}
100% {
transform: translate(0, 0);
}
}
<div id="banner">
<div class="fill">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="300px" height="300px" viewBox="0 0 300 300" enable-background="new 0 0 300 300" xml:space="preserve">
<path fill="#04ACFF" id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4
c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9
c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z"/>
</svg>
</div>
</div>
This example includes a pour line (most bowls fill from the top, not the bottom). The pour line first animates from top to bottom while an animation-delay
property prevents the fill animation from happening until the pour has finished.
#banner {
border-radius: 50%;
width: 150px;
height: 150px;
background: #000;
overflow: hidden;
backface-visibility: hidden;
transform: translate3d(0, 0, 0);
position: relative;
}
#banner .fill {
transform: translateY(150px);
animation-name: fillAction;
animation-iteration-count: 1;
animation-timing-function: cubic-bezier(.2, .6, .8, .4);
animation-duration: 4s;
animation-fill-mode: forwards;
animation-delay: 0.25s;
}
#banner .pour {
width: 6px;
position: absolute;
left: 50%;
margin-left: -3px;
bottom: 0;
top: 0;
background: #009ae6;
animation-name: pourAction;
animation-timing-function: linear;
animation-duration: 0.25s;
}
#banner #waveShape {
animation-name: waveAction;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-duration: 0.5s;
width: 300px;
height: 150px;
fill: #04ACFF;
}
@keyframes pourAction {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(0);
}
}
@keyframes fillAction {
0% {
transform: translateY(150px);
}
100% {
transform: translateY(-5px);
}
}
@keyframes waveAction {
0% {
transform: translate(-150px, 0);
}
100% {
transform: translate(0, 0);
}
}
<div id="banner">
<div class="pour"></div>
<div class="fill">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="300px" height="300px" viewBox="0 0 300 300" enable-background="new 0 0 300 300" xml:space="preserve">
<path fill="#04ACFF" id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4
c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9
c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z" />
</svg>
</div>
</div>
This example adds a few more properties to the CSS to make it look a little more realistic.
.bowl {
position: relative;
border-radius: 50%;
width: 150px;
height: 150px;
box-shadow: inset 0 -5px 0 0 rgba(0, 0, 0, 0.5), inset 0 -20px 5px 0 rgba(0, 0, 0, 0.2), inset -15px 0 5px 0 rgba(0, 0, 0, 0.1), inset 15px 0 5px 0 rgba(0, 0, 0, 0.1);
background: -moz-radial-gradient(center, ellipse cover, transparent 0%, transparent 76%, rgba(0, 0, 0, 0.65) 100%);
background: -webkit-radial-gradient(center, ellipse cover, transparent 0%, transparent 76%, rgba(0, 0, 0, 0.65) 100%);
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 76%, rgba(0, 0, 0, 0.65) 100%);
margin: 20px;
}
.bowl:before {
overflow: hidden;
border-radius: 50%;
content: "";
box-shadow: inset 0 -5px 0 0 rgba(0, 0, 0, 0.5), inset 0 -20px 5px 0 rgba(0, 0, 0, 0.2), inset -15px 0 5px 0 rgba(0, 0, 0, 0.1), inset 15px 0 5px 0 rgba(0, 0, 0, 0.1);
background: -moz-radial-gradient(center, ellipse cover, transparent 0%, transparent 60%, rgba(0, 0, 0, 0.65) 81%, black 100%);
background: -webkit-radial-gradient(center, ellipse cover, transparent 0%, transparent 60%, rgba(0, 0, 0, 0.65) 81%, black 100%);
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 60%, rgba(0, 0, 0, 0.65) 81%, #000000 100%);
position: absolute;
width: 150px;
height: 150px;
z-index: 2;
}
.bowl:after {
content: "";
width: 60px;
border-radius: 50%;
height: 5px;
background: #039be4;
box-shadow: inset 0 0 10px 0 #000;
position: absolute;
left: 50%;
margin-left: -30px;
bottom: 0;
z-index: 2;
}
.bowl .inner {
border-radius: 50%;
width: 150px;
height: 150px;
background: -moz-radial-gradient(center, ellipse cover, transparent 0%, transparent 76%, rgba(0, 0, 0, 0.65) 100%);
background: -webkit-radial-gradient(center, ellipse cover, transparent 0%, transparent 76%, rgba(0, 0, 0, 0.65) 100%);
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 76%, rgba(0, 0, 0, 0.65) 100%);
overflow: hidden;
-webkit-backface-visibility: hidden;
-webkit-transform: translate3d(0, 0, 0);
}
.bowl .inner:before {
content: "";
width: 20px;
height: 20px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
position: absolute;
right: 40%;
top: 60%;
z-index: 2;
}
.bowl .inner:after {
content: "";
width: 20px;
height: 40px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
position: absolute;
right: 30%;
top: 15%;
transform: rotate(-20deg);
z-index: 2;
}
.bowl .fill {
-webkit-animation-name: fillAction;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: cubic-bezier(0.2, 0.6, 0.8, 0.4);
-webkit-animation-duration: 4s;
-webkit-animation-fill-mode: forwards;
}
.bowl .waveShape {
-webkit-animation-name: waveAction;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
-webkit-animation-duration: 0.5s;
width: 300px;
height: 150px;
fill: #039be4;
}
@-webkit-keyframes fillAction {
0% {
-webkit-transform: translate(0, 150px);
}
100% {
-webkit-transform: translate(0, 10px);
}
}
@-webkit-keyframes waveAction {
0% {
-webkit-transform: translate(-150px, 0);
}
100% {
-webkit-transform: translate(0, 0);
}
}
/* For aesthetics only ------------------------------------------*/
body {
margin: 0;
font-family: Segoe, "Segoe UI", "DejaVu Sans", "Trebuchet MS", Verdana, sans-serif;
}
h1 {
font: 200 1.2em "Segoe UI Light", "DejaVu Sans", "Trebuchet MS", Verdana, sans-serif;
font-weight: 200;
color: #fff;
background: #039be4;
padding: 20px;
margin: 0;
border-bottom: 10px solid #ccc;
}
h1 strong {
font-family: "Segoe UI Black";
font-weight: normal;
}
.explanation {
padding: 20px 40px;
float: right;
background: #e64a19;
-webkit-box-shadow: inset 0 30px 3px 0 rgba(0, 0, 0, 0.5);
box-shadow: inset 0 3px 5px 0 rgba(0, 0, 0, 0.2);
border-bottom: 10px solid #ccc;
max-width: 300px;
}
.explanation p {
color: #fff;
font-size: 0.8rem;
}
<div class="bowl">
<div class="inner">
<div class="fill">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="300px" height="300px" viewBox="0 0 300 300" enable-background="new 0 0 300 300" xml:space="preserve">
<path class="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4
c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9
c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z" />
</svg>
</div>
</div>
</div>