I am trying to chain together 2 CSS animations, see pen: http://codepen.io/jdesilvio/pen/pjwvyO
I have followed the syntax provided in other answers to similar questions, but they don't seem to work correctly:
animation-name: falling, laydown;
animation-duration: 2000ms, 1000ms;
animation-timing-function: ease-in, ease-out;
animation-iteration-count: 1, 1;
It is playing the second animation, then the first and finally the second one again. How can I get it to play the first, then second?
Here is the full code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
html, body {
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: center;
}
#keyframes falling {
0% {
transform: translate3d(0, -400px, 0);
}
100% {
transform:
translate3d(0, 40%, 0)
rotateX(30deg)
rotateY(0deg)
rotateZ(60deg);
}
}
#keyframes laydown {
0% {
transform:
translate3d(0, 40%, 0)
rotateX(30deg)
rotateY(0deg)
rotateZ(60deg);
}
100% {
transform:
translate3d(0, 40%, 0)
rotateX(70deg)
rotateY(0deg)
rotateZ(80deg);
}
}
#falling-card-parent {
height: 150px;
width: 100px;
margin: auto;
perspective: 1000px;
}
#falling-card {
height: 150px;
width: 100px;
background-color: black;
margin: auto;
transform:
translate3d(0, 40%, 0)
rotateX(70deg)
rotateY(0deg)
rotateZ(80deg);
animation-name:
falling, laydown;
animation-duration:
2000ms, 1000ms;
animation-timing-function:
ease-in, ease-out;
animation-iteration-count:
1, 1;
}
</style>
<html>
<body>
<div id="falling-card-parent">
<div id="falling-card"></div>
</div>
</body>
</html>
The problem is actually not with the order of the animations but because of how multiple animations on pme element works. When multiple animations are added on an element, they start at the same time by default.
Because of this, both the laydown and falling animations start at the same time but the laydown animation actually completes within 1000ms from the start but the first animation (which is falling) doesn't complete till 2000ms.
The W3C spec about animations also say the following about multiple animations accessing the same property during animation:
If multiple animations are attempting to modify the same property, then the animation closest to the end of the list of names wins.
In the code provided in question, both animations are trying to modify the transform property and the second animation is the closest to the end. So while the second animation is still running (which is, for the first 1000ms) the transform changes are applied as specified in the second animation. During this time the first animation is still running but it has no effect because its values are overwritten. In the 2nd 1000ms (when the second animation has already completed but 1st is still executing), the transforms are applied as directed by the first animation. This is why it looks as if the second animation is running before the first animation and then the first.
To fix this problem, the execution of the second animation should be put on hold (or delayed) until the time the first animation is complete. This can be done by adding a animation-delay (that is equal to the animation-duration of the first animation) for the second animation.
animation-name: falling, laydown;
animation-duration: 2000ms, 1000ms;
animation-delay: 0ms, 2000ms; /* add this */
animation-timing-function: ease-in, ease-out;
animation-iteration-count: 1, 1;
html,
body {
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: center;
}
#keyframes falling {
0% {
transform: translate3d(0, -400px, 0);
}
100% {
transform: translate3d(0, 40%, 0) rotateX(30deg) rotateY(0deg) rotateZ(60deg);
}
}
#keyframes laydown {
0% {
transform: translate3d(0, 40%, 0) rotateX(30deg) rotateY(0deg) rotateZ(60deg);
}
100% {
transform: translate3d(0, 40%, 0) rotateX(70deg) rotateY(0deg) rotateZ(80deg);
}
}
#falling-card-parent {
height: 150px;
width: 100px;
margin: auto;
perspective: 1000px;
}
#falling-card {
height: 150px;
width: 100px;
background-color: black;
margin: auto;
transform: translate3d(0, 40%, 0) rotateX(70deg) rotateY(0deg) rotateZ(80deg);
animation-name: falling, laydown;
animation-duration: 2000ms, 1000ms;
animation-delay: 0ms, 2000ms;
animation-timing-function: ease-in, ease-out;
animation-iteration-count: 1, 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div id="falling-card-parent">
<div id="falling-card"></div>
</div>
html, body {
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: center;
}
#keyframes falling {
0% {
transform: translate3d(0, -400px, 0);
}
100% {
transform:
translate3d(0, 40%, 0)
rotateX(30deg)
rotateY(0deg)
rotateZ(60deg);
}
}
#keyframes laydown {
0% {
transform:
translate3d(0, 40%, 0)
rotateX(30deg)
rotateY(0deg)
rotateZ(60deg);
}
100% {
transform:
translate3d(0, 40%, 0)
rotateX(70deg)
rotateY(0deg)
rotateZ(80deg);
}
}
#falling-card-parent {
height: 150px;
width: 100px;
margin: auto;
perspective: 1000px;
}
#falling-card {
height: 150px;
width: 100px;
background-color: black;
margin: auto;
transform:
translate3d(0, 40%, 0)
rotateX(70deg)
rotateY(0deg)
rotateZ(80deg);
animation-name:
laydown, falling;
animation-duration:
2000ms, 1000ms;
animation-timing-function:
ease-in, ease-out;
animation-iteration-count:
1, 1;
}
<div id="falling-card-parent">
<div id="falling-card"></div>
</div>
Just reverse this:
animation-name:
falling, laydown;
to
animation-name:
laydown, falling;
Here: http://jsfiddle.net/hfxopjjy/
Related
I'm trying to play different css animations one after another but I can't figure out how to.
Basically what I'm trying to do is play one Animation, have it on screen for 15 seconds, then play the next one, show it for 15 seconds and on to the next one and when the last one has been played, it should start again from the top.
Here's an example of the first one it should play, show for 15 seconds and then move on to the next one and do the same.
<style> #animated-example {
width: 300px;
height: 200px;
border: solid 1px #1A7404;
position: absolute;
background-color: #62A80A;
}
.animated {
-webkit-animation-duration: 2s;
animation-duration: 2s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
#-webkit-keyframes bounceInLeft {
0% {
opacity: 0;
-webkit-transform: translateX(-2000px);
}
60% {
opacity: 1;
-webkit-transform: translateX(30px);
}
80% {
-webkit-transform: translateX(-10px);
}
100% {
-webkit-transform: translateX(0);
}
}
#keyframes bounceInLeft {
0% {
opacity: 0;
transform: translateX(-2000px);
}
60% {
opacity: 1;
transform: translateX(30px);
}
80% {
transform: translateX(-10px);
}
100% {
transform: translateX(0);
}
}
.bounceInLeft {
-webkit-animation-name: bounceInLeft;
animation-name: bounceInLeft;
}
</style>
<img id="animated-example" class="animated bounceInLeft" src="http://webmarketingtoday.com/wp-content/uploads/Screen-Shot-2012-05-24-at-7.31.54-AM-288x216.png">
And then run another one, show it for 15 seconds and move on.
<style> #animated-example {
width: 300px;
height: 200px;
border: solid 1px #1A7404;
position: absolute;
background-color: #62A80A;
}
.animated {
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
#-webkit-keyframes bounceInDown {
0% {
opacity: 0;
-webkit-transform: translateY(-2000px);
}
60% {
opacity: 1;
-webkit-transform: translateY(30px);
}
80% {
-webkit-transform: translateY(-10px);
}
100% {
-webkit-transform: translateY(0);
}
}
#keyframes bounceInDown {
0% {
opacity: 0;
transform: translateY(-2000px);
}
60% {
opacity: 1;
transform: translateY(30px);
}
80% {
transform: translateY(-10px);
}
100% {
transform: translateY(0);
}
}
.bounceInDown {
-webkit-animation-name: bounceInDown;
animation-name: bounceInDown;
}
</style>
<img id="animated-example" class="animated bounceInDown" src="https://www.facebookbrand.com/img/fb-art.jpg">
The only way to achieve that in pure CSS is to run all the animations at the same time and do some calculations:
the length of each animation should be the same and equal to the total length of desired animations (meaning if you want two 15-second animations, the CSS animations should be set to length of 30 seconds, no delays)
to control the start/end point of each animation, you need to modify the percentages accordingly - in the above case, it means that the first animation ends at 50% and that's when the second animation starts. Also, all in-between values need to be interpolated. It's easy for two animations, but you might need to use a calculator as the total number of animations increases. This is if we don't take the delays into account - the numbers change when we have a 15-second animation that will finish animation after 5 seconds, which now equals 33%, etc...
It will be more clear once you see it in action here:
.animated-example {
width: 300px;
height: 200px;
border: solid 1px #1A7404;
position: absolute;
background-color: #62A80A;
}
.animated {
animation-duration: 20s;
animation-fill-mode: both;
animation-iteration-count: infinite;
}
.bounceInLeft {
-webkit-animation-name: bounceInLeft;
animation-name: bounceInLeft;
}
.bounceInDown {
-webkit-animation-name: bounceInDown;
animation-name: bounceInDown;
}
#keyframes bounceInLeft {
0% {
opacity: 0;
transform: translateX(-2000px);
}
6% {
opacity: 1;
transform: translateX(30px);
}
8% {
transform: translateX(-10px);
}
10% {
transform: translateX(0);
}
40% {
opacity: 1;
transform: translateX(0);
}
42% {
opacity: 1;
transform: translateX(30px);
}
55% {
opacity: 0;
transform: translateX(-2000px);
}
100% {
opacity: 0;
transform: translateX(-2000px);
}
}
#keyframes bounceInDown {
0% {
opacity: 0;
transform: translateY(-2000px);
}
50% {
opacity: 0;
transform: translateY(-2000px);
}
56% {
opacity: 1;
transform: translateY(30px);
}
58% {
transform: translateY(-10px);
}
60% {
transform: translateY(0);
}
90% {
transform: translateY(0);
}
92% {
opacity: 1;
transform: translateY(30px);
}
100% {
opacity: 0;
transform: translateY(-2000px);
}
}
<img class="animated-example animated bounceInLeft" src="http://webmarketingtoday.com/wp-content/uploads/Screen-Shot-2012-05-24-at-7.31.54-AM-288x216.png">
<img class="animated-example animated bounceInDown" src="https://www.facebookbrand.com/img/fb-art.jpg">
Using animation-delay.
animation: a, b;
animation-duration: 2s, 2s;
animation-delay: 0s, 4s;
The animation b will start after 4s while animation a will start without any delay.
animation-delay would do exactly what you're looking for except for the fact that you want the animations to repeat after the last one has been completed; unfortunately there is (currently) no way to specify a delay between iterations of a looping animation.
You could, however, achieve what you're looking to do using a little bit of JavaScript, like the following. To add more animations, simply add their class names to the animations array at the start of the code.
var animations=["bounceInLeft","bounceInDown"],
count=animations.length,
classlist=document.querySelector("img").classList,
holder=document.createElement("div"),
style=window.getComputedStyle(holder),
delay=15,
current,wait,x;
holder.style.display="none";
document.body.appendChild(holder);
animate();
function animate(){
wait=0;
x=0;
while(x<count){
setTimeout(function(a){
classlist.remove(current);
classlist.add(a);
current=a;
},wait*1000,animations[x]);
holder.className=animations[x];
wait+=delay+parseInt(style.getPropertyValue("animation-duration"));
x++;
}
setTimeout(animate,wait*1000);
};
img{
animation-fill-mode:both;
height:200px;
width:300px;
}
.bounceInDown{
animation-duration:1s;
animation-name:bounceInDown;
}
.bounceInLeft{
animation-duration:2s;
animation-name:bounceInLeft;
}
#keyframes bounceInDown{
0%{
opacity:0;
transform:translateY(-2000px);
}
60%{
opacity:1;
transform:translateY(30px);
}
80%{
transform:translateY(-10px);
}
100%{
transform:translateY(0);
}
}
#keyframes bounceInLeft{
0%{
opacity:0;
transform:translateX(-2000px);
}
60%{
opacity:1;
transform:translateX(30px);
}
80%{
transform:translateX(-10px);
}
100%{
transform:translateX(0);
}
}
<img src="http://webmarketingtoday.com/wp-content/uploads/Screen-Shot-2012-05-24-at-7.31.54-AM-288x216.png">
I have managed to achieve something similar by adapting this concept by Noah Addy: http://digitalfio.github.io/Stagger.css/
You will need to work on the timings a bit to get the 15sec delay you want, but other than that it should be fairly straightforward.
I'm encountering an issue when I try to apply the animation CSS rule in tandem with the blend mode.
If I'm using the mix-blend-mode without the animation rule (animation: 0.8s cubic-bezier(0.215, 0.61, 0.355, 1) 0s 1 normal both running alpha_buildIn;) everything is working fine, I'm not sure why maybe I'm doing something wrong or might be an issue.
In my example, I've used a keyframe to define the CSS animation which later is going to be applied to the parent element which had the image context where I'm using the mix-blend-mode: color rule.
I bet on animation-fill-mode both could cause the issue but I'm not sure why.
All help is appreciated!
#keyframes alpha_buildIn {
0% {
opacity: 0;
-webkit-opacity: 0;
transform: translateX(0) translateY(0);
-webkit-transform: translateX(0) translateY(0);}
100% {
opacity: 1;
-webkit-opacity: 1;
}
}
#-webkit-keyframes alpha_buildIn {
0% {
opacity: 0;
-webkit-opacity: 0;
transform: translateX(0) translateY(0);
-webkit-transform: translateX(0) translateY(0);
}
100% {
opacity: 1;
-webkit-opacity: 1;
}
}
.wrapper {
width: 580px;
height: 400px;
background-color: rgb(218, 39, 39);
transform: scale(2.81724);
transform-origin: 0px 0px 0px;
}
.element {
animation: 0.8s cubic-bezier(0.215, 0.61, 0.355, 1) 0s 1 normal both running alpha_buildIn;
width: 100%;
height: 100%;
}
.image {
width: 100%;
height: 100%;
background-image: url(https://d1az8j93w0r5an.cloudfront.net/assets/media/8oxrr);
mix-blend-mode: color;
}
<div class="wrapper">
<div class="element">
<div class="image" />
</div>
</div>
I tried making divs as buttons that link to another subpage. I animated them so they rotate in when going on the site. Now I want to make a hover effect that scales the buttons. But it seems like it doesn´t get recognized. When I comment the animaton out it works. Is there a way to have the animation and the hover effect?
Thanks in advance!
.animation-links div {
background: #444;
margin: 10px;
text-align: center;
height: 100px;
transform: rotateY(90deg);
transform-origin: center;
}
#animation-1 {
box-sizing: border-box;
background: linear-gradient(90deg, #03a9f4, #f441a5, #ffeb3b, #03a9f4);
background-size: 400%;
animation-name: animated;
animation-duration: 1s;
animation-fill-mode: forwards;
animation-delay: 0.5s;
} animation-delay: 0.5s;
#keyframes animated {
0% {
transform: rotateY(90deg);
}
100% {
transform: rotateY(0);
}
}
}
.animation-links div:hover {
transform: scale(1.10);
}
It is because animation-fill-mode: forwards;
If the value for animation-fill-mode is not none then when animation ends it will not apply the css property values.
So I have removed animation-fill-mode: forwards; from #animation-1 style rule and transform: rotateY(90deg); from .animation-links div style rule. Also there were some extra { and style set outside the braces. I have removed those as well.
See the Snippet below:
.animation-links div {
background: #444;
margin: 10px;
text-align: center;
height: 100px;
transform-origin: center;
}
#animation-1 {
box-sizing: border-box;
background: linear-gradient(90deg, #03a9f4, #f441a5, #ffeb3b, #03a9f4);
background-size: 400%;
animation-name: animated;
animation-duration: 1s;
/*animation-fill-mode: forwards;*/
animation-delay: 0.5s;
transition: transform 1s ease;
}
#keyframes animated {
0% {
transform: rotateY(90deg);
}
100% {
transform: rotateY(0);
}
}
.animation-links div:hover {
transform: scale(2);
}
<div class="animation-links">
<div id="animation-1">Animation 1</div>
</div>
You can test it here also.
I want to float the button from right bottom to right top. While floating, it comes to the middle left like shown in attached image. I have tried a few steps but it is not working properly. Any help is highly appreciated. Thank you!
.animated {
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.animated.infinite {
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite;
}
.bounceInUp {
-webkit-animation-name: bounceInUp;
animation-name: bounceInUp;
}
#keyframes bounceInUp {
from, 60%, 75%, 90%, to {
-webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
}
from {
opacity: 0;
-webkit-transform: translate3d(0, 3000px, 0);
transform: translate3d(0, 3000px, 0);
}
60% {
opacity: 1;
-webkit-transform: translate3d(0, -20px, 0);
transform: translate3d(0, -20px, 0);
}
75% {
-webkit-transform: translate3d(0, 10px, -1000);
transform: translate3d(0, 10px, -1000);
}
90% {
-webkit-transform: translate3d(0, -5px, 0);
transform: translate3d(0, -5px, 0);
}
to {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}
<div style="float:right;" class="animated infinite bounceInUp">Button</div>
Instead of float: right, I have used position: absolute and right: for the desired effect. Check below snippet.
I have minimized the frames, please modify as per your needs.
.animated {
background-color: coral;
padding: 4px 10px;
position: absolute;
right: 0px;
-webkit-animation-duration: 5s;
animation-duration: 5s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.animated.infinite {
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite;
}
.bounceInUp {
-webkit-animation-name: bounceInUp;
animation-name: bounceInUp;
}
#keyframes bounceInUp {
from {
right: 0px;
opacity: 0;
-webkit-transform: translateY(100vh);
transform: translateY(100vh);
}
60% {
opacity: 1;
right: 40%;
}
to {
right: 0px;
-webkit-transform: translateY(0px);
transform: translateY(0px);
}
}
<div class="animated infinite bounceInUp">Button</div>
I want pendulum effect with pure CSS but it's not smooth.
Here is what I want, but with pure CSS. http://www.webdevdoor.com/demos/html5-pendulum-demo/
But I prefer more looked like natural speed variation according to it's position.
Fiddle
.bellImg {
height: 20px;
width: 20px;
position: absolute;
right: 10px;
top: 18px;
-webkit-animation-name: rotate;
animation-delay: 3s;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: linear;
-webkit-transform-origin: 50% 0%;
-webkit-animation-timing-function: ease-in-out;
}
#-webkit-keyframes rotate {
0% {
-webkit-transform: rotate(0deg);
}
10% {
-webkit-transform: rotate(10deg);
}
20% {
-webkit-transform: rotate(20deg);
}
30% {
-webkit-transform: rotate(10deg);
}
40% {
-webkit-transform: rotate(5deg);
}
50% {
-webkit-transform: rotate(0deg);
}
60% {
-webkit-transform: rotate(-5deg);
}
70% {
-webkit-transform: rotate(-10deg);
}
80% {
-webkit-transform: rotate(-20deg);
}
90% {
-webkit-transform: rotate(-10deg);
}
100% {
-webkit-transform: rotate(0deg);
}
}
<img class="bellImg" src="img/bell.png">
There are a few problems in your code:
The animation-timing-function is specified as ease-in-out. This indicates that the animation starts and end slowly but has more speed in between. For a graceful and equal move, this should be set to linear.
This is what MDN says about ease-in-out timing function:
This keyword represents the timing function cubic-bezier(0.42, 0.0, 0.58, 1.0). With this timing function, the animation starts slowly, accelerates then slows down when approaching its final state. At the beginning, it behaves similarly to the ease-in function; at the end, it is similar to the ease-out function.
There is no value called linear for animation-direction.
The splits are not equal. That is, for some 10% gap it is rotating by 10 degree whereas for others it is rotating only by 5 degree. Make the splits equal.
The below snippet with all corrections done produces a smooth animation.
.bellImg {
height: 20px;
width: 20px;
position: absolute;
right: 10px;
top: 18px;
-webkit-animation-name: rotate;
animation-delay: 3s;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: normal;
-webkit-transform-origin: 50% 0%;
-webkit-animation-timing-function: linear; /* or make your custom easing */
}
#-webkit-keyframes rotate {
0% {
-webkit-transform: rotate(0deg);
}
25% {
-webkit-transform: rotate(20deg);
}
75% {
-webkit-transform: rotate(-20deg);
}
100% {
-webkit-transform: rotate(0deg);
}
}
<img class="bellImg" src="https://cdn1.iconfinder.com/data/icons/freeline/32/bell_sound_notification_remind_reminder_ring_ringing_schedule-48.png">
Setting the animation's speed to depend on the position (that is, slow down as it reaches the extremes and quicken up in the middle) is impossible to achieve with pure CSS (even if we add extra elements).
For setting the animation's speed depending on its position, one option would be to do the following:
Add the image into a container element. Animate it such that it rotates from 20deg to -40deg.
Make the animation on the parent start earlier than the child by 1/3rd of the animation duration of both. That is, reduce the delay on parent by 0.66s. This is done to get the parent to offset initial rotation on the child. The difference is 1/3rd of animation duration because it is the time taken by parent to come to 0deg.
Change the keyframes for the image's animation such that the rotation is from -20deg to 40deg.
Set the animation-direction as alternate for both so that they go in forward direction for first iteration, in reverse for the next and so on.
Set the animation-timing-function as ease-in-out so that it slows down as it approaches the extremes. The effect is more apparent when the animation duration is increased.
.container {
position: absolute;
height: 20px;
width: 20px;
/* right: 10px; commented for demo */
top: 18px;
transform: rotate(20deg);
animation-name: rotate-container;
animation-delay: 2.33s;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-direction: alternate;
transform-origin: 50% 0%;
animation-timing-function: ease-in-out;
}
.bellImg {
height: 100%;
width: 100%;
transform: rotate(-20deg);
animation-name: rotate;
animation-delay: 3s;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-direction: alternate;
transform-origin: 50% 0%;
animation-timing-function: ease-in-out;
}
#keyframes rotate {
0% {
transform: rotate(-20deg);
}
100% {
transform: rotate(40deg);
}
}
#keyframes rotate-container {
0% {
transform: rotate(20deg);
}
100% {
transform: rotate(-40deg);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='container'>
<img class="bellImg" src="https://cdn1.iconfinder.com/data/icons/freeline/32/bell_sound_notification_remind_reminder_ring_ringing_schedule-48.png">
</div>
Prefix-free library is used in the snippet only to avoid browser prefixes.
The equation involved in the movement of a pendulum is a sinusoidal movement.
You can get this movement with the following animation
.base {
height: 600px;
width: 10px;
position: absolute;
animation: base 10s infinite linear;
background-color: lightgray;
transform: translateX(588px);
}
#keyframes base {
from {transform: translateX(77px);}
to {transform: translateX(760px);}
}
.element {
height: 10px;
width: 10px;
background-color: green;
border-radius: 100%;
animation: element 10s infinite;
transform: translateY(553px);
}
#keyframes element {
from {transform: translateY(294px); animation-timing-function: ease-out;}
25% {transform: translateY(36px); animation-timing-function: ease-in;}
50% {transform: translateY(294px); animation-timing-function: ease-out;}
75% {transform: translateY(553px); animation-timing-function: ease-in;}
to {transform: translateY(294px);}
}
.ref {
width: 800px;
height: 600px;
background-image: url(http://i.stack.imgur.com/Fx4bR.png);
background-size: cover;
}
<div class="base">
<div class="element">
</div>
</div>
<div class="ref"></div>
I had to use some hand-worked values to adjust for the background-image, but the key idea is in the timing functions.
If the preset function is not what you want, you can set a cubic bezier and adjust it as you want.
.base {
height: 600px;
width: 10px;
position: absolute;
animation: base 10s infinite linear;
background-color: lightgray;
transform: translateX(588px);
}
#keyframes base {
from {transform: translateX(77px);}
to {transform: translateX(760px);}
}
.element {
height: 10px;
width: 10px;
background-color: green;
border-radius: 100%;
animation: element 10s infinite;
transform: translateY(553px);
}
#keyframes element {
from {transform: translateY(294px);
animation-timing-function: cubic-bezier(0.1, 0.3, 0.3, 1);}
25% {transform: translateY(36px);
animation-timing-function: cubic-bezier(0.7, 0.0, 0.9, 0.7);}
50% {transform: translateY(294px);
animation-timing-function: cubic-bezier(0.1, 0.3, 0.3, 1);}
75% {transform: translateY(553px);
animation-timing-function: cubic-bezier(0.7, 0.0, 0.9, 0.7);}
to {transform: translateY(294px);}
}
.ref {
width: 800px;
height: 600px;
background-image: url(http://i.stack.imgur.com/Fx4bR.png);
background-size: cover;
}
<div class="base">
<div class="element">
</div>
</div>
<div class="ref"></div>
This is the image used for reference
And this would be the 2 timing functions applied to a pendulum
.test {
height: 400px;
width: 10px;
background-color: lightgreen;
display: inline-block;
margin: 10px 100px;
transform-origin: center top;
}
.anim1 {
animation: oscil1 6s infinite;
}
.anim2 {
animation: oscil2 6s infinite;
}
#keyframes oscil1 {
from {transform: rotate(0deg); animation-timing-function: cubic-bezier(0.1, 0.3, 0.3, 1);}
25% {transform: rotate(20deg); animation-timing-function: cubic-bezier(0.7, 0.0, 0.9, 0.7);}
50% {transform: rotate(0deg); animation-timing-function: cubic-bezier(0.1, 0.3, 0.3, 1);}
75% {transform: rotate(-20deg); animation-timing-function: cubic-bezier(0.7, 0.0, 0.9, 0.7);}
to {transform: rotate(0deg);}
}
#keyframes oscil2 {
from {transform: rotate(0deg); animation-timing-function: ease-out;}
25% {transform: rotate(20deg); animation-timing-function: ease-in;}
50% {transform: rotate(0deg); animation-timing-function: ease-out;}
75% {transform: rotate(-20deg); animation-timing-function: ease-in;}
to {transform: rotate(0deg);}
}
<div class="test anim1"></div>
<div class="test anim2"></div>
I haven't used CSS here, but since (it seems like) you just want to avoid libraries, I've implemented it in native JS. It uses the Math.sin() method to tween the values smoothly. As you can see, the effect is very smooth and requires very little code.
var img = document.querySelector( '.bellImg' ),
start = 0;
function sine(){
img.style.transform = "rotate(" + 20 * Math.sin( start ) + "deg)";
start += 0.05;
setTimeout(sine, 1000/30)
}
setTimeout( sine, 3000 );
.bellImg {
height: 20px;
width: 20px;
position: absolute;
left: 10px;
top: 18px;
}
<img class="bellImg" src="https://cdn1.iconfinder.com/data/icons/freeline/32/bell_sound_notification_remind_reminder_ring_ringing_schedule-48.png">
Hope this helps!