So I have this link item animation where by default, it's just text (:hover makes it change color), but if the link has the class of ".linke-active", :hover now adds an animated underline via a ::before pseudoelement.
So what I did was this:
&.link-active {
position: relative;
&::before {
content: ' ';
position: absolute;
height: 2px;
background: red;
bottom: 10px;
width: 100%
transition: 250ms ease-in-out;
transform: scaleX(0);
transform-origin: left;
}
&:hover, &:focus {
&::before {
transform: scaleX(1);
}
}}
So it's pretty straightforward.
It behaves perfectly when I move my mouse over the active link, and when I move my mouse over the inactive link.
My problem is that when I do the INITIAL CLICK that leads to the link gaining the .link-active the :before psuedoelement is there automatically, and since my mouse is obviously ON the link during the click, the psuedolement is in it's hover-state.
Is there any way to make the pseudolement NOT be in it hover-state on the initial click? Or is there a better way to trigger this style ruleset?
Thanks!
Related
I am animating a button. A class is assigned depending on my app state. This is actually implemented in Svelte as follows:
<div class="default"
class:run-animation="{$animate === true}">
But the equivalent in vanilla javascript without Svelte is:
let element = document... (find element)
element.classList.add("run-animation")
... later ...
element.classList.remove("run-animation)
For the sake of a minimum reproducible example, the classes I'm trying to animate/transition between look like this:
.default {
top: 20px;
color: white;
}
#keyframes button-animation {
from {
top: 20px;
color: white;
}
20% {
top: 23px;
color: white;
}
25% {
color: red;
}
100% {
top: 23px;
color: red;
}
}
.run-animation {
animation-name: button-animation;
animation-duration: 2s;
/* Preserve the effect of the animation at ending */
animation-fill-mode: forwards;
}
I add the class to the element, and the button animates just like I want it to. My problem arises when I remove the class. I want the button to transition smoothly back to the default CSS. I have tried adding the animation to the run-animate class:
.run-animation {
animation-name: ... ;
top: 23px;
color: red;
}
I have come across many people stating the transition upon class removal will apply if I add a transition property to the default class. I have tried this as follows:
.default {
...
transition: all 3s linear;
}
But it isn't working. The animation runs smoothly when it is added but the styling immediately reverts to the default when the class is removed (no smooth transition).
MY GOAL: I want to smoothly transition away from the end-state of the animation to the default class when the animate class is removed. Is this possible?
Ideally, I'm adding the class with the Svelte logic at the top so the animation should not be triggered in javascript but rather naturally occur as a result of class assignment.
(My code in practice is a little more complicated than shown, the button has another class with styles not being animated at all and the animation includes more styles such as box-shadow and text-shadow. Still, I don't see why this should be more problematic than just color and top included above)
// JS only toggles '.animation'
document.querySelector("button").addEventListener("click", () => {
document.querySelector("div.default").classList.toggle("animation");
});
body {display: flex}
button {position: absolute; left: 120px}
div.default {
position: absolute;
width: 100px;
height: 100px;
background: darkgreen;
}
/* Above code to make a visible working example */
div.default {
top: 20px;
color: white;
transition: top 0.4s, color 0.1s 0.4s;
}
#keyframes define-animation {
from {
top: 20px;
color: white;
}
}
div.default.animation {
animation-name: define-animation;
animation-duration: 2s;
top: 24px;
color: red;
}
<div class="default">I'm colourful</div>
<button>Toggle ".animation"-class</button>
Above is a working snippet with an animation running on class addition and no reverse transition on class removal. I have tried setting animation direction to opposite values in .default and .animation. I have tried defining the .animate end state properties in the class and/or in the keyframes to attributes.
EDIT: It works now! How?
You cannot apply:
animation-fill-mode: forwards;
The end-attributes need to be defined in the animate class not in the keyframe.
The animation plays when the class is added. Transition timings are used when the class is removed (if the animation has completed).
To get a transition effect, you can use the transition-property.
The transition-property can be used here, since every property you want to animate only has a start- and end-value.
Translating animation-percentages to seconds
To translate the percentages of your CSS Animation button-animation to seconds, you just calculate 'percentage' * 'animation-duration'.
This works for both the transition-duration-property as well as for the transition-delay-property.
Example:
color is being animated from 20% to 25%, which is a duration of 5% with a delay of 20%.
All in all, the animation should take 2 seconds.
So we calculate for:
transition-duration: 5% * 2s = 0.1s
transition-delay: 20% * 2s = 0.4s
With that, we can add transition: color 0.1s 0.4s to the .default-class.
Why add it to .default, and not to .animation?
If we were to add the transition-property to .animation, the following would happen:
When adding .animation, there will be a transition-effect, since the element now has a transition-property defined.
But when removing .animation, the element would no longer have a transition-property defined, meaning there would be no transition.
Now, we want to transition on both adding and removing .animation, meaning we want to have a transition-property defined both when .animation is present and when it is not. That means, transition should not be defined in .animation.
// JS only toggles '.animation'
document.querySelector("button").addEventListener("click", () => {
document.querySelector("div.default").classList.toggle("animation");
});
body {display: flex}
button {align-self: center}
div.default {
position: relative;
border: 2px solid black;
width: 100px;
height: 100px;
background: darkgreen;
}
/* Above code to make a visible working example */
div.default {
top: 20px;
color: white;
transition: top 0.4s, color 0.1s 0.4s;
}
div.default.animation {
top: 23px;
color: red;
}
<div class="default">Some text to see the "color"-property</div>
<button>Toggle ".animation"-class</button>
Why does it behave differently...
...when placing the properties inside the to-section of the animation, than when placing them inside .animation itself?
That is, because the properties are not directly applied to the element itself, but rather the element is stopped in its animation (right at the very end), giving only the appearance of the properties being actually applied.
Removing animation-fill-mode: forwards shows the actually applied properties after the animation has played. Those actually applied properties will be the start-values for transition after .animation is removed.
When defining these properties in .animation, they will inherently be the to-values for the animation (if not defined otherwise in animation itself), and be the applied properties of the element.
That means, when removing .animation, the transition will start from there.
I have blog post with set of images that are enlarged on hover. My problem is that when i enlarge element and it overlaps with other image that is later in page render order then the next image is on top of the enlarged one.
The easy way to stop this is to give some kind of z-index on :hover pseudo selector. But then i have very pesky problem when just after I stop hovering my image then next one is on top of it for fraction of second.
You can see behaviour in this imgur album or on jsfiddle(hover first image)
In short i have following css for hovering effect:
.photo-exp
{
position: relative;
transition: all .4s ease-in-out;
/* some properties deleted which have no connection to hovering effect */
}
.photo-exp:hover
{
transform: scale(1.7);
z-index : 10;
}
It would be very easy to have same effect with javascript and setTimeout function.
But i would like to avoid javascript solution and have some CSS workaround which will change slowly z-index in time after hovering ends.
I tried CSS transition but it is not working
I tried to eddit this snippet but i could not get it working in the way that i wanted.
You need to assign a new transition-delay property, and remove it as soon as the hover begins. That way the z-index can persist for some time even after the mouse is gone. It's a little counter-intuitive; I would expect that the delay should be added on hover and removed off-hover but the opposite works on chrome:
.expander {
position: absolute;
left: 50%; top: 50%;
width: 100px; height: 100px;
margin-top: -50px; margin-left: -50px;
z-index: 1;
transition: transform 400ms 0ms, z-index 0ms 400ms; /* That final "400ms" delays the z-index transition! */
}
.expander:hover {
transform: scale(1.8);
z-index: 2; /* A hovered expander is always on top */
transition: transform 400ms 0ms, z-index 0ms 0ms; /* Remove the z-index transition delay on hover. This is counter-intuitive but works. */
}
.expander:nth-child(1) {
margin-left: -105px;
background-color: #a00000;
}
.expander:nth-child(2) {
margin-left: 5px;
background-color: #00af00;
}
<div class="expander"></div>
<div class="expander"></div>
Note that (unless you try to mouse around really quickly in order to break it) neither square bleeds through the other, not even for a frame, when they expand.
I've also finally managed to solve by myself my problem.
It's more intuitive than #Gershom Maes answer in my opinion.
Fiddle
I have used animation system to achieve the result.
#keyframes nohovering {
0% { z-index: 9; }
100% { z-index: 1; }
}
#keyframes hovering {
0% { z-index: 10; }
100% { z-index: 10; }
}
First one will be by default fired on selector without :hover like this
animation: nohovering 0.9s;
It will guarantee that after i complete my hovering it will go smoothly down from z-index 9 to z-index 1. After hovering my image will be on top of other images. When i tested it for z-index 10 for 0% i had a little glitch when i tried to hovered 2 images at same time and then hover only 1 of them.
For my hovering selector I used:
animation: hovering 0.1s infinite;
It will just loop my image on z-index 10. On hover it will always be on top of the other images. Short animation time guarantee that it will go off after hovering stopped in maximum time of 0.1s.
After deleting normal static z-indexes it works.
I'm very new to HTML and CSS but I've strung together some code that makes my images enlarge a little bit when my mouse hovers over them. However, the images sometimes don't enlarge over each other, some stay behind others because they are positioned closely together. Does anyone know how to make them ignore the other images when they enlarge?
Here's my code:
img.one {
position: absolute;
cursor: pointer;
top: 10px;
left: 10px;
transition: all .2s ease-in-out;
}
img.one:hover {
transform: scale(1.1);
}
Use the z-index property to control which elements are in front of others.
img.one:hover {
transform: scale(1.1);
z-index: 1;
}
I'm trying to create a slightly animated pushable button.
My goal is to achieve a button like this:
see the source code (jsfiddle)
with using exactly one HTML tag and the minimal amount of new CSS features like transform and transition.
However as you see in the jsfiddle; if you click the button at least twice the whole line (and the content beneath) bounces too.
That is ofcourse because of the margin set within the :active selector.
My second approach was using the the CSS transform property. That worked perfectly except I still had the need the change the height of the button (which will result in the same problem).
So my question is: How can I achieve the same effect without adjusting the positions of other elements?
How can I achieve the same effect without adjusting the positions of other elements?
I suggest using position: relative; for the button, then set top: 2px for :active pseudo-class, as follows:
button {
/* Other styles... */
position: relative; /* position the element as relative */
outline: none; /* Just added for the demo */
}
button:active {
top: 2px; /* move the element without affecting the others' position */
box-shadow: inset 0px 2px 1px 0px rgba(0, 0, 0, .1);
}
JSFiddle Demo.
You can use transform: translate property over top/left properties (Check here if you know why) and of course if you don't care about legacy IE browsers:
button {
....
transition: all .1s ease-in-out;
-moz-transition: all .1s ease-in-out;
-webkit-transition: all .1s ease-in-out;
....
}
button:active {
transform: translatey(2px);
-moz-transform: translatey(2px);
-webkit-transform: translatey(2px);
...
}
Demo
My issue is that during CSS transition border-radius temporarily stops clipping elements inside if transition of overlapping element involves transform. In my case I have two divs absolutely positioned one above the other where the first one has transition triggered by action on clicking a navigation element inside the second one, like:
<div id="below"></div>
<div id="above"><div id="nav"></div></div>
The above div has border-radius: 50% and clips the nav div. In CSS it goes like (minimal example, original onclick action illustrated as :hover):
#below {
position: absolute; width: 250px; height: 250px;
-webkit-transition: all 1s linear;
transition: all 1s linear;
}
#below:hover {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
#above {
position: absolute;
width: 200px; height: 200px;
border-radius: 50%;
overflow: hidden;
}
#nav {
width: 40px;
height: 200px;
background-color: rgba(0,0,0,0.3);
}
Of course it is better visible in http://jsfiddle.net/UhAVG/ with some additional styling for better illustration.
This works as expected in IE10+ and FF25, also in Chrome 31 and 32 with hardware acceleration disabled. In result only accelerated Chrome shows this unwanted behaviour. So I'm wondering if it's possible to workaround it somehow using current CSS3 techniques.
After some more experiments I've finally found the solution. Sometimes simple ones are the hardest to find. In this case #above {z-index: 1;} (like in http://jsfiddle.net/UhAVG/1/) solves the issue. Wild guess is that z-index prevents some optimization that combines operations from single layer and doing so mistakenly optimizes out applying border-radius on element. With layers separated this is no longer the case.