CSS transition doesn't work in Firefox when position is changed - css

I've found annoying bug. I try to animate CSS properties of child elements when at the same time position of parent is changing (in the example it's from fixed to absolute). This works without problem in Webkit browsers, but in Firefox (v. 17.0.1) there's no animated transition.
jsFiddle example: http://jsfiddle.net/chodorowicz/bc2YC/5/
Is there any solution to make it work in FF?
EDIT
It's fixed in Firefox 34
https://bugzilla.mozilla.org/show_bug.cgi?id=625289
CSS
#container {
position:fixed; left:100px; top:100px;
}
#container.some_state_position {
position:absolute;
}
.box {
width:100px; height:100px;
background:blue;
}
.some_state .box {
background:red; width:50px; height:50px;
}
img, .box {
-webkit-transition:all 1.5s ease;
-moz-transition:all 1.5s ease;
-ms-transition:all 1.5s ease;
transition:all 1.5s ease;
}
img {width:100%;}
.some_state .other_container img {
width:50%;
}

It seems you have found a good bug. Although this isn't my favorite fix, it does the job. Change your button2 to do this on click.
$("#button2").on({
click: function() {
$("#container").toggleClass("some_state");
setTimeout(function() {
$("#container").toggleClass("some_state_position");
}, 50);
}
});
It appears for firefox the toggleClass() fires immediately for both classes, causing some issues with the transition effects. Putting the timeout gives jQuery the enough time for it to process what it needs to, in order to do the transitions similar to those in Chrome, etc. I put the timeout to 50ms, this appears to give it enough time for jQuery to process what it needs to do. Going lower than that I saw sometimes, it fail and do what you are currently experiencing.

Related

How can I animate my less from "display: block" to "display: none"?

I have a less file that hide and display an element like the following:
.cmp-accordion__panel {
&--hidden {
display: none;
}
&--expanded {
display: block;
-webkit-animation: slide-down 0.5s ease-out;
-moz-animation: slide-down 0.5s ease-out;
}
}
#-webkit-keyframes slide-down {
0% {
opacity: 0;
-webkit-transform: translateY(-5%);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
}
}
#-moz-keyframes slide-down {
0% {
opacity: 0;
-moz-transform: translateY(-5%);
}
100% {
opacity: 1;
-moz-transform: translateY(0);
}
}
In my JavaScript, I toggle the class name of the element between "cmp-accordion__panel--hidden" and "cmp-accordion__panel--expanded" if the event is triggered. I use keyframe and opacity to animate the transition from "display:none" to "display:block".
However, when I go from "display:block" to "display:none" to hide the element, the effect happens INSTANTLY. What should I add to animate the hiding?
As already said, is not possible animate or transition from display:block; to display: none; but this could be simulated in another way and is not necessary to use CSS animations, simply CSS transitions (in addition, is not necessary anymore to use vendor-prefixes to declare transitions or animations).
Please, look at this working example:
HTML (I inserted a fake content to create an element with a relative big height)
<div class="cmp-accordion__panel--expanded">
b<br>b<br>b<br>b<br>b<br>b<br>b<br>b<br>b<br>b<br>b<br>b<br>b<br>b<br>b<br>b<br>b<br>b<br>b<br>b
</div>
LESS
[class*="cmp-accordion__panel"] {
border:solid 1px red;
overflow:hidden;
transition:opacity 0.3s ease-out, max-height 0.8s ease-out;
}
.cmp-accordion__panel {
&--hidden {
max-height:0;
opacity:0;
}
&--expanded {
opacity:1;
max-height:1000px;
}
}
Please note that, thanks to attribute partial value selector I added also some rules that apply to both *--hidden and *--expanded classes (I personally prefer a general class and an addition of a second one in some cases, instead of switching between two, but I did not want to change too much your approach).
The key rule is switching between two values of max-height property, from a 0 value to another "enough big" one. If you effectively know final height of the element you can simply use also height property, but in case of dynamic content, max-height did the trick.
Please note also the presence of overflow:hidden; applied to both classes, to simulate height changes.
Finally, animation effect relies only on a CSS transition applied to opacity and max-height properties, with different timings to enhance effect.
You cannot animate or transition from display: block; to display: none;, so you will need to remove this if you wish to animate it.
To ensure it fades and is removed you should animate the visibilty and opacity attributes.
Alternatively if you are using jQuery you can use the .fadeOut() function.
MDN - CSS Visibility
jQuery - fadeOut()

perform an animation from the current position of the element at 10px on the right

I'm new to this topic of animations. Basically I want to move a div .son in your hover property. I would like this animation to start from its current position at 10px on the right. I am normally able to perform this animation by defining an initial position for "left" and an ending for "left" but in this case I want it to take the initial position in which it is and thus move 10px to the right.
<div class="father">
<div class="son">
</div>
</div>
.father{
position:relative;
width:300px;
height:300px;
border:1px solid red;
}
.son{
position:absolute;
width:100px;
height:100px;
border:1px solid red;
}
.son:hover {
-webkit-transition: all 0.5s ease;
-webkit-animation: fadein_1 0.2s ease-in;
}
#-webkit-keyframes fadein_1 {
from { opacity: 1;left:auto;z-index:2; }
to { opacity: 0; left:10px;z-index:3; }
}
this is my code:
https://jsfiddle.net/ze9vdLa3/it?
thanks
If you can use jQuery, check this :
https://jsfiddle.net/95agyjuo/
jQuery('.son').mouseover(function(){
jQuery(this).animate({
left : "+=10"
})
})
You can accomplish the desired animation using the CSS translateX() function. As #Joe Koker stated, this is more performant than animating the left property.
#-webkit-keyframes fadein_1 {
from {
opacity: 1;
transform: translateX(0px); //start from initial position
z-index:2;
}
to {
opacity: 0;
transform: translateX(10px); //move 10px
z-index:3;
}
}
Your question is a bit vague, but if what you want is to animate an element to the right, it's easy. You almost had it.
Some things:
It's not possible to animate from auto, so I had to change left:auto -> left: 0; (from { opacity: 1;left:0;z-index:2; })
-webkit- isn't necessary
It's not possible to animate z-index either
Here is a working fiddle: https://jsfiddle.net/oqdn6xLg/1/
Though for this use case, I would recommend using the transition property instead, as its simpler. Check out the difference here: https://jsfiddle.net/L04xhgpr/
Note that transform: translateX() performs better, as others have already said :)

In Chrome, CSS3 animation on element with changing visibility gets played only when hidden

See this Plunkr:
Assume an animated (CSS3) element is visible when the page is loaded. When you toggle it's visibility (visible/hidden) the animation will stop playing. If you pay closer attention you'll see that it's actually playing when the element is hidden and stops playing when it's visible.
This problem goes away if the element is set to visibility:hidden when the page is loaded and only then switched to visible.
Here's my simple jquery code for switching classes
$(function(){
$("body").click(function(){
if($(".busy-content").hasClass("on")){
$(".busy-content").removeClass("on");
}else{
$(".busy-content").addClass("on");
}
});
});
And here is the CSS (copied from another app):
.busy-content{
position:absolute;
top:0;
right:0;
bottom:0;
left:0;
background:rgba(255,255,255,0.7);
visibility:hidden;
opacity:0;
-webkit-transition: all 0.2s;
-o-transition: all 0.2s;
-moz-transition: all 0.2s;
transition: all 0.2s;
}
.busy-content.on{
visibility:visible;
opacity:1;
}
It's a very strange behavior that doesn't repeat itself in IE/Edge/FF. Is this a Chrome bug? Does anyone have a solution?
Thanks!
E
I think you can use .toggle(). Here is the updated fiddle and it is working in chrome.
jQuery Code
$(function(){
$("body").click(function(){
//if($(".busy-content").hasClass("on")){
//$(".busy-content").removeClass("on");
//}else{
// $(".busy-content").addClass("on");
//}
$(".busy-content").toggle();
});
});

Slide up/down effect with ng-show and ng-animate

I'm trying to use ng-animate to get a behavior similar to JQuery's slideUp() and slideDown(). Only I'd rather use ng-show
I'm looking at the ng-animate tutorial here - http://www.yearofmoo.com/2013/04/animation-in-angularjs.html,
and I can reproduce the fade in/out effect in the example provided.
How could I change the css to get slide up/down behaviour?
Also, if possible, it's better that the css doesn't know about the component height in pixels.
That way I can reuse the css for different elements.
I've written an Angular directive that does slideToggle() without jQuery.
https://github.com/EricWVGG/AngularSlideables
This is actually pretty easy to do. All you have to do is change the css.
Here's a fiddle with a very simple fade animation: http://jsfiddle.net/elthrasher/sNpjH/
To make it into a sliding animation, I first had to put my element in a box (that's the slide-container), then I added another element to replace the one that was leaving, just because I thought it would look nice. Take it out and the example will still work.
I changed the animation css from 'fade' to 'slide' but please note that these are the names I gave it. I could have written slide animation css named 'fade' or anything else for that matter.
The important part is what's in the css. Here's the original 'fade' css:
.fade-hide, .fade-show {
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
}
.fade-hide {
opacity:1;
}
.fade-hide.fade-hide-active {
opacity:0;
}
.fade-show {
opacity:0;
}
.fade-show.fade-show-active {
opacity:1;
}
This code changes the opacity of the element from 0 (completely transparent) to 1 (completely opaque) and back again. The solution is to leave opacity alone and instead change the top (or left, if you want to move left-right).
.slide-hide, .slide-show {
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
}
.slide-hide {
position: relative;
top: 0;
}
.slide-hide.slide-hide-active {
position: absolute;
top: -100px;
}
.slide-show {
position: absolute;
top: 100px;
}
.slide-show.slide-show-active {
position: relative;
top: 0px;
}
I'm also changing from relative to absolute positioning so only one of the elements takes up space in the container at a time.
Here's the finished product: http://jsfiddle.net/elthrasher/Uz2Dk/. Hope this helps!
update for Angular 1.2+ (v1.2.6 at the time of this post):
.stuff-to-show {
position: relative;
height: 100px;
-webkit-transition: top linear 1.5s;
transition: top linear 1.5s;
top: 0;
}
.stuff-to-show.ng-hide {
top: -100px;
}
.stuff-to-show.ng-hide-add,
.stuff-to-show.ng-hide-remove {
display: block!important;
}
(plunker)
This can actually be done in CSS and very minimal JS just by adding a CSS class (don't set styles directly in JS!) with e.g. a ng-clickevent. The principle is that one can't animate height: 0; to height: auto; but this can be tricked by animating the max-height property. The container will expand to it's "auto-height" value when .foo-open is set - no need for fixed height or positioning.
.foo {
max-height: 0;
}
.foo--open {
max-height: 1000px; /* some arbitrary big value */
transition: ...
}
see this fiddle by the excellent Lea Verou
As a concern raised in the comments, note that while this animation works perfectly with linear easing, any exponential easing will produce a behaviour different from what could be expected - due to the fact that the animated property is max-height and not height itself; specifically, only the height fraction of the easing curve of max-height will be displayed.
I ended up abandoning the code for my other answer to this question and going with this answer instead.
I believe the best way to do this is to not use ng-show and ng-animate at all.
/* Executes jQuery slideDown and slideUp based on value of toggle-slidedown
attribute. Set duration using slidedown-duration attribute. Add the
toggle-required attribute to all contained form controls which are
input, select, or textarea. Defaults to hidden (up) if not specified
in slidedown-init attribute. */
fboApp.directive('toggleSlidedown', function(){
return {
restrict: 'A',
link: function (scope, elem, attrs, ctrl) {
if ('down' == attrs.slidedownInit){
elem.css('display', '');
} else {
elem.css('display', 'none');
}
scope.$watch(attrs.toggleSlidedown, function (val) {
var duration = _.isUndefined(attrs.slidedownDuration) ? 150 : attrs.slidedownDuration;
if (val) {
elem.slideDown(duration);
} else {
elem.slideUp(duration);
}
});
}
}
});
This class-based javascript animation works in AngularJS 1.2 (and 1.4 tested)
Edit: I ended up abandoning this code and went a completely different direction. I like my other answer much better. This answer will give you some problems in certain situations.
myApp.animation('.ng-show-toggle-slidedown', function(){
return {
beforeAddClass : function(element, className, done){
if (className == 'ng-hide'){
$(element).slideUp({duration: 400}, done);
} else {done();}
},
beforeRemoveClass : function(element, className, done){
if (className == 'ng-hide'){
$(element).css({display:'none'});
$(element).slideDown({duration: 400}, done);
} else {done();}
}
}
});
Simply add the .ng-hide-toggle-slidedown class to the container element, and the jQuery slide down behavior will be implemented based on the ng-hide class.
You must include the $(element).css({display:'none'}) line in the beforeRemoveClass method because jQuery will not execute a slideDown unless the element is in a state of display: none prior to starting the jQuery animation. AngularJS uses the CSS
.ng-hide:not(.ng-hide-animate) {
display: none !important;
}
to hide the element. jQuery is not aware of this state, and jQuery will need the display:none prior to the first slide down animation.
The AngularJS animation will add the .ng-hide-animate and .ng-animate classes while the animation is occuring.
You should use Javascript animations for this - it is not possible in pure CSS, because you can't know the height of any element. Follow the instructions it has for you about javascript animation implementation, and copy slideUp and slideDown from jQuery's source.
What's wrong with actually using ng-animate for ng-show as you mentioned?
<script src="lib/angulr.js"></script>
<script src="lib/angulr_animate.js"></script>
<script>
var app=angular.module('ang_app', ['ngAnimate']);
app.controller('ang_control01_main', function($scope) {
});
</script>
<style>
#myDiv {
transition: .5s;
background-color: lightblue;
height: 100px;
}
#myDiv.ng-hide {
height: 0;
}
</style>
<body ng-app="ang_app" ng-controller="ang_control01_main">
<input type="checkbox" ng-model="myCheck">
<div id="myDiv" ng-show="myCheck"></div>
</body>

How to disable CSS3 transitions when zooming?

When I use CSS3 transitions on an element's width/height or top/right/bottom/left, and I adjust the page zoom using CTRL+, CTRL- or CTRL0, the browser animates the change to these attributes.
Is there a way to use these transitions, but prevent the browser from using them only when zooming?
EDIT:
Sample HTML:
<div></div>
Sample CSS:
div {
background:red;
height:200px;
width:200px;
-moz-transition:1s;
-webkit-transition:1s;
transition:1s;
}
div:hover {
height:300px;
width:300px;
}
Code also available on jsFiddle.
I've thought of a workaround that uses Javascript to disable the transition while CTRL is being pressed. It handles the keyboard shortcuts listed above, as well as CTRL+scrollwheel, but only when the document has focus.
It can't handle zooming initiated by using the menu, but its better than nothing.
HTML
<div></div>
CSS:
div {
background:red;
height:200px;
width:200px;
-moz-transition:1s;
-webkit-transition:1s;
transition:1s;
}
div:hover {
height:300px;
width:300px;
}
.zooming {
-moz-transition:0s;
-webkit-transition:0s;
transition:0s;
}
jQuery:
$(document)
.keydown(function(e) { if (e.ctrlKey) { $('div').addClass('zooming'); }})
.keyup(function(e) { $('div').removeClass('zooming'); });
Updated jsFiddle. Only tested in Chrome so far.
Try this solution:
http://jsfiddle.net/995zE/
It works by adding the transition css when you click the buttons, and when you zoom the browser window, it removes that css.
This works on Firefox, Chrome, and IE 10. On Firefox and IE, when you zoom, the transition continues as normal, and the zooming doesn't affect it. On Chrome, the transition fast-forwards to its final state.
HTML:
<button id="decrease_width">- width</button>
<button id="increase_width">+ width</button>
<div id="test"></div>
CSS:
div#test
{
width: 100px;
height: 100px;
border: 1px solid red;
}
div#test.transition
{
transition: width 2s ease;
-webkit-transition: width 2s ease;
-moz-transition: width 2s ease;
-o-transition: width 2s ease;
}
JavaScript:
var transition_class = 'transition';
var $test = jQuery('#test');
function transition_test(width) {
$test.addClass(transition_class).css('width', $test.width() + width);
}
jQuery("#decrease_width").click(function () {
transition_test(-50);
});
jQuery("#increase_width").click(function () {
transition_test(50);
});
jQuery(window).resize(function () {
$test.removeClass(transition_class);
});

Resources