Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a gravity effect in a CSS animation?

Tags:

css

physics

I want to animate an element as if you're looking down at the Earth. The element jumps towards you, hits its apex, and falls back down a little. A side view of the trajectory would be like this:

     _
   /   \
  /     |
 |
 |

I wasn't able to attain a realistic effect with keyframe animations. Mine looks artificial like this:

    /\
   /  \
  /
 /
/

CSS

@keyframes springIn {
    0% {
        transform: scale(0.0);
    }
    80% {
        transform: scale(1.2);
    }
    100% {
        transform: scale(1.0);
    }
}

.someElement {
    animation: springIn 1s linear 1s 1 forwards;
}

How do you put parabolic function on the animation to get the gravity effect? I thought I could use Bezier Curves, but the CSS standard does not allow points outside of [0, 0].

like image 734
Pwner Avatar asked Mar 15 '13 02:03

Pwner


2 Answers

Use animation-timing-function in your keyframes to make the two transitions (rise followed by fall) parabolic.


To do this correctly (i.e. according to physics), you need firstly make sure that the 'peak' point scale and time correspond properly. Your animation runs from 0% to 100%, and the top of the parabola (point of maximum scale) is somewhere between the two, let's call it m%. The scale starts at 0 (at 0%), ends at 1 (at 100%) and peaks at let's say s (at m%). Then using a bit of basic math, the relationship between these two variables is:

m = 100 / (1 + sqrt(s-1))

or

s = (100/m - 1)^2 + 1

...so for the peak to be reached at 80%, you need s = 17/16 = 1.0625

alternatively, for a max scale s of 1.2, you need to peak at m = 69.0983... %


Now, to make the transitions properly parabolic, you need to use parabolic animation-timing-function settings. You want to effectively use something like ease-out as your object shoots up, and like ease-in as it starts falling back down... but the cubic bezier curves associated with these two keywords are not exactly parabolas.

Instead, use:

animation-timing-function: cubic-bezier(0.33333, 0.66667, 0.66667, 1)

for the "rise" part of the animation, and:

animation-timing-function: cubic-bezier(0.33333, 0, 0.66667, 0.33333)

for the "fall" part. These do give you exact parabolas. (See here for the maths behind the derivation of these values; note that ideally you'd use 1/3 rather than 0.33333 and 2/3 rather than 0.66667, but CSS doesn't allow fractions).

Putting all this together gives you this CSS:

.someElement { animation: springIn 1s linear 1s 1 none }

@keyframes springIn
{
  0% { transform: scale(0.0); animation-timing-function: cubic-bezier(0.33333, 0.66667, 0.66667, 1) }
  69.0983% { transform: scale(1.2); animation-timing-function: cubic-bezier(0.33333, 0, 0.66667, 0.33333) }
  100% { transform: scale(1.0) }
}

...and if I've done my sums right, that should give you a perfectly parabolic animation trajectory!

(Note: I changed the animation-fill-mode to "none" because it did not seem necessary for the animation to continue enforcing transform: scale(1.0) after it ends. If this is actually necessary for some reason, change this value back to "forwards").

like image 125
Doin Avatar answered Nov 03 '22 00:11

Doin


I think that you can do it using bezier curves.

in you case, it could be something like that:

-webkit-transition: all 500ms cubic-bezier(0.310, 0.440, 0.445, 1.650); 
-moz-transition: all 500ms cubic-bezier(0.310, 0.440, 0.445, 1.650); 
-ms-transition: all 500ms cubic-bezier(0.310, 0.440, 0.445, 1.650); 
-o-transition: all 500ms cubic-bezier(0.310, 0.440, 0.445, 1.650); 
transition: all 500ms cubic-bezier(0.310, 0.440, 0.445, 1.650); /* custom */

-webkit-transition-timing-function: cubic-bezier(0.310, 0.440, 0.445, 1.650); 
-moz-transition-timing-function: cubic-bezier(0.310, 0.440, 0.445, 1.650); 
-ms-transition-timing-function: cubic-bezier(0.310, 0.440, 0.445, 1.650); 
-o-transition-timing-function: cubic-bezier(0.310, 0.440, 0.445, 1.650); 
transition-timing-function: cubic-bezier(0.310, 0.440, 0.445, 1.650); /* custom */

I haven't done it by myself, check this link:

css easing animation tool

I have made an example in a JSFiddle.

I put an outer div to make the hover stable:

<div class="container">
    <div class="moving"></div>
</div>

and the CSS is as follows:

.moving {
    position: absolute; width: 200px; height: 150px; top: 50px;  left: 50px;
    background-color: green;
    -webkit-transition: all 5s cubic-bezier(0.310, 0.440, 0.445, 1.650); 
}

.container:hover .moving {
    zoom: 1.5;
}

Editing

Just an image of what can you get with a bezier curve (from the ease animation tool page) to show that the object speed doesn't need to be constant (and can be almost parabolic)

bezier curve

like image 28
vals Avatar answered Nov 03 '22 00:11

vals