Smooth CSS rotate animation on hover - css

I've create a button, and I want it to rotate 360deg on mouse hover, and rotate backwords 360deg on hover off. So far it work well, but if you go slowly towards it with the mouse, it flickers.
Here's the short version of the code:
.btn {
display: block;
margin: 60px auto;
width: 250px;
padding: 15px;
position: relative;
color: #3498db;
font-weight: 300;
font-size: 24px;
text-decoration: none;
border: 5px solid #3498db;
transform: rotate(360deg);
transition: all 0.5s;
transition-timing-function: cubic-bezier(1, 0.8, 0.5, 1);
}
.btn-rotate:hover {
transform: rotate(0deg);
transition-delay: 0;
transition: all 0.5s;
}
I am a button!
for full code, check the codpen demo http://codepen.io/andornagy/pen/ojBNZx

The flicker issue is happening because, when you hover on the element, the elements start to rotate. After rotating some x degree, the element would've rotated to certain degree and the mouse/cursor is not anymore on the element.
This is the reason the flicker is happening.
Comparing to the above one, I feel using wrapper (div) and analyzed how much width we may need, we set that to div. On div:hover element, we can perform the transition. It gives better result compared to now.
Here is the fiddle
.buttonHolder {
padding: 50px;
}
.buttonHolder:hover .btn-rotate {
transform: rotate(360deg);
transition-delay: 0;
transition: all 0.6s;
}

Here's an idea where it adds an extra pseudo element only when you're hovering :
Demo
.btn:after {
content: '';
width: 300px;
height: 150px;
display: none;
position: absolute;
top: 50%;
left: 50%;
border-radius: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.btn:hover:after {
display: block;
}
Gave it a bit of background color, just so it's better visible what's going on...
For the most control, I'd resort to some JavaScript though.

Related

scale() from a high value to low distorts transition

I am trying to make a transition so when I click I button two divs are scaled from a small circle to a large circle. Basically that looks something like this when expanded:
This has gone from scale(1) to scale(20) and scale(25). When I click the button, the circles shrink with no problem. However, if I increase the scaling to for example scale(40) and scale(45) the result I get when clicking the button again (to shrink everything) looks like this:
So this happens right after I click. In this case I just have a transition-delay on so I could screenshot it.
The SCSS for the circles are like this:
.inner-circle-bg {
display: none;
width: 30px;
height: 30px;
border-radius: 100%;
position: fixed;
background: $primaryRed;
bottom: 35px;
right: 35px;
z-index: 999999;
transform: scale(1);
transition: transform 0.3s ease-in;
transition-delay: 4.1s;
&.is-active {
transform: scale(40);
transition: transform 0.3s ease-in;
transition-delay: 0.1s;
}
}
.outer-circle-bg {
display: none;
width: 30px;
height: 30px;
border-radius: 100%;
position: fixed;
background: $primaryRedDark;
bottom: 35px;
right: 35px;
z-index: 99999;
transform: scale(1) ;
transition: transform 0.3s ease-in;
transition-delay: 4.2s;
&.is-active {
transform: scale(45) ;
transition: transform 0.3s ease-in;
}
}
And then I just have some JS to toggle the is-active state when a button is clicked.
So yeah, any idea what might be causing this, and if there is a way to fix it ?
EDIT:
I now have a working JSfiddle illustrating the problem (although with a hover effect instead):
https://jsfiddle.net/47znjxwo/

How to fix movement menu on load page?

I have animation with using transform:translate(-100%) and transition, but when i load page my block is moving from 0% to -100%;
in normal condition she have to have transform:translate(-100%) and when checkbox is checked - transform:translate(0%)
It works well but on load is moving from o to -100%
https://katehrybkova.github.io/ETmenu/index.html - link on github-page
https://github.com/katehrybkova/ETmenu - source
.menuBlock {
background-color: #35393b;
height: 100vh;
color: white;
padding: 25px 0;
width: 400px;
position: absolute;
transform: translateX(-100%);
transition: 1s;
}
#idishka:checked~.menuBlock {
transform: translateX(0);
}
The animation starts with .menuBlock at left: 0, that's why transform: translateX(-100%) starts fading it to the left.
Maybe you can replace translateX function with left, because you have .menuBlock with fixed width.
This is the final code:
.menuBlock {
background-color: #35393b;
height: 100vh;
color: white;
padding: 25px 0;
width: 400px;
transition: 1s;
position: absolute;
left: -400px;
}
#idishka:checked ~ .menuBlock {
left: 0;
}
I don't recommend you using fixed widths (in pixels), for responsivity issues ;)

Transform and Stacking Order

I am trying to understand what is really happening “3d” world of CSS.
I made a simple example
Particularly the code which bugs me the most is:
.back {
background-color: tomato;
transform: rotateY(180deg);
z-index: 1;
}
The thing which is not clear to me is why when you hover over .inner, its background color (gold) is not visible?? If you remove the transform property from .back or if you set the rotateY to 0deg then the gold background color of the .inner is clearly visible.
Why is the transform property of .back changing the stacking order?
Logically it makes sense that children(.front and .back) should appear in front of their parent(.inner).
Also, I would like to know what really happens when you set transform-style to flat? Does that make parent and all of its children collapse into single “unit” where element with highest stacking order takes priority/visibility?
in your code :
.outer {
display: block;
width: 200px;
height: 200px;
border: 2px solid gold;
perspective: 1000px;
padding: 10px;
margin: 0 auto;
}
.inner {
position: relative;
height: 100%;
width: 100%;
transition: transform 2s linear;
transform-style: preserve-3d;
background-color: gold;
backface-visibility: visible;
transform: rotateY(50deg);
}
.sides {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
color: white;
backface-visibility: hidden;
}
.front {
background-color: blue;
transform: translateZ(20px)
}
.back {
background-color: tomato;
transform: rotateY(180deg) translateZ(10px);
}
.inner:hover {
transform: rotateY(180deg)
}
<div class="outer">
<div class="inner">
<div class="sides front">Front Side</div>
<div class="sides back">Back Side</div>
</div>
</div>
you are using
transform: rotateY(180deg) translateZ(10px);
The transforms are applied right to left, so first it goes to the front 10px. But after that, it rotates 180deg. (around the transform-origin that is constant). That makes the previous 10px go towards the back instead of to the front.
if the order is the inverse
transform: translateZ(10px) rotateY(180deg);
now the rotation is done first, and so the translation is unafected by it and goes to the front.
and No, sorry, z-index is not a substitute for 3-d transforms, if you want to use 3d transforms, translation is the only way to go ....
In your first example, z-index is useless, as can be seen easily
codepen with z-index removed
This works because you are setting
backface-visibility: hidden;
So only the face that is facing front will be visible

CSS Transition after animation ends

I have a css transition that moves an element on hover and an animation that rotates the element on hover too. There's a delay on the animation equal to the transition duration so that after it's transitioned to it's correct position, the animation starts. And it works nice, however, when we mouse off, the animation stops but it doesn't transition back down.
Is it possible to get it to transition back after we mouse off and the animation ends?
You can see an example here: http://codepen.io/jhealey5/pen/zvXBxM
Simplified code here:
div {
width: 200px;
height: 200px;
margin: 40px auto;
background-color: #b00;
position: relative;
&:hover {
span {
transform: translateY(-60px);
animation-name: rotate;
animation-duration: 1s;
animation-delay: .5s;
animation-iteration-count: infinite;
animation-direction: alternate;
}
}
}
span {
position: absolute;
width: 20px;
height: 20px;
background-color: #fff;
bottom: 10px;
left: 0;
right: 0;
margin: auto;
transition: .5s;
}
#keyframes rotate {
from {
transform: translateY(-60px) rotate(0);
}
to {
transform: translateY(-60px) rotate(-90deg);
}
}
I have forked your project and adapted it so it works. You can find it here.
What I have changed is the following:
I give the white square a start position of top: 150px and let it, on hover of div, get a top: 0. The span gets a transition: top .5s and with that it goes to top: 0; on hover and back to top: 150px; when the mouse leaves.
I have removed the translateY(-60px); from the animation, because that would move it even more up when the animation would start.
Here's your new CSS:
div {
width: 200px;
height: 200px;
margin: 40px auto;
background-color: #b00;
position: relative;
&:hover {
span {
top: 0px;
animation: rotate 1s infinite .5s alternate;
animation-direction: alternate;
}
}
}
span {
position: absolute;
width: 20px;
height: 20px;
background-color: #fff;
bottom: 10px;
left: 0;
right: 0;
top: 150px;
margin: auto;
transition: top .5s;
}
#keyframes rotate {
from {
transform: rotate(0);
}
to {
transform: rotate(-90deg);
}
}
Edit: The problem is that an animation is time-based and not action-based, which means that as soon as you trigger an animation, a timer starts running and it will run through all the keyframes until the set time has passed. Hover-in and hover-out have no effect, except that the timer can be stopped prematurely, but the animation will not continue (or reversed, which you wanted) after that. transition is action-based, which means it gets triggered every time an action (for example :hover) is happening. On :hover, this means it takes .5s to go to top:0 and when the hover ends, it takes .5s to got to top:150px.
I hope the above addition makes sense :)
As you can see, I also cleaned up a bit in your animation-name: etc., since it can be combined into one line.
As Harry pointed out, the problem is that you are animating/transitioning the same property, in this case transform. It looks like the current versions of Chrome/FF will allow the animation to take control of the property, thereby breaking the transition. It seems like the only way to work around this is to transition/animation a different property. Since you need to continue rotating the element, you could translate/position the element by changing the bottom property instead. I know that doesn't produce the exact same results, but nonetheless, it does move the element (just not relative to the parent element).
Updated Example
div:hover span {
bottom: 80px;
}
As an alternative, you could also wrap the span element, and then translate that element instead.
In the example below, the .wrapper element is transitioned to translateY(-60px) on hover, and then the child span element is rotated and maintains the animation.
Example Here
div {
width: 200px;
height: 200px;
margin: 40px auto;
background-color: #b00;
position: relative;
}
div:hover .wrapper {
transform: translateY(-60px);
}
div:hover .wrapper span {
animation-name: rotate;
animation-duration: 1s;
animation-delay: .5s;
animation-iteration-count: infinite;
animation-direction: alternate;
}
.wrapper {
display: inline-block;
transition: .5s;
position: absolute;
bottom: 10px;
left: 0;
right: 0;
text-align: center;
}
.wrapper span {
display: inline-block;
width: 20px;
height: 20px;
background-color: #fff;
}
#keyframes rotate {
from {
transform: rotate(0);
}
to {
transform: rotate(-90deg);
}
}
<div>
<span class="wrapper">
<span></span>
</span>
</div>

CSS 3D Cube Z-Index Issue When Rotating

I've created a basic 3D cube using CSS and <div>s. However, when it animates, the sides are not "overlapping" properly. It's a bit hard to explain, so see the http://jsfiddle.net/JNCNr/ to see precisely what I mean. I've read through some SO posts, the MDN, and so forth, but I am not quite sure what is causing my issue. I simply want the sides to behave properly when they rotate behind each other.
EDIT: Right now it's working for Chrome only.
Here is some of my CSS:
.container {
position: absolute;
left: 50%;
top: 50%;
height: 100px;
width: 100px;
/* for 3d animations */
-webkit-perspective: 800;
}
.box {
/* size */
height: 100px;
width: 100px;
position: absolute;
-webkit-user-select: none;
cursor: move;
/* color */
opacity: 1;
background: -webkit-radial-gradient(#666, #333);
border: 1px solid black;
/* 3d stuff */
-webkit-transform-origin: 50px 50px -50px;
}
.s1 {
-webkit-animation: as1 4s linear infinite;
}
#-webkit-keyframes as1 {
from {
-webkit-transform: rotate3d(0, -1, 0, -270deg);
}
to {
-webkit-transform: rotate3d(0, -1, 0, 90deg);
}
}
Thanks~!
Apply backface-visibility: hidden to hide the parts of element that have been rotated to show the backface:
.s1, .s2, .s3, .s4 {
-webkit-backface-visibility: hidden;
}
Updated jsFiddle

Resources