Why is CSS Hover behaviour flaky? - css

I want to create a button (using only css and html) that reveals another button beneath it on hover by rotating on it's lowermost axis.
I've been mostly successful: http://codepen.io/machinarius/pen/BdtCb
But as you can see on my pen the hover behaviour is flaky at best, it resets the animation on any movement of the cursor. Why id that happening? Isn't -webkit-animation-fill-mode: both; supposed to reverse the animation once the selector goes off?

There seem to be two parts to this question:
Why is the hovering flaky?
Like Palpatim said, as soon as the unfold-button is hovered over, it jumps away, so you'll need to have an unmoving element that will catch your hovers without un-hovering itself. So let's add a div that will do this:
<div class="container">
<div class="unfold-button orange">
Hello World
</div>
</div>
Likewise, let's update the CSS selector accordingly:
.container:hover .unfold-button {
Now if you put that in your HTML, you'll see that the hovering is no longer flaky. However, as you described, it still isn't animating back into place. This brings us to our second question:
Why is the animation not reversing?
Actually, animation-fill-mode does not mean that the animation will reverse back when the animation is no longer assigned; it only determines what attributes "fill out" before and after the animation occurs. If you remove the line defining animation-fill-mode, you'll see that the only difference is that, without it, the animation reverts after completing.
Also, elements have no memory of the animation values that they used to have, so as soon as an element's animation attribute changes, the element immediately "pops" into what it is assigned to be with no influence from any previous values of animation.
As a result, what's actually happening with your CSS is that, when the unfold-button is hovered over, it is handed the unfold animation and plays it (like it should), but when it is un-hovered, it suddenly has no animation assigned, so having "forgotten" about the animation, it just snaps back to what it was originally assigned to be.
Considering that the unfold animation is one simple motion, I would recommend expressing it instead as a transition:
.unfold-button {
/* ... */
border-style: none;
box-sizing: border-box;
transform-origin: 50% 100%;
-webkit-transform-origin: 50% 100%;
-webkit-transition: 0.5s;
transform: rotateX(0deg);
-webkit-transform: rotateX(0deg);
}
.container:hover .unfold-button {
-transform: rotateX(180deg);
-webkit-transform: rotateX(180deg);
}
Note how the transition attribute is maintained throughout both the hovered and non-hovered states. Like with animation, no animation results from it without its immediate presence.
And there you have it!
If the HTML and CSS look like what I have sitting in front of me right now, all should be good.
There's a little bit more information about reversing a CSS animation on hover-out here:
How to make CSS Animation reverse on hover-out?

The problem is that you're applying your animation on the :hover pseudo-class. Once the animation happens, you're no longer hovering, and so the animation resets. Try wrapping a container class around your animation element, and applying your animation trigger to the container's :hover, as in the example on https://developer.mozilla.org/en-US/docs/Web/CSS/animation-fill-mode.

Related

Trigger second animation keyframe (or move div permanently) with CSS animations?

I've tried searching for a solution to this problem, but haven't found one yet.
What I'm trying to do is simple:
When I click one button, I'd like a box to move 200px to the right with CSS transitions. When I click a second button, I'd like the box to move 200px down from the position it is currently in.
I have this basic code here:
HTML
<button class="one">First</button>
<button class="two">Second</button>
<div class="box"></div>
CSS
.box {
width: 100px;
height: 100px;
background-color: red;
transition: transform 2s;
}
.box.transOne {
transform: translateX(200px);
}
.box.transTwo {
transform: translateY(200px);
}
JS
$(".one").click(function() {
$(".box").toggleClass("transOne");
});
$(".two").click(function() {
$(".box").toggleClass("transTwo");
})
However, when I click on button number two, the box does move 200 down, but it moves diagonally back to the first X axis position while it's going down (I.e. it doesn't stay 200px over on the X axis).
Is there a way I can possibly do this with keyframes? Like triggering a second keyframe with a second button click, etc. Or is there a better way? I'm pretty stumped and can't find any solutions, so any help is much appreciated. Thanks!
SHORT ANSWER
set the translation X in class .transTwo too
.box.transTwo {
transform: translate(200px 200px);
}
EXPLANATION
the transform is overriding the others, this is the nature behaviour of the css, just like other property color, background-color,
The basic rule is the latest property set is the strongest, the strongest is at inline style unless you implement !important

Safari and Touchpad specific issue causing animated element to get stuck

I have a very odd problem that I only observe with Safari, on a touchpad.
When scrolling down, my navbar fades in / down via CSS transition. If I happen to scroll back up, thus removing the class responsible for the transition, the navbar gets stuck visually in the wrong place, only on safari. The CSS / styles say the correct values, and even the hover/click handlers are in the right place.
That is, In the image below, my mouse is hovering at the blank white area, while the navbar stuck below gets highlighted.
There are several odd things about this:
The element is the navbar via global styles, yet only happens on this particular page.
I can't seem to trigger the problem via scrolling with the mouse.
I can only trigger it via very subtle trackpad movements, or fast trackpad movements.
Any suggestions on how to fix this?
Relevant CSS
.is-sticky-slide-down {
#include experimental(animation, fadeInDown .3s ease-out 0s);
}
#-webkit-keyframes fadeInDown {
0% {
opacity: 0;
-webkit-transform: translateY(-20px);
transform: translateY(-20px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
transform: translateY(0);
}
}
The problem was due to enabling -webkit-backface-visibility: hidden on elements. Removing this "fix" for hover glitches (like twitching opacity fades) fixed the other glitches on Safari.
To be clear, the fix is to remove -webkit-backface-visibility from affected elements.

How can I stop a CSS3 animation from jumping back to the beginning when finished

I have a CSS3 animation, that simply moves a <div> down (via top: 0px; to top: 300px;). But my problem is, I don't know how to prevent the <div> from returning to the top when the animation is finished. Is there a way I can prevent this?
Here's a sample fiddle: http://jsfiddle.net/y8tJ7/
You need to invert the animation like so.
#keyframes move {
from{
top: 20px;
}
}
See http://jsfiddle.net/gleezer/y8tJ7/1/
This way its finishing position is specified in the div styling and you only specify the beginning state in the keyframe.
EDIT: Another way of achieving it could be to simply add:
animation-fill-mode: forwards.
See this other fiddle.
Some more info about it on Mozilla Dev Network.

Hovering over CSS transition in Safari lightens certain font color

In my CSS I defined a transition for a class. For some reason, when I hover over the class with the transition, the transition-duration for some reason alters the font color elsewhere (form placeholders and certain links). (This happens only in Safari as far as I can tell.)
Here's a jsFiddle that shows what I'm talking about:
http://jsfiddle.net/EJUhd/
Does anyone know why this occurs and how I can prevent it?
I was struggling with a similar issue.
For me, random links throughout the page became apparently bold (clearly something to do with OSX and anti-aliasing in Safari, as Chrome (in windows 7 and OSX) as well as the same version of Safari in Windows worked fine.
The solution is not obvious, and depending on what you are doing might not be optimal, but adding this line of code fixed the issue:
-webkit-transform: translateZ(0);
This basically triggers the GPU to do animation, and the text no longer had artifacts in my site. Do note that it's not always appropriate to use it, as it may use more battery life and use more resources. Sometimes however, it uses less, so basically check the performance when you add it.
You add this to the normal state not the :hover animated state.
img { -webkit-transform: translateZ(0); }
As opposed to on the:
img:hover { /* not here */ }
The other very positive side effect is that depending on the animation you are doing, it might be smoother through the GPU. So you won't get the choppy animation you mention in your follow up post. In my case, the animation was more seamless in safari. I was doing a 120% scale and 5 degree rotation of an image with some box-shadow appearing at the same time. In my situation, it did not reduce CPU usage unfortunately.
There is no more relevant topic I've found for a problem I had, but that's related to mentioned above issue. So, might be helpful for some one.
In two words: I have some container (popup), some element inside.
Appearing goes the following way: container background is fading up to dark via opacity and element inside is scaling up (like coming closer to us from behind). Everything works great everywhere but not in Safari (Mac/Win/iPhone). Safari "initially" shows my container, but it blinks some strange way (tiny short flash appears).
Only adding -webkit-transform: translateZ(0); (to container!!!) did help.
.container {
-webkit-transform: translateZ(0); /* <-- this */
}
.container section {
-webkit-transform: translateZ(0) scale(.92); /* <-- and I added translate here as well */
-webkit-transition: -webkit-transform .4s, opacity .3s;
opacity:0;
}
.container.active section {
-webkit-transform:translateZ(0) scale(1);
-webkit-transition: -webkit-transform .3s, opacity .3s;
opacity:1;
}
But speaking of the transitions, there was also the following part of code:
.container {
...
top:-5000px;
left:-5000px;
-webkit-transition: opacity .5s, top 0s .5s, left 0s 5s, width 0s 5s, height 0s 5s;
}
.container.active {
-webkit-transition: opacity .5s;
top:0;
left:0;
width:100%;
height:100%;
}
considering, that I want to show/hide the popup using only css switching (and also to make it disappear nicely instead just "display:none").
so, somehow on appearing Safari (obviously) was inheriting transition properties besides "opacity" even as I've overloaded them with only -webkit-transition: opacity .5s;
so, adding the following solved the problem:
.container {
...
-webkit-transition: opacity .5s, top 0s 0s, left 0s 0s, width 0s 0s, height 0s 0s;
}
I can't begin to tell you why it's doing this, but Safari isn't changing your text color, it's anti-aliasing the text differently while the transition is in motion. The text edges get smoother, and the text itself becomes thinner. This is extra obvious if you zoom in on the fiddle with accessibility tools. At some smaller sizes, the shading around the button next to the form text shifts too. (Is it possible that Safari is redrawing some things, or reorienting them on a sub-pixel level during the transitions ? Somebody explain this please, it's driving me nuts now!)
Because I have no real idea why it's doing this either, these might not be the best solutions:
Depending on what you're transforming, replacing the css transform with a javascript animation will probably fix it.
For example in your fiddle, the problem also occurred with a scale transformation, but not with a similar jQuery animate function.
There seem to be some shades and styles where the anti-aliasing change is less obvious (at least in the fiddle), so you could also try styling the placeholders and other effected text differently.
(This thread may help with styling the placeholders, if you go that route: Change an HTML5 input's placeholder color with CSS )
Thanks to the identification of anti-aliasing above, as well as help from the articles below, I modified my code to include translate3d(0,0,0) and the problem disappeared:
-webkit-transition-duration: .17s, .17s translate3d(0,0,0);
The transition isn't as smooth as it once was but that's a subject for another question.
Wonky text anti-aliasing when rotating with webkit-transform in Chrome
http://johanbrook.com/design/css/a-fix-for-antialiasing-issues-in-webkit-browsers/
http://www.webkit.org/blog/386/3d-transforms/
i had the same problem, while a transition some text became antialiased. this happen only in anchor text that are positioned relative e with z-index inside an element positioned and with z-index itself.
if i remove all position and index the problem disappear.
There is a similar problem using transition and translate3d. Sometimes any element on the page with :hover styles shows its hover behavior. I have this problem using a slider. Put the -webkit-transform: translateZ(0); to the :hover element and its behavior is normal.
For rotation() maybe it's fine, but for scale() It didn't worked the -webkit-transform: translateZ(0); formula.
I used :
-webkit-font-smoothing: antialiased;

css z-index lost after webkit transform translate3d

I have two absolutely positioned div elements that overlap. Both have set z-index values via css. I use the translate3d webkit transform to animate these elements off the screen, and then back onto the screen. After the transform, the elements no longer respect their set z-index values.
Can anyone explain what happens to the z-index / stack-order of the div elements once I do a webkit transform on them? And explain what I can do to keep the stack-order of the div elements?
Here is some more information on how I am doing the transform.
Before the transform, each element gets these two webkit transition values set via css (I am using jQuery to do the .css() function calls:
element.css({ '-webkit-transition-duration': duration + 's' });
element.css({ '-webkit-transition-property': '-webkit-transform' });
The element is then animated using the translate3d -webkit-transform:
element.css({ '-webkit-transform': 'translate3d(' + hwDelta + 'px, 0, -1px)' });
Btw, I have tried setting the 3rd parameter of translate3d to several different values to try to replicate the stack-order in the 3d space, but to no luck.
Also, iPhone/iPad and Android browsers are my target browser that this code needs to run on. Both support webkit transitions.
This might be related to: https://bugs.webkit.org/show_bug.cgi?id=61824
Basically when you apply a 3D transform on the z-axis, the z-index can't be accounted for anymore (you're now in a 3 dimensional rendering plane, use different z-values). If you want to switch back to 2D rendering for child elements, use transform-style: flat;.
This is most definitely related to the bug noted by samy-delux. This should only affect any elements which are positioned as absolute or relative. In order to remedy the issue, you can apply the following css statement to every element which is positioned this way and is causing issues:
-webkit-transform: translate3d(0,0,0);
This will apply the transform to the element without actually doing a transformation, but affecting its render order so it is above the element causing the issue.
Bit Late to this but try putting on the elements that have lost their Z-index placing the following, I had an issue when doing some parallax stuff recently and this helped massively.
transform-style: preserve-3d;
This saves putting
transform: translate3d(0,0,0);
On other elements which puts more strain on the GPU
Waiting to see the example
Have you tried to do a transform scale(1)? I remember to had a similar problem, and I had to re-arrange the html order of elements, and utilise a transform that I didn't need it just because the z-index of the use of transform changed.
If I am not in error, every time that you use a transform, it become the highest z-index available, and it is ordered by the nearest element of html is to the start of the tag. So from up to below.
I hope that this help
z-index will work against 3d transformed divs if you style the stackable element with -webkit-transform: translateZ(0px);
Snippet on codepen -> http://codepen.io/mrmoje/pen/yLIul
In the example, the buttons stack up and stack down raise and lower the footer's z-index (+/-1) against the rotated element (an img in this case).
I haven't been able to reproduce the problem you describe here. Regardless of what I do, the z-index value is retained throughout all transforms. I'm testing using Chromium (Google Chrome).
The third argument of the translate3d function manipulates the z-axis of the element. The concept is similar to, but not exactly the same as, the z-index... Elements with a lower z-axis are under elements with a higher value.
I know you tried values of the third argument to match your intended z-index, but the problem is that the z-axis doesn't seem to change during CSS3 animation. In the following example, the hovered element should be on top, but #element_a stays on top.
If I add a z-index to both the regular selector and the :hover selector, it seems to work and allow the hovered element to be top-most.
Although it's not exactly what you were looking for, this behavior provides a solution. You just need to use translate3d and z-index to set the order for the initial rendering.
<style>
div {
width: 300px;
height: 150px;
background-color: white;
border: 5px outset gray;
float: left;
margin: 20px;
-webkit-transition: 2s;
}
#element_a {
-webkit-transform: translate3d(0, 0, 50px);
}
#element_b {
-webkit-transform: translate3d(0, 0, 100px);
}
#element_a:hover {
-webkit-transform: translate3d(100px, 0, 60px);
}
#element_b:hover {
-webkit-transform: translate3d(-100px, 0, -60px);
}
</style>
<body>
<div id="element_a">
<img src="http://www.google.com/intl/en_com/images/srpr/logo3w.png">
</div>
<div id="element_b">
<img src="http://www.google.com/intl/en_com/images/srpr/logo3w.png">
</div>
</body>
I had this problem on iphone/ios where I had a dropdown menu that overlapped a leafletjs map but was being covered by the map. Noticed that the map had translate3d applied.
Added this to the dropdown element:
transform: translate3d(0,0,0);
...and it is fixed. Thank you stackoverflow people.
Another way around this is that you can create a parent element and apply all other transitions related to it:
# Apply transitions to a parent div
<div>
# This image z-index -1
<img src="foo"/>
# This image z-index -3
<img src="bar"/>
#This image z-index -2
<img src="gg"/>
</div>
JsFiddle

Resources