Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filling water animation

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 the 3rd one:

  1. It fills up the wrong way
  2. It resets to empty (black) after it has filled *
  3. For now, I am using the <img> tags, but I would like to move this effect to body { background-image: } and need some direction on how to do this.

What I have tried so far:

#banner {    width: 300px;    height: 300px;    position: relative;  }  #banner div {    position: absolute;  }  #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, 1)), color-stop(0.25, rgba(0, 0, 0, 1)), color-stop(0.27, rgba(0, 0, 0, 0)), color-stop(0.80, rgba(0, 0, 0, 0)), color-stop(1.00, rgba(0, 0, 0, 0)));  }  @-webkit-keyframes wipe {    0% {      -webkit-mask-position: 0 0;    }    100% {      -webkit-mask-position: 300px 300px;    }  }
<div id="banner">    <div>      <img src="http://i.imgur.com/vklf6kK.png" />    </div>    <div>      <img src="http://i.imgur.com/uszeRpk.png" />    </div>  </div>

Giving it a default mask position as @anpsmn suggested, doesn't reset it to black anymore.

like image 520
Arian Faurtosh Avatar asked Apr 20 '15 03:04

Arian Faurtosh


2 Answers

Here are four different versions to supplement @misterManSam's brilliant answer.

1. With Easing


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>

2. SVG Deliciousness

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>

3. With pour line


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>

4. With Serious Bling (with beautiful aesthetics)


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>
like image 73
Chris Spittles Avatar answered Sep 25 '22 21:09

Chris Spittles


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

Working Example

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>
like image 23
misterManSam Avatar answered Sep 21 '22 21:09

misterManSam