How to capture css3 animation iteration if alternate - javascript

I'm developing a small application based on css3 animation with keyframes. I'm interested only in Chrome behavior. I can use jQuery or vanilla Javascript.
If I set an animation "infinite" along with "alternate" property on an element, the event animationiteration is fired two times before reaching the initial state of the element, since the iteration is counted even when the animation "reverses" itself.
The only solution I came up, to know when the initial state is reached, is to track the iteration with a variable:
CSS example:
.animate{
-webkit-animation: k-wide 0.6s ease-in infinite alternate;
animation: k-wide 0.6s ease-in infinite alternate;
}
#-webkit-keyframes k-wide{
from { transform: scale(1); }
to { transform: scale(1.2); }
}
#keyframes k-wide{
from { transform: scale(1); }
to { transform: scale(1.2); }
}
Javascript example:
var initialState = true;
elem = $('#myAnimatedElem')
//event listener
.on('webkitAnimationIteration animationiteration',function(){
initialState = !initialState ;
if (initialState) runMyCustomScript();
})
//start animation
.addClass('animate');
The question is: is there another method to catch the initial state? Is there a more efficient one? I was wondering if I missed some event handler.

As far as I know, no, you can't.
Alternatives:
1) Remove alternate from the animation. Make it alternate using more keyframes
#-webkit-keyframes k-wide {
from { transform: scale(1); }
50% { transform: scale(1.2); }
to { transform: scale(1); }
}
2) Use timeelapsed from the event and do some math.
Note that in the first alternative, the timing function is preserved.
from the standard
For a keyframed animation, the ‘animation-timing-function’ applies
between keyframes, not over the entire animation. For example, in the
case of an ‘ease-in-out’ timing function, an animation will ease in at
the start of the keyframe and ease out at the end of the keyframe. An
‘animation-timing-function’ defined within a keyframe block applies to
that keyframe, otherwise the timing function specified for the
animation is used.
In the following demo, an alternate animation moves the red box. An independent, single animation, move the shadow around it.
Notice that the second part of the animation needs to have an ease-out (because the original is reversed
.base {
width: 400px;
height: 800px;
border: solid 1px;
display: inline-block;
position: absolute;
left: 10px;
top: 10px;
}
.move {
position: absolute;
width: 100%;
height: 10px;
border: solid 1px lightgreen;
-webkit-animation: move 10s infinite linear;
animation: move 10s infinite linear;
}
#-webkit-keyframes move {
from {top: 0px;}
to {top: 100%;}
}
#keyframes move {
from {top: 0px;}
to {top: 100%;}
}
.test1, .test2 {
position: absolute;
width: 10px;
height: 10px;
top: 0px;
left: 100%;
}
.test1 {
background-color: red;
-webkit-animation: test1 5s infinite ease-in alternate;
animation: test1 5s infinite ease-in alternate;
}
.test2 {
box-shadow: 0px 0px 0px 5px blue;
-webkit-animation: test2 10s infinite ease-in;
animation: test2 10s infinite ease-in;
}
#-webkit-keyframes test1 {
from {left: 0px;}
to {left: 100%;}
}
#-webkit-keyframes test2 {
from {left: 0px;}
50% {left: 100%; -webkit-animation-timing-function: ease-out;}
to {left: 0px;}
}
#keyframes test1 {
from {left: 0px;}
to {left: 100%;}
}
#keyframes test2 {
from {left: 0px;}
50% {left: 100%; animation-timing-function: ease-out;}
to {left: 0px;}
}
<div class="base">
<div class="move">
<div class="test1"></div>
</div>
</div>
<div class="base" id="base2">
<div class="move">
<div class="test2"></div>
</div>
</div>

Related

how make another transform animation after fade-in and existing transform?

hello I have a text which has fade-in and transform(translate) animation so after the fade-in and transform animation I want a moving text like up and down a lite, not too much in an infinite loop like you are waiting for loading of something
.imgContaner {
width: 100%;
height: 86.6vh;
background-image: url("https://upload.wikimedia.org/wikipedia/commons/8/85/Sky-3.jpg");
background-size: cover;
background-repeat: no-repeat;
position: relative;
overflow: hidden;
}
.imgContaner p {
color: white;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 3em;
text-align: center;
text-shadow: -1px 0 #000000, 1px 0 #000000, 0 1px #000000, 0 -1px #000000;
animation: 4s ease-in-out test ;
opacity: 1;
}
#keyframes test {
from {
transform: translate(-50% , 400%);
opacity: 0;
}
to {
transform: translate(-50%, -50%);
opacity: 1;
}
}
<div className={classes.imgContaner}>
<p>Make Your Dreams Come True</p>
</div>
You can do this in two ways with css.
First way: Add more frames in your #keyframes definition. Instead of using from and to properties, use percentages. For example:
#keyframes test {
0% {
transform: translate(-50% , 400%);
opacity: 0;
}
50% {
transform: translate(-50%, -50%);
opacity: 1;
}
75% {
Do other stuff...
}
100% {
Do other stuff...
}
}
Second way: Chain animations to play after the other one. By adding delay to the second animation equal to the duration of first animation. Like:
animation: test 4s ease-in-out, otherAnim 2s ease-in-out 4s;
Note that you can't do it this way:
animation: test 4s ease-in-out;
animation: otherAnim 2s ease-in-out 4s;
This way first one will be overwriten by second one. Which is not what you want.

Keyframe animation :hover doesn't obey the "ease-out" part of the animation on mouse-out

I have a 3 chevron animation sequence set up for a back button I designed. The animation triggers on hover exactly the way I want it to but it doesn't respect the ease-out part of the animation property when I hover off of the button. I know that typically with CSS animations you fix this by putting the animation on the actual element and not the :hover state but the problem with that is that the keyframe animation triggers on page load and gets a little wonky on :hover. Is there a mouse-out or hover-out-like state that I could use so that when the user moves away from the button the animation eases out or even reverses? I tried adding animation-direction: reverse; property to the base elements but that doesn't do anything, probably because it doesn't know what animation I'm referring to because it's not present in the base elements for the reasons above. Do I possibly need some CSS or javascript to prevent the animation from triggering until the :hover state actually occurs and then I could place the animation in the base elements instead of the :hover state?
https://jsfiddle.net/astombaugh/L7k1r63f/54/
<body style="background-color: #214365">
<div class="backBtn">
<div class="chevronContainer">
<div class="backBtnChevronTop"></div>
<div class="backBtnChevronMid"></div>
<div class="backBtnChevronFar"></div>
</div>
Back
</div>
</body>
#import url('https://fonts.googleapis.com/css2?family=Oswald:wght#700&display=swap');
.backBtn {
font-family: Oswald, Univers, Helvetica Neue, Helvetica, Arial;
display: inline-block;
position: absolute;
left: 4rem;
font-weight: 700;
width: auto;
height: auto;
color: white;
background-color: transparent;
padding: 0.2rem 0em 0.1rem 0em;
margin: 0rem 0rem 0rem 0rem;
text-align: center;
text-decoration: none;
font-size: 1.6em;
word-spacing: normal;
cursor: pointer;
}
.chevronContainer {
display: inline-block;
position: relative;
transform: translateY(-1.3rem) translateX(-1rem);
}
.backBtnChevronTop {
content: url(https://i.imgur.com/YHZi17i.png);
filter: invert(1);
position: absolute;
opacity: 1;
height: 1.33rem;
width: 1.33rem;
}
.backBtnChevronMid {
content: url(https://i.imgur.com/YHZi17i.png);
filter: invert(1);
position: absolute;
opacity: 0;
height: 1.33rem;
width: 1.33rem;
animation-direction: reverse;
}
.backBtnChevronFar {
content: url(https://i.imgur.com/YHZi17i.png);
filter: invert(1);
position: absolute;
opacity: 0;
height: 1.33rem;
width: 1.33rem;
animation-direction: reverse;
}
.backBtn:hover .backBtnChevronMid {
animation: animateChevronMid 0.6s ease-in-out;
animation-fill-mode: forwards;
}
.backBtn:hover .backBtnChevronFar {
animation: animateChevronFar 0.6s ease-in-out;
animation-fill-mode: forwards;
}
#keyframes animateChevronTop {
0% {
transform: translateX(0rem);
opacity: 0;
}
70%,
100% {
transform: translateX(0);
opacity: 1;
}
}
#keyframes animateChevronMid {
0% {
transform: translateX(0);
opacity: 0;
}
70%,
100% {
transform: translateX(-0.7rem);
opacity: 1;
}
}
#keyframes animateChevronFar {
0% {
transform: translateX(0);
opacity: 0;
}
70%,
100% {
transform: translateX(-1.4rem);
opacity: 1;
}
}
You can probably resolve this by adding the transition on element when there is no hover at the moment and tweak a little the keyframes. Like this:
.backBtn .backBtnChevronMid {
animation: animateChevronMid2 0.6s ease-in-out;
animation-fill-mode: forwards;
}
.backBtn .backBtnChevronFar {
animation: animateChevronFar2 0.6s ease-in-out;
animation-fill-mode: forwards;
}
#keyframes animateChevronMid2 {
0% {
transform: translateX(-0.7rem);
opacity: 1;
}
70%,
100% {
transform: translateX(0);
opacity: 0;
}
}
#keyframes animateChevronFar2 {
0% {
transform: translateX(-1.4rem);
opacity: 1;
}
70%,
100% {
transform: translateX(0);
opacity: 0;
}
}
this additional keyframes are exact opposite of the keyframes that you have done. And they do apply when you move your cursor from the element (so on hover off so to speak).
Jacck is right and beat me to it.
You can use that, and add a fadeIn transition to the back button itself. It's hacky but put this on the back button:
animation: fadeIn 0.6s ease-in-out;
And tweak the animation accordingly. It'll run once. If you don't want a fade just move the "stop" close to the end and this controls the container that holds the other animations so your whole effect won't show until it has loaded:
#keyframes fadeIn {
0% {opacity:0;}
95% {opacity: 0}
100% {opacity:1;}
}

Getting Top/Right values ​from a css-animated element

I'm trying to get the Top and Right values from an element being rotated by a CSS animation, for this I am using the following code:
HTML:
<div id="ball1"> </div>
CSS:
#keyframes spin {
0% {transform: rotate(0deg);}
100% {transform: rotate(360deg);}
}
#ball1 {
transform-origin: center right;
animation: spin 2.5s linear 0s infinite forwards;
position: relative;
background-color: #7883f7;
width: 10;
height: 10;
border-radius: 10px;
}
Javascript:
console.log(window.getComputedStyle(document.getElementById("ball1"), null).top);
console.log(window.getComputedStyle(document.getElementById("ball1"), null).right);
However it returns a value of 0px, I wanted to get the value from Right and Top as if I was manually setting them (and not by the transform animation).
If this is not possible, is there a way to simulate a "circle" rotation and return the right/top values without using the transform?
ie:
https://66.media.tumblr.com/fb22b61bcbca3785a515e86c2276451b/tumblr_inline_pmimnjEvbK1v6q8wn_1280.gif?fbclid=IwAR2zjgE0hfB8emWOg0f6TOcQb8DWGbEvu9IQOr92fMq4HmMKjiAQRQzLmI0
Use getBoundingClientRect():
const ball = document.getElementById("ball");
setInterval(() => {
const rect = ball.getBoundingClientRect();
console.log(rect.top, rect.right);
}, 300);
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#ball {
transform-origin: center right;
animation: spin 2.5s linear 0s infinite forwards;
position: relative;
background-color: #7883f7;
width: 10px;
height: 10px;
border-radius: 10px;
}
<div id="ball"></div>
Here is an approximation using top/left. The trick is to animate each property individually alternating the ease function to simulate the circular path:
.box {
width:100px;
height:100px;
position:relative;
}
#ball1 {
animation:
Atop 2.5s infinite,
Aleft 2.5s infinite;
position: absolute;
background-color: #7883f7;
width: 10px;
height: 10px;
border-radius: 10px;
}
#keyframes Atop {
0%,50%,100% {
top:50%;
animation-timing-function: ease-out;
}
25% {
top:0%;
animation-timing-function: ease-in;
}
75% {
top:100%;
animation-timing-function: ease-in;
}
}
#keyframes Aleft {
0%,100% {
left:0;
animation-timing-function: ease-in;
}
25%,75% {
left:50%;
animation-timing-function: ease-out;
}
50% {
left:100%;
animation-timing-function: ease-in;
}
}
<div class="box">
<div id="ball1"> </div>
</div>

How to apply a hover effect on an element which has already been handled by an forward animation?

I have a text and block in an animation of an SVG element.
Here in my example i simplified everything.
I want to have one initial animation and afterwards a hover animation on the block element. The initial animation is fine as it is. (use chrome to have equals measurements). But after the initial animation the user should be able to hover the block and the block itself should resize (which is also working already) and the text should get an opacity of 1. But this won't work since the opacity is already set by the keyframe animation.
Any suggestions on how to work around on this one?
I don't mind if i use JS or CSS or any frameworks. I don't rely on CSS animations. Just used them because i thought i'd be cleaner.
Important Edit: I forgot a simple but very important thing. Before the animation there are some other animations on different elements. So i have a delay of let's say 2 seconds. Before the animation starts, the opacity should be 0 so the text is not visible until the animation starts. Sorry, forgot about that!
.text{
font-weight: bold;
opacity: 0;
transition: all .8s;
animation: showText 3s ease-in-out forwards;
animation-delay: 2s;
}
.text:hover{
opacity: 1;
transition: all .8s;
}
.block{
top: 50px;
left: 50px;
position: absolute;
height: 20px;
width: 20px;
background-color: red;
transition: all .8s;
animation: popup 3s ease-in-out;
animation-delay: 2s;
}
.block:hover{
transform: scale(2);
transition: all .8s;
}
#keyframes showText {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0.3;
}
}
#keyframes popup {
0% {
transform: scale(1);
}
50% {
transform: scale(2);
}
100% {
transform: scale(1);
}
}
<div class='text'>
Foo Bar!
</div>
<div class='block'>
</div>
codepen.io link (same code as above): https://codepen.io/jdickel/pen/xJbQrY
Instead of a forwards animation, you can simply set the initial opacity to 0.3.
EDIT:
I'm fairly confident that forwards animation styles can't be easily overridden (though I'm unable to find it in documentation for some reason), so you could do similarly to what was originally suggested and just extend the time of the animation like so:
.text{
font-weight: bold;
/* Start out at 0.3 */
opacity: 0.3;
transition: all .8s;
/* 2s + 3s = 5s */
animation: showText 5s ease-in-out; /* no forwards */
}
.text:hover{
opacity: 1;
transition: all .8s;
}
.block{
top: 50px;
left: 50px;
position: absolute;
height: 20px;
width: 20px;
background-color: red;
transition: all .8s;
animation: popup 3s ease-in-out;
}
.block:hover{
transform: scale(2);
transition: all .8s;
}
#keyframes showText {
0% {
opacity: 0;
}
50% {
opacity: 0;
}
/* Note the new animation keyframe locations */
70% {
opacity: 1;
}
100% {
opacity: 0.3;
}
}
#keyframes popup {
0% {
transform: scale(1);
}
50% {
transform: scale(2);
}
100% {
transform: scale(1);
}
}
<div class='text'>
Foo Bar!
</div>
<div class='block'>
</div>
First, you need to remove forwards from the .text animation. You can use Javascript's mouseenter and mouseleave events to set the text's opacity when .block is hovered over.
.text{
font-weight: bold;
opacity: 0;
transition: all .8s;
animation: showText 3s ease-in-out;
animation-delay: 2s;
}
.text:hover{
opacity: 1;
transition: all .8s;
}
.block{
top: 50px;
left: 50px;
position: absolute;
height: 20px;
width: 20px;
background-color: red;
transition: all .8s;
animation: popup 3s ease-in-out;
animation-delay: 2s;
}
.block:hover{
transform: scale(2);
transition: all .8s;
}
#keyframes showText {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0.3;
}
}
#keyframes popup {
0% {
transform: scale(1);
}
50% {
transform: scale(2);
}
100% {
transform: scale(1);
}
}
<div class='text' id="text" onmouseenter="function1()" onmouseleave="function2()">
Foo Bar!
</div>
<div class='block' onmouseenter="function1()" onmouseleave="function2()">
</div>
<script>
setTimeout(function(){
document.getElementById("text").style.opacity = "0.3";
}, 3000)
function function1(){
document.getElementById("text").style.opacity = "1";
}
function function2(){
document.getElementById("text").style.opacity = "0.3";
}
</script>

Ocillate function between two values javascript

I need to have a growing and decreasing circle in made in javascript.
My idea is to use a div with
border-radius : 50%
To get a circle. I need to make it scale from 0.2 to 1 every [x] seconds .
Like It grows from 0.2 to 1 in 5 seconds, then it decreases from 1 to 0.2 in5 seconds too. THen the movement starts again.
I think i have to use sin or cos functions but i don't know how to get this interval depending on time.
I need it to be coupled with a javascript timing function, so that when i satr a timer, the animation begins, and when I pause it it pauses the animation.
Thanks for advice !
One more example of CSS animation which probably better option then javascript:
.circle {
background: coral;
height: 100px;
width: 100px;
border-radius: 50%;
-webkit-animation: pulse 1s ease infinite alternate;
animation: pulse 1s ease infinite alternate;
}
#-webkit-keyframes pulse {
from {
-webkit-transform: scale(1);
}
to {
-webkit-transform: scale(.2);
}
}
<div class="circle"></div>
For Firefox you will need to add prefixless rule
#keyframes pulse {
from { transform: scale(1); }
to { transform: scale(.2); }
}
You could do this with CSS3 animations. Look at this example and change it until it does exactly how you want it.
#circle {
-webkit-animation: oscillate 10s infinite;
animation: oscillate 10s infinite;
border: 1px solid black;
height: 100px;
width: 100px;
}
#-webkit-keyframes oscillate {
0% { border-radius: 20%; }
50% { border-radius: 100%; }
100% { border-radius: 20%; }
}
#keyframes oscillate {
0% { border-radius: 20%; }
50% { border-radius: 100%; }
100% { border-radius: 20%; }
}
<div id="circle">Hi</div>
Use a CSS3 animation set on infinite. Check this fiddle.
#-webkit-keyframes mymove {
0%, 100% { -webkit-transform: scale(1); }
50% { -webkit-transform: scale(0.2); }
}
#keyframes mymove {
0%, 100% { transform: scale(1); }
50% { transform: scale(0.2); }
}
div {
width: 100px;
height: 100px;
background: red;
position: relative;
-webkit-animation: mymove 5s infinite; /* Chrome, Safari, Opera */
animation: mymove 5s infinite;
border-radius:60px;
}

Categories