Demo of the problem: https://jsfiddle.net/t0qsek8n/1/
<div class="test" id="test">Test text</div>
.test {
position: relative;
top: 0px;
border: 1px solid #ccc;
animation: test 5s;
transition: top 1s;
}
#keyframes test {
0% {
opacity: 0;
transition: none;
}
100% {
opacity: 1;
transition: none;
}
}
const test = document.getElementById('test');
setTimeout(() => {
test.style.top = "100px"
}, 1000);
I expect if the value of top property is changed by JS, transition transition: top 1000ms doesn't happen because of transition: none that provides #keyframes test, but actually, the transition happens.
I cannot understand why transition value from keyframes doesn't override any existing definition for transition.
let's take another example using display:
.test {
position: relative;
top: 0px;
border: 1px solid #ccc;
animation: test 5s forwards;
}
#keyframes test {
0% {
opacity: 0;
display: none;
}
100% {
opacity: 1;
display: none;
}
}
<div class="test" id="test">
Test text
</div>
We logically expect to never see the element since we set display:none and we made the animation to be forwards but display is simply ignored because it cannot be animated. Same logic with transition since it's a property that we cannot animate ref.
Basically, any property that cannot be animated will simply get ignored when used with keyframes.
Properties that aren't specified in every keyframe are interpolated if possible — properties that can't be interpolated are dropped from the animation. ref
Related
Coming from https://stackoverflow.com/a/9334132/3779853: Let's assume a basic element that gets toggled programmatically. This could mean setting display to none/block or removing/inserting the element altogether.
$('#toggle').click(() => $('#square').toggle());
#square {
width: 50px;
height: 50px;
background: lightblue;
}
.animated {
animation: fade-in 1s;
}
#keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="toggle">toggle</button>
<div id="square" class="animated"></div>
With a simple animation, you can add a transition effect for when the element appears. How do you do the same thing for when the element disappears?
I do not want to add further classes, no :hover, and no more Javascript code. In many JS frameworks, you can show/hide elements easily: .toggle() (JQuery, as above), ng-if (AngularJS), *ngIf (Angular), conditional rendering (React), v-if (VueJS) and so on. With above solution, a simple class="animated" is enough to have it appear with custom animations. So I am looking for a pure CSS solution for fade out animation here, assuming this is a standard problem.
Here is a 100% pure css solution.
#square {
width: 50px;
height: 50px;
background: lightblue;
transition: opacity 1s ease-in-out;
-webkit-transition: opacity 1s ease-in-out;
-moz-transition:opacity 1s ease-in-out;
}
#myBox:checked ~ .animated {
opacity: 0;
}
#myBox ~ .animated {
opacity: 1;
}
<input type="checkbox" id="myBox" style="display:none;"/>
<button id="toggle"><label for="myBox">toggle</label></button>
<div id="square" class="animated"></div>
You can use the opacity property with transition effect.
$('#toggle').click(() => $('#square').toggleClass('animated'));
#square {
width: 50px;
height: 50px;
background: lightblue;
transition: opacity 0.5s;
opacity: 1;
}
#square.animated {
opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="toggle">toggle</button>
<div id="square" class="animated"></div>
Once I start animating, on Chrome I get a ripple effect. My circle transform scales up. On Firefox, that exact same animation is ignored for some reason.
$("#animate").click(function() {
$("#square").toggleClass("animate");
$("#fab").toggleClass("ripple");
});
#keyframes ripple {
from {
transform: scale(0)
}
to {
transform: scale(20)
}
}
#square {
position: relative;
width: 300px;
height: 300px;
overflow: hidden;
border: 1px solid red;
transition: background 0.1s linear 0.6s, transform 1s;
transform: rotate(0deg);
}
#fab {
position: absolute;
width: 56px;
height: 56px;
border-radius: 50%;
background: #4FB5AB;
top: 122px;
right: 0;
transform: scale(1);
transition: transform 1s;
}
.ripple {
animation: ripple 1s 0.5s;
transform: scale(20) !important;
/*Duration - delay */
transition: transform 0s 1s !important;
}
.animate {
transform: rotate(90deg) !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="square">
<div id="fab"></div>
</div>
<br />
<button id="animate">animate</button>
CodePen Demo
Before I start explaining the problem with your code, here is a word of caution - Do not use transitions and animations together. They generally end up causing problems like the one faced here.
When an animation is specified on an element, it will take complete control over the properties that are being animated unless there is a rule with !important setting. If !important setting is used then that rule takes precedence over the animation. (but unfortunately Chrome and Firefox seem to be handling this case differently).
As per W3C Spec:
CSS Animations affect computed property values. During the execution of an animation, the computed value for a property is controlled by the animation. This overrides the value specified in the normal styling system. Animations override all normal rules, but are overriden by !important rules.
emphasis is mine
In your code, there were two problems and they are as follows:
Within .ripple selector, you were specifying the transition-duration as 0s, which means, there is no transition at all and that the change of transform is an instant one. As explained in the W3C Spec, Firefox seems to be (correctly) giving the control to the rule with !important setting (that is, the transform and transition within .ripple selector) and so it transitions the state change immediately after the specified 1s delay+. Chrome lets animation take control and thus produces the effect you are looking for.
Firefox seems to animate the element quicker than Chrome does and so while a duration of 1s is enough for the animation in Chrome, FF needs it to be 2s to be slower and show the effect.
+ - You can further verify this by removing the !important settings on the rules. Once !important is removed, the animation would take control.
$("#animate").click(function() {
$("#square").toggleClass("animate");
$("#fab").toggleClass("ripple");
});
#keyframes ripple {
from {
transform: scale(0)
}
to {
transform: scale(20)
}
}
#square {
position: relative;
width: 300px;
height: 300px;
overflow: hidden;
border: 1px solid red;
transition: background 0.1s linear 0.6s, transform 1s;
transform: rotate(0deg);
}
#fab {
position: absolute;
width: 56px;
height: 56px;
border-radius: 50%;
background: #4FB5AB;
top: 122px;
right: 0;
transform: scale(1);
transition: transform 1s;
}
#fab.ripple {
animation: ripple 2s 1s;
transform: scale(20);
/*Duration - delay */
transition: transform 1s 1s;
}
#square.animate {
transform: rotate(90deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="square">
<div id="fab"></div>
</div>
<br />
<button id="animate">animate</button>
Finally, please do not use !important unless it is mandatory. Instead just make the selector more specific. In the snippet, I have made it more specific by using the #id.class format.
I have an image and I need its on hover opacity to be 0.5, then It must scale up to 200% and back the opacity up to 1 when image is full scaled size.
Example
I am able to make a scaling transform and opacity on hover, but I need the opacity to be 1 after the scale when image is at 200% size.
#imagecontainer {
border: 2px solid red;
width: 251px;
height: 251px;
opacity: 1;
position: absolute;
}
#image {
width: 250px;
height: 250px;
border: 2px solid black;
position: absolute;
opacity: 1;
-webkit-transition: -webkit-transform 1s ease-in-out;
}
#image:hover {
opacity: 0.8;
-webkit-transform: scale(2, 2);
}
Since there are more than one state change (that is, opacity: 0.5 initially before the transform is completed and then opacity: 1 after the transform is completed, you cannot do it with transition alone because the transition can only change the opacity value once and retain it. You either need to use CSS3 animations or alter the styling using JS with transitionend event.
Below is a sample snippet with CSS3 animations where on hover the image gets opacity: 0.5 and this state is retained till the 99% keyframe. All this happens while the image goes from not having any transform to transform: scale(2,2). Then at the 100% frame, the transform is retained as it is but opacity is changed from 0.5 to 1.
#imagecontainer {
border: 2px solid red;
width: 251px;
height: 251px;
opacity: 1;
position: absolute;
}
#image {
width: 250px;
height: 250px;
border: 2px solid black;
position: absolute;
opacity: 1;
}
#image:hover {
opacity: 0.5;
animation: opacitynscale 1s ease-in-out forwards;
}
#keyframes opacitynscale {
99% {
transform: scale(2, 2);
opacity: 0.5;
}
100% {
transform: scale(2, 2);
opacity: 1;
}
<div id='imagecontainer'>
<img id='image' src='http://lorempixel.com/250/250/nature/1' />
</div>
The downside of using CSS animation instead of transition for this is that unlike transition, the animation wouldn't automatically produce the reverse effect on hover out (that is, it would snap back to original state and not gradually go back). Another animation must be written for the reverse effect.
If you can't use CSS3 animation for whatever reasons (including the aforementioned) then you can do it with a bit of JavaScript by using the transitionend event.
var img = document.getElementById('image'),
mousein = false;
img.addEventListener('transitionend', function() { /* this event is fired when transition is over */
if (mousein)
img.style.opacity = 1; /* changes element's opacity to 1 */
else
img.style.opacity = null; /* remove inline style on hover out, otherwise it will override others */
});
/* to determine if mouse is over image or not */
img.addEventListener('mouseover', function() {
mousein = true;
});
img.addEventListener('mouseout', function() {
mousein = false;
});
#imagecontainer {
border: 2px solid red;
width: 251px;
height: 251px;
opacity: 1;
position: absolute;
}
#image {
width: 250px;
height: 250px;
border: 2px solid black;
position: absolute;
opacity: 1;
transition: transform 1s ease-in-out;
}
#image:hover {
opacity: 0.5;
transform: scale(2, 2);
}
<div id='imagecontainer'>
<img id='image' src='http://lorempixel.com/250/250/nature/1' />
</div>
I would like to define a CSS3 animation which, at some points during the animation, uses the natural value for a property as if the animation was not applied.
e.g.
#keyframes fadeblue
{
0%
{
background-color: natural;
}
100%
{
background-color: blue;
}
}
.thing1
{
background-color: red;
animation: fadeblue 2s;
}
.thing2
{
background-color: green;
animation: fadeblue 2s;
}
thing1 would fade from red to blue while thing2 would fade from green to blue.
What value should I use in the place of natural in the 0% keyframe?
I have tried both inherit and transparent but neither had the desired effect.
N.B. I know this can be done with a JavaScript solution but if possible I'd prefer a pure css3 solution.
So it seems you can't reference the original colour in keyframes. However, you can just specify one keyframe in a keyframes declaration and let the browser interpolate the colors for you. Using a keyframe of just 50% will use the original properties at 0% (aka from) and 100% (aka to).
With this knowledge we can also effectively queue animations using animation-delay to create what looks like a single animation, but isn't.
For example:
#keyframes fadeblue {
50% {
background-color: blue;
}
}
#keyframes fadewhite {
50% {
background-color: white;
}
}
.thing1 {
background-color: red;
animation: fadeblue 2s,
fadewhite 2s 2s;
/* shorthand here is: animation-name animation-duration animation-delay */
}
.thing2 {
background-color: green;
animation: fadeblue 2s,
fadewhite 2s 2s;
}
.thing3 {
background-color: yellow;
animation: fadeblue 2s,
fadewhite 2s 2s;
}
.thing4 {
background-color: purple;
animation: fadeblue 2s,
fadewhite 2s 2s;
}
<div class="thing1">Thing 1</div>
<div class="thing2">Thing 2</div>
<div class="thing3">Thing 2</div>
<div class="thing4">Thing 2</div>
You'll see the elements fade to blue and back to the original colour, then fade to white then the original colour.
jsfiddle for good measure.
You can achieve this if you use a pseudo-element (e.g. :before) for .thing1 and . thing2, set it's color to blue, and animate it's opacity. It's a bit more work, but I believe it will be more flexible solution:
(see working demo below)
#keyframes fadeblue {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.thing1,
.thing2 {
width: 100px;
height: 100px;
display: inline-block;
position: relative;
}
.thing1:before,
.thing2:before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: blue;
animation: fadeblue 2s infinite;
}
.thing1 {
background-color: red;
}
.thing2 {
background-color: green;
}
<div class="thing1"></div>
<div class="thing2"></div>
I have a link that's running an infinite animation with the background color. I want to stop the animation and transition into a different background color on hover.
.startlink{
background-color:#206a9e;
color:#fff;
border-radius:15px;
font-family: 'Myriad Pro';
-webkit-animation:changeColor 3.4s infinite;
-webkit-transition:all 0.2s ease-in;
}
.startlink:hover{
-webkit-animation-play-state: paused;
background-color: #014a2a;
}
#-webkit-keyframes changeColor
{
0% {background:#206a9e;}
50% {background:#012c4a;}
100% {background:#206a9e;}
}
Why is this code not working? And is there an alternate way to get this done? (preferably without Javascript).
Try -webkit-animation: 0;. Demo here. 0 is the default value for animation or what you must set to disable any existing CSS3 animations.
-webkit-animation-play-state: paused
and
-webkit-animation-play-state: running
Found another way round to achieve this.
Write another animation keyframe sequence and call it on your hover.
.startlink{
background-color:#206a9e;
color:#fff;
border-radius:15px;
font-family: 'Myriad Pro';
-webkit-animation:changeColor 3.4s infinite;
-webkit-transition:all 0.2s ease-in;
}
.startlink:hover{
-webkit-animation:hoverColor infinite;
}
#-webkit-keyframes changeColor
{
0% {background:#206a9e;}
50% {background:#012c4a;}
100% {background:#206a9e;}
}
#-webkit-keyframes hoverColor
{
background: #014a2a;
}
I was trying to achieve the same kind of thing and after trying to dynamically change keyframes and all, I found a weird solution by using basic css, see fiddle here. It is not very elegant but does exactly what I (and you, I hope) want.
#menu, #yellow{
position: fixed;
top: 2.5vw;
right: 2.5%;
height: 25px;
width: 25px;
border-radius: 30px;
}
#menu{
animation: blink 2s infinite;
transition: 1s;
}
#keyframes blink{
0% { background-color: grey; }
50% { background-color: black; }
100% { background-color: grey; }
}
#yellow{
background-color: rgba(255, 0, 0, 0);
transition: 1s;
}
#disque:hover #yellow{
pointer-events: none;
background-color: rgba(255, 0, 0, 1);
}
#disque:hover #menu{
opacity: 0;
}
<div id="disque">
<div id="menu"></div>
<div id="yellow"></div>
</div>
I have the same issue and the solution I found is the following.
Create the animation you want and for the element you and to each assign each one a different class.
Then use .mouseover() or .mouseenter() jQuery events toggle between the classes you assigned to each animation.
It is similar to what you use for a burger menu, just with a different handler.
For those who are interested by animation slide with stop between 2 images
var NumImg = 1; //Img Number to show
var MaxImg = 3; //How many Img in directory ( named 1.jpg,2.jpg ...)
function AnimFond() {
NumImg = NumImg> MaxImg ? 1 : NumImg +=1;
var MyImage = "http://startinbio.com/Lib/Images/Fond/" + NumImg + ".jpg";
$("#ImgFond1").attr("src", MyImage);
$("#ImgFond2").fadeOut(3000, function() {
$("#ImgFond2").attr("src", MyImage);
$("#ImgFond2").fadeIn(1);
});
}
setInterval("AnimFond()", 10000); //delay between 2 img
#AnimFond {
position: fixed;
height: 100%;
width: 100%;
margin: 0 0 0 -8;
}
#AnimFond img {
position: absolute;
left: 0;
height: 100%;
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
<div id="AnimFond">
<img id="ImgFond1" src="http://startinbio.com/Lib/Images/Fond/1.jpg" />
<img id="ImgFond2" src="http://startinbio.com/Lib/Images/Fond/1.jpg" />
</div>