Transitioning from animated to not animated in Chrome vs Firefox - css

Firefox has a nice behavior when turning off animation in a transition enabled element, it takes the element wherever it is and transition back to original form.
In Chrome it just jumps without transitioning.
Why the inconsistency? Is there any way to replicate in Chrome without using too much JS?
.wrapper {
width: 400px;
height: 200px;
}
.move {
width: 100px;
height: 100px;
position: absolute;
background-color: #f66;
transition: 1s;
cursor: pointer;
}
.move {
animation: move 2s linear infinite;
}
.wrapper:hover .move {
animation: none;
}
#keyframes move {
50% {
transform: translateX(200px);
}
}
<div class="wrapper">
<div class="move"></div>
</div>

$(".spinny").bind("webkitAnimationEnd mozAnimationEnd animationEnd", function(){
$(this).removeClass("spin")
})
$(".spinny").hover(function(){
$(this).addClass("spin");
})
div {
width: 100px;
height: 100px;
background-color: #f66;
transition: 1s;
cursor: pointer;
}
.spin {
animation: spin 1s linear 1;
}
#keyframes spin {
100% {
transform: rotate(180deg);
}
}
<script src="https://code.jquery.com/jquery-3.1.0.js"></script><div class="spinny"></div>
Taken from this answer, JS can be used to add and remove classes on hover and animation finish, respectively. jQuery is used in this example, but it is not necessary for the functionality.
EDIT: Now without jQuery, this will play the animation whenever hovered over by remembering the state and checking after the end of every animation.
hover = 0;
s = document.getElementById("spinny");
s.addEventListener("animationend", function(){
s.className = "";
if (hover)
setTimeout(function(){s.className = "spin"},0);
})
s.onmouseover = function(){
s.className = "spin";
hover = 1;
}
s.onmouseout = function(){
hover = 0;
}
div {
width: 100px;
height: 100px;
background-color: #f66;
transition: 1s;
cursor: pointer;
}
.spin {
animation: spin 1s linear 1;
}
#keyframes spin {
100% {
transform: rotate(180deg);
}
}
<div id="spinny"></div>

Add transition along side your transform
.rotate {
width: 200px;
height: 200px;
transform: rotate(0deg);
transition: 0.5s ease all;
background-color: #222;
}
.rotate:hover {
transform: rotate(20deg);
transition: 0.5s ease all;
}
This should prevent it from jumping.

Related

I'm trying to correct the animation duration and timeline of infinite flip, rotation, spin and disappearance of three images in pure CSS

I tested with the following answers:
Pure CSS rotate animation broken while in infinite loop
Stop infinite CSS3 animation and smoothly revert to initial state
CSS Image Fade Animation Only Runs First Time,
but the animation duration and timeline (for example, from step by step, from start to end) did not work. The three images need to be in the same place at once.
I wanted to use https://codepen.io/jay-bee-why/pen/Htejl, but unfortunately I do not want to use jQuery. I am CSS and JavaScript purist.
An image is worth a thousand words. You will understand easily the image. I also provide very small snippet code box.
.flipping-images
{
align-items: center;
display: flex;
flex-flow: row nowrap;
height: 80%;
justify-content: center;
/* opacity: 0; */
position: relative;
transform: translateX(100%);
width: 22%;
}
.show-l
{
animation: show-image 5s ease-in-out 300ms infinite;
left: 0;
position: absolute;
transform-origin: left;
}
.hide-l
{
animation: hide-image 5s ease-in-out 800ms infinite;
position: absolute;
transform-origin: left;
}
.hide-l2
{
animation: hide-image 5s ease-in-out 600ms infinite;
position: absolute;
transform-origin: right;
}
#keyframes hide-image
{
0%
{
left: 0;
transform: rotateY(0deg);
}
30%
{
left: 10%;
transform: rotateY(0deg);
}
50%
{
opacity: 1;
}
100%
{
left: -100%;
opacity: 0;
transform: rotateY(90deg);
}
}
#keyframes show-image
{
0%
{
left: 100%;
transform: rotateY(90deg);
}
30%
{
left: 110%;
transform: rotateY(90deg);
}
100%
{
left: 0%;
transform: rotateY(0deg);
}
}
<div class="flipping-images">
<img class="show-l" src="https://via.placeholder.com/432x864/fdc34f/FEFEFE?text=1">
<img class="hide-l2" src="https://via.placeholder.com/432x864/3e72ff/FEFEFE?text=2">
<img class="hide-l" src="https://via.placeholder.com/432x864/222222/FEFEFE?text=3">
</div>
I'm not sure I understand your image since it says the second image should disappear but it also says the animation is infinite. I hope it's working as you intended, if not just leave a comment on what needs to be fixed.
I'm using the animationend event to control the animations.
var counter = 1;
var div = document.querySelector('.flipping-images');
var images = document.querySelectorAll('.flipping-images img');
var showNext = function () {
counter++;
if (counter > 3) counter = 1;
div.classList.remove('image1', 'image2', 'image3')
div.classList.add('image'+counter);
};
for (var img of images) {
img.addEventListener('animationend', showNext);
img.addEventListener('click', showNext);
}
document.querySelector('#next').addEventListener('click', showNext);
.flipping-images {
perspective: 300px;
}
.flipping-images img {
display: none;
animation: rotate 5s linear 1;
}
.flipping-images.image1 img:nth-child(1),
.flipping-images.image2 img:nth-child(2),
.flipping-images.image3 img:nth-child(3) {
display: block;
}
.flipping-images.image2 img:nth-child(2) {
animation: rotate 5s linear infinite;
}
#keyframes rotate {
0% { transform: rotateY(-45deg); }
100% { transform: rotateY(45deg); }
}
button {
margin: 1em;
}
<div class="flipping-images image1">
<img src="https://via.placeholder.com/100x100/fdc34f/FEFEFE?text=1">
<img src="https://via.placeholder.com/100x100/3e72ff/FEFEFE?text=2">
<img src="https://via.placeholder.com/100x100/222222/FEFEFE?text=3">
</div>
<button id="next">Next</button>

CSS fade transition (no jQuery)

I'm trying to create a visual transition between content changes in a toy SPA I'm writing. To that end, I define a simple class for animating the opacity of an element.
.fade {
transition: opacity 1.5s;
}
In my render function, I now change the opacity of my outlet div after content changes like so:
function render(content) {
var outlet = document.getElementById("outlet");
outlet.classList.remove("fade");
outlet.style.opacity = 0;
outlet.innerHTML = content;
outlet.classList.add("fade");
outlet.style.opacity = 1;
}
Unfortunately, the animation never fires. When I delay changing the opacity to 1 via setTimeout for 10ms, say, it works sometimes if I don't change the content again while the animation is still running, indicating a timing issue/race condition.
I used a similar approach in the past to fade out messages, but there I intentionally delayed changing the opacity by a few seconds so users could read the message before it starts fading out.
Pure CSS animation fadeIn
li {
position: absolute;
top: 50%;
left: 50%;
margin-top: -75px;
}
.logo {
width: 300px;
height: 150px;
background: red;
margin-left: -150px;
z-index: 30;
-webkit-animation: fade-in-slogan 4s .2s ease-in forwards;
-moz-animation: fade-in-slogan 4s .2s ease-in forwards;
animation: fade-in-slogan 4s .2s ease-in forwards;
}
.menu {
width: 600px;
height: 150px;
background: blue;
margin-left: -300px;
opacity: 0;
-webkit-animation: fade-in-menu 3s 4s ease-out forwards;
-moz-animation: fade-in-menu 3s 4s ease-out forwards;
animation: fade-in-menu 3s 4s ease-out forwards;
}
#-webkit-keyframes fade-in-slogan {
0% { opacity: 0; }
30% { opacity: 1; }
50% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; }
}
#-webkit-keyframes fade-in-menu {
0% { display: block; opacity: 0; }
30% { display: block; opacity: .3; }
60% { display: block; opacity: .6; }
80% { display: block; opacity: .8; }
100% { display: block; opacity: 1; }
}
<ul class"main">
<li class="logo"></li>
<li class="menu"></li>
</ul>
Try this, I hope this will solve the issue
.fade{
animation-name: example;
animation-duration: 4s;}
#keyframes example {
from {opacity:1}
to {opacity:0;}
}
div{
width:200px;
height:200px;
background:#000;
}
<html>
<head>
</head>
<body>
<div class="fade"></div>
</body>
</html>
I've solved it now inspired by Muhammad's answer. I defined the fade class as follows:
.fade {
animation-name: fadein;
animation-duration: 1.25s;
#keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
}
Then in the render function, I do
function render(content) {
outlet.classList.remove("fade");
outlet.innerHTML = "";
setTimeout(() => {
outlet.classList.add("fade");
outlet.appendChild(content);
}, 100);
}
Even though this adds an additional delay before the new content actually starts to fade in, it seems the most elegant and concise solution to me.

How to make CSS animation to do vice versa after being completed?

The code below is a part of my code :
.myBox:hover::after {
animation-name: underline;
animation-duration: 350ms;
animation-fill-mode: forwards;
}
#keyframes underline {
from { width: 0; }
to { width: 100%; }
}
It works nicley, but I want to do it vice versa when animation completed, I mean when it finished then width should be 0 again, In fact for this part I want to do it when my element is not hovered. Which property can help me ?
You need to use alternate and run 2 iterations of the animation:
.box {
height:200px;
background:red;
animation: underline 500ms alternate 2 forwards;
}
#keyframes underline {
from { width: 0; }
to { width: 100%; }
}
<div class="box">
</div>
Or consider the use of transition if you want the effect on hover:
.box {
height: 200px;
background: red;
width: 0;
transition: 500ms;
}
body:hover .box {
width: 100%;
}
<div class="box">
</div>
You can specify multiple values for animations rather then from and to using percentage:
#keyframes underline {
0%, 100% { width: 0; }
50% { width: 100%; }
}
More detailed information can be found here.
.myBox:hover::after {
animation-name: underline infinite;
animation-duration: 350ms;
animation-fill-mode: forwards;
}
#keyframes underline {
from { width: 0; }
to { width: 100%; }
}
You infinite for this

:hover rotation CSS keep position on uncover

I am rotating an object with CSS upon hovering, and would like for it to remain in it's new position as you unhover it. I have searched around, but the only thing I could find is css :hover rotate element and keep the new position, which seems to go above and beyond.
Is this effect possible to achieve purely with CSS? I want the icon to remain at the 180 position once you stop hovering.
I used this code:
i.fa.fa-globe:hover {
color: #e9204f;
transition: 0.9s;
transform: rotatey(180deg);
}
Also it's a font-awesome icon if this makes any difference.
Edit - The easy CSS solution for everyone else who needs it (taken from the comments):
.lovernehovermarket i.fa.fa-rocket {
transform: rotate(0deg);
transition: transform 999s;
}
I had a circular icon that I wanted to rotate on every hover, not just the first, and not rotate when un-hovered.
Original
I saw this problem when I had CSS that looked like this
.icon {
transition: transform 0.5s;
}
.icon:hover {
transform: rotate(90deg);
}
Solution
The simple solution was to put the transition inside the :hover psuedo class
.icon:hover {
transition: transform 0.5s;
transform: rotate(90deg);
}
Boom, done!
This works because I was originally setting the transition to be 0.5s by default. In this case, that means both forward and backward. By putting the transition property inside the hover, I have a 0.5s transition when hover is activated, but a 0s transition (the default) when the icon is un-hovered. Having a 0s hover means it just instantly snaps back to position, invisibly to the viewer.
I you want a pure CSS solution, you can set a transtion time to go back to the base state quite high.
It's not for ever, but it's pretty close for most users:
.test {
display: inline-block;
margin: 10px;
background-color: tomato;
transform: rotate(0deg);
transition: transform 999s 999s;
}
.test:hover {
transform: rotate(90deg);
transition: transform 0.5s;
}
<div class="test">TEST</div>
You also need an initial transform state in the regular CSS of your element, so that it can transform between two defined states:
.rotate {
width: 20px;
height: 100px;
background: blue;
transition: 0.9s;
transform: rotate(0deg);
}
.rotate:hover {
transform: rotate(180deg);
}
body {
padding: 100px;
}
<div class="rotate"></div>
If you want to maintain the rotated state, you may have to use a little JQuery to check when the transition ends and change the class so it doesn't revert back to its original state on blur.
This way the div is rotated once and then its class is changed to maintain the rotated state.
$('.rotate').hover(function () {
$(this).addClass("animate");
$(this).one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend',
function(e) {
$(this).removeClass('rotate').addClass('rotated');
});
});
.rotate {
width: 100px;
height: 100px;
background: gold;
transition-property: transform;
transition-duration: 1.5s;
transition-timing-function: linear;
}
.animate {
animation: rotate 1s linear;
transform: rotate(180deg);
animation-play-state: running;
}
.rotated
{
width: 100px;
height: 100px;
background: gold;
transform: rotate(180deg);
}
body {
padding: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="rotate">some text</div>
Use an animation, and apply it using JS event listener, when the element is hovered (mouseover event). When the element is hovered for the 1st time, remove the event listener:
var rect = document.querySelector('.rectangle')
function rotate() {
this.classList.add('rotate');
rect.removeEventListener('mouseover', rotate);
}
rect.addEventListener('mouseover', rotate);
.rectangle {
width: 100px;
height: 100px;
background: gold;
}
.rotate {
animation: rotate 0.5s linear;
}
#keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(180deg);
}
}
body {
padding: 30px;
}
<div class="rectangle"></div>
What worked for me was to put the transform not on hover but on the main css.
not:
#gear {
width: 3vh;
height: auto;
cursor: pointer;
&:hover {
transform: rotate(45deg);
transition: transform 200ms;
}
}
but
#gear {
width: 3vh;
height: auto;
cursor: pointer;
transition: transform 200ms;
&:hover {
transform: rotate(45deg);
}
}

Don't let the :after animation rule fire on page load

I have the following code that does almost everything I need it to do.
.mouse {
margin-bottom:20px;
padding:5px;
overflow:hidden;
width:200px;
}
.mouse:after {
content:"";
height:2px;
display:block;
background:red;
margin-top:5px;
transform:translateX(100%);
animation: hoverOut 1.5s ease 1;
}
.mouse:hover:after {
animation: hoverIn 1.5s ease 1;
}
#keyframes hoverOut {
0% {transform:translateX(0%);}
100% {transform:translateX(100%);}
}
#keyframes hoverIn {
0% {transform:translateX(-100%);}
100% {transform:translateX(0%);}
}
<div class="mouse">watch the line below</div>
Basically when you mouse over the text, you see a line slide in from the left. When you mouse out, you see the line slide out to the right.
My problem is if you go to your browser and press the refresh button to cause a page load, you'll see a line travel off to the right. I do not want this animation effect on page load. Is there something I can do with just HTML and CSS ? I do not want to introduce Javascript unless I absolutely have to.
Instead of using an animation use a transition with the property transform and a value of scaleX.
The "trick" here is to change transform-origin on the hover state.
Basically you do this:
.mouse:after {
content: "";
transform: scaleX(0);
transition: transform 1.5s ease;
transform-origin: right;
}
.mouse:hover:after {
transform: scaleX(1);
transform-origin: left;
}
Code Snippet:
.mouse {
margin-bottom: 20px;
padding: 5px;
overflow: hidden;
width: 200px;
}
.mouse:after {
content: "";
height: 2px;
display: block;
background: red;
margin-top: 5px;
transform: scaleX(0);
transition: transform 1.5s ease;
transform-origin: right;
}
.mouse:hover:after {
transform: scaleX(1);
transform-origin: left;
}
<div class="mouse">watch the line below</div>
If you however want to use your animation as it is, you would need to use javascript.
You need to add a class once the user hovers your element, in this case .running.
The basics are this:
.mouse:after {
content: "";
transform: translateX(100%);
animation: hoverOut 1.5s ease 1;
animation-play-state: paused; /* Initial state of the animation, paused. */
animation-delay: -1.5s; /* Get the first frame of the animation, this value has to be the total duration of the animation with a negative value */
}
.mouse.running:after {
animation-play-state: running; /* When the class is added, the animation state is changed to running */
animation-delay: 0s /* Set back to normal flow. */;
}
Code Snippet:
document.querySelector(".mouse").addEventListener("mouseenter", function() {
this.classList.add("running");
});
.mouse {
margin-bottom: 20px;
padding: 5px;
overflow: hidden;
width: 200px;
}
.mouse:after {
content: "";
height: 2px;
display: block;
background: red;
margin-top: 5px;
transform: translateX(100%);
animation: hoverOut 1.5s ease 1;
animation-play-state: paused;
animation-delay: -1.5s;
}
.mouse.running:after {
animation-play-state: running;
animation-delay: 0s;
}
.mouse:hover:after {
animation: hoverIn 1.5s ease 1;
}
#keyframes hoverOut {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(100%);
}
}
#keyframes hoverIn {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(0%);
}
}
<div class="mouse">watch the line below</div>
Animations always run when the element becomes visible.(or the page loads)
For something like this you want to use a transition. much more streamlined.
Here is your code using transition.
.mouse {
margin-bottom:20px;
padding:5px;
overflow:hidden;
width:200px;
}
.mouse:after {
content:"";
height:2px;
display:block;
background:red;
margin-top:5px;
transition: transform 1s;
transform:translateX(100%);
}
.mouse:hover:after {
transform:translateX(-100%);
}
</style>
</head>
<body>
<div class="mouse">watch the line below</div>
</body>
</html>

Resources