I am currently writing a jQuery plugin to create / manage CSS transitions, and I found this strange behavior with transition-duration.
Apparently, while a transition is running, any changes to the duration property are ignored unless the properties being transitioned receive a different value. The duration itself does not cause the transition to change.
Following is some code which shows an example of this, and below are some links to jsFiddle to give you a better idea of the transition behavior I am trying to achieve.
/* starting transition */
.t1 {
-webkit-transition-duration: 5s;
-webkit-transition-property: width;
width: 500px;
}
/* during the above, this will do nothing */
.t2 {
-webkit-transition-duration: 200ms;
-webkit-transition-property: width;
width: 500px;
}
/* but this will override the transition as expected */
.t3 {
-webkit-transition-duration: 200ms;
-webkit-transition-property: width;
width: 501px; /* 1 pixel added */
}
jsFiddle 1 - CSS duration problem: http://jsfiddle.net/danro/Kd58j/
jsFiddle 2 - Desired effect w/ jQuery: http://jsfiddle.net/danro/xPwc4/
Any ideas on how to force the transition to accept the updated duration?
UPDATE
It looks like this behavior is defined in the spec, but I am still open to a workaround if anyone has one.
(From www.w3.org/TR/css3-transitions/#starting)
Once the transition of a property has started, it must continue running based on the original timing function, duration, and delay, even if the ‘transition-timing-function’, ‘transition-duration’, or ‘transition-delay’ property changes before the transition is complete.
I've run into the same issue when I needed to overwrite transition-duration but leaving the transition-property intact. The only simple workaround I've found so far is to actually change transition-property a little bit, i.e. instead of opacity: 0 make it opacity: 0.0001.
just tested your first link with Chrome and Safari and it works fine, just like the jQuery example :)
Related
Sometimes when using JQuery to set the css attribute of an element such as "height", "max-height", it automatically binds animation to the change. Sometimes it is awesome, but it is not always necessary. Is there a way to disable this kind of animation?
Actually what is the exact situation that causes JQuery to automatically bind animations? because I don't always see this kind of behavior. I am not using JQuery-UI.
Perhaps the element you are changing the height of has a css transition property that is responsibe for the animation.
$(function() {
$('.myClass').css('width', '100px');
});
.myClass {
height: 50px;
width: 300px;
background-color: red;
}
.transition {
transition: width 3s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
with transition
<div class="myClass transition">
</div>
without transition
<div class="myClass">
</div>
Borrowing from What is the cleanest way to disable CSS transition effects temporarily?
You can then create a class that will override the transition property and toggle that class
.notransition {
-webkit-transition: none !important;
-moz-transition: none !important;
-o-transition: none !important;
-ms-transition: none !important;
transition: none !important;
}
Note
If you go this route, you may run into the issue of needing to reflow the css
From What is the cleanest way to disable CSS transition effects temporarily? once again:
There are various ways to do this - see here for some. The closest
thing there is to a 'standard' way of doing this is to read the
offsetHeight property of the element.
One solution that actually works, then, is
$someElement.addClass('notransition'); // Disable transitions
doWhateverCssChangesYouWant($someElement);
$someElement[0].offsetHeight; // Trigger a reflow, flushing the CSS
changes $someElement.removeClass('notransition'); // Re-enable
transitions
You can use jQuery.fx.off parameter to define if animation should be used. Read about it here
Also you can modify css values directly without modifying function like
$("#id").css('height', '100px');
instead of
$("#id").height(100);
i'm working on a project where i need a semi-transparent div to slideover the entire page at a certain point. i have a version working currently, but it's not as smooth as i'd like it to be.
http://jsfiddle.net/27e310e8/
jQuery from above example:
var windowWidth = $(window).innerWidth();
var windowHeight = $(window).innerHeight();
function blackOut() {
$('#fail-screen').css({
"width": windowWidth + "px",
"height": windowHeight + "px"
});
$('#fail-screen').delay(1000).animate({
top: '0px'
}, 1000, 'linear');
}
$(document).ready(function () {
blackOut(windowWidth, windowHeight);
});
as you can see i'm getting the innerWidth and height to set the "fail-screen" div, as setting it to 100% wasn't working well. i'm using jQuery to animate the top position of the "fail-screen" div.
again, i'm just looking to refactor this code and improve overall presentation and performance. i'm open to using animation/physic libraries if anyone knows of any that would be good to use here.
appreciate any suggestions.
As #Jason has mentioned, I strongly recommend using CSS transforms instead of fiddling with the offsets. That is being not only are CSS transforms offloaded to the GPU (intelligently determined by the browser as of when needed, but you can also force it), but it allows for subpixel rendering. Paul Irish published a rather good write-up on this topic back in 2012.
Also, your code is slightly problematic in the sense that it fails to handle viewport resize events. In fact, a more straightforward solution would simply be using position: fixed, and then playing around with CSS transform to bring the element into view after a delay.
See updated fiddle here: http://jsfiddle.net/teddyrised/27e310e8/2/
For the JS, we simply use the .css() method. The delay, animation duration and even the timing function can be easily done via CSS.
The new JS is rather straightforward: we set the transform of #fail-screen so that we move it back to its original vertical position. jQuery automagically prefixes the transform property ;)
$(document).ready(function () {
$('#fail-screen').css({
'transform': 'translateY(0)'
});
});
For the CSS, we initially set a vertical translation (translateY) of -100%, which means we push the element upwards by its own height. Using fixed positioning and declaring all four offsets as 0, we can force the element to fill the viewport without any advanced hack of listening to window resize events via JS. Remember that you will have to add vendor prefixes to the transform property to maximize cross-browser compatibility.
CSS can also handle transition delay, duration and even the timing function, i.e. transition: [property] [duration] [timing-function] [delay]; Since in your jQuery code you have set both duration and delay to be 500ms, it should be transition: all 0.5s linear 0.5s. However, the linear timing function doesn't look good — perhaps using ease-in-out would be better, or even a custom cubic-bezier curve, perhaps?
Also, I recommend moving the opacity to the background-color value, simply because if you set an opacity on the element itself, all child nodes will be rendered at 0.6 opacity, too... it might be something that you do not want to achieve.
#fail-screen{
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0,0,0,.6); /* Moved opacity to background-color */
position: fixed; /* Use fixed positioning */
z-index: 303;
/* Use CSS transform to move the element up by its own height */
-webkit-transform: translateY(-100%);
-ms-transform: translateY(-100%);
-o-transform: translateY(-100%);
transform: translateY(-100%);
/* CSS transition */
transition: all .5s ease-in-out .5s;
}
For a number of projects now I have had elements on the page which I want to translate out of the screen area (have them fly out of the document). In proper code this should be possible just by adding a class to the relevant element after which the css would handle the rest. The problem lies in the fact that if for example
.block.hide{
-webkit-transform:translateY(-10000px);
}
is used the element will first of all fly out of the screen unnecessarily far and with an unnecessarily high speed. And purely from an aesthetic point of view there's a lot left to be desired (Theoretically speaking for example a screen with a height of 10000px could be introduced one day in the future).
(Update) The problem why percentages can't be used is that 100% is relative to the element itself, rather than to the parent element/screen size. And containing the element in a full-sized parent would be possible, but would create a mess with click events. And after a few answers, allow me to point out that I am talking about translations, not about position:absolute css3 transitions (which are all fine, but once you get enough of them they stop being fun).
What aesthetically pleasing solutions to allow an element to translate out of a screen in a fixed amount of time can you guys think of?
Example code can be found in this jsfiddle demonstrating the basic concept.
http://jsfiddle.net/ATcpw/
(see my own answer below for a bit more information)
If you wrap the .block div with a container:
<div class="container">
<div class="block"></div>
</div>
<button>Click</button>
you could expand and then, translate the container itself after the click event
document.querySelector("button").addEventListener("click", function () {
document.querySelector(".container").classList.add("hide");
});
with this style
.block {
position:absolute;
bottom:10px;
right:10px;
left:10px;
height:100px;
background:gray;
}
.container {
-webkit-transition: -webkit-transform ease-in-out 1s;
-webkit-transform-origin: top;
-webkit-transition-delay: 0.1s; /* Needed to calculate the vertical area to shift with translateY */
}
.container.hide {
position:absolute;
top:0;
left:0;
bottom:0;
right:0;
/* background:#f00; /* Uncomment to see the affected area */
-webkit-transform: translateY(-110%);
}
In this way, it is possible to apply a correct translationY percentage ( a little more than 100%, just to have it out of the way ) and mantaining the button clickable.
You could see a working example here : http://jsfiddle.net/MG7bK/
P.S: I noticed that the transition delay is needed only for the transitionY property, otherwise the animation would fail, probably because it tries to start before having an actual value for the height. It could be omitted if you use the horizontal disappearing, with translateX.
What I did is use the vh (view height) unit. It's always relative to the screen size, not the element itself:
/* moves the element one screen height down */
translateY(calc(100vh))
So if you know the position of the element in the screen (say top:320px), you can move it exactly off the screen:
/* moves the element down exactly off the bottom of the screen */
translateY(calc(100vh - 320px))
I know this is not exactly what you were asking but...
Would you consider using CSS animations with Greensock's Animation Platform? It is terribly fast (it claims it's 20 times faster than jQuery), you can see the speed test here: http://www.greensock.com/js/speed.html
It would make your code nicer I believe, and instead of trying to hack CSS animations you could focus on more important stuff.
I have created a JSFiddle here: http://jsfiddle.net/ATcpw/4/
Both CSS and possible JS look simpler:
document.querySelector("button").addEventListener("click",function(){
var toAnimate = document.querySelector(".block");
TweenMax.to(toAnimate, 2, {y: -window.innerHeight});
});
CSS:
.block{
position:absolute;
bottom:10px;
right:10px;
left:10px;
height:100px;
background-image: url(http://lorempixel.com/800/100);
}
I recently built an app which used precisely this technique for sliding 'panels' (or pages) and tabs of the application in and out of view. A basic implementation of the tabs mechanism can be seen here.
Basically (pesudo-code to illustrate the concept, minus prefixes etc):
.page {
transform: translateY(100%);
}
.page.active {
transform: translateY(0%);
}
The problem I had was that Android Webkit in particular wouldn't calculate percentage values correctly. In the end I had to use script to grab the viewport width and specify the value in pixels, then write the rules using a library for dynamic stylesheet parsing.
But eventually, and in spite of only these minor platform-specific problems, this worked perfectly for me.
Use calc method (http://jsfiddle.net/ATcpw/2/):
.block{
position:absolute;
top: -webkit-calc(100% - 110px);
right:10px;
left:10px;
height:100px;
background:gray;
-webkit-transition: all 2s;
/*this adds GPU acceleration*/
transform: translate3d(0,0,0);
-webkit-transform: translate3d(0,0,0);
}
.block.hide{
top: -100px;
}
Since you are using -webkit prefix I used it as well.
calc is supported by majority of browsers: http://caniuse.com/#search=calc
One very simple, but not aesthetically pleasing solution is to define the class dynamically:
var stylesheet = document.styleSheets[0];
var ruleBlockHide;
and
//onresize
if(ruleBlockHide) stylesheet.deleteRule(ruleBlockHide);
ruleBlockHide = stylesheet.insertRule('.block.hide{ -webkit-transform:translateY(-'+window.innerHeight+'px); }',stylesheet.cssRules.length);
see: http://jsfiddle.net/PDU7T/
The reason a reference to the rule needs to be kept is that after each screen resize the rule has to be deleted and re-added.
Although this solution gets the job done, there has to be some DOM/CSS combination which would allow this to be done without javascript (something along the lines of a 100%x100% element containing such a block, but I haven't been able to figure out any transform based way).
get the document width. then use a java script trigger to trigger the css3 translation.
function translate(){
var width = document.body.Width;
document.getElementById('whateverdiv').style='translateX(' + width + 'px)';
}
This is simple
add the following to your div
.myDiv {
-webkit-transition-property: left;
-webkit-transition-duration: 0.5s;
-webkit-transition-timing-function: ease-in-out;
-webkit-transition-delay: initial
}
then change the "left" property of it either by adding an additional class or by jQuery
This will animate it along the x-axis
Note: you can change the -webkit-transition-property to any property you want and this will animate it
I'm trying to replicate this effect using CSS effects or transitions.
Using animations I can animate the opacity, but only fadeIn, and the height (which should control the slide) doesn't seem to work at all :(
The closest I've got is by using javascript to set a temporary class on the element I want to animate, and on which I apply the initial opacity. But height doesn't work either. And there seems to be a slight delay on animation start.
Any other ideas?
So I ended up using the solution posted in the question Simon mentioned: With javascript I wrap the element I want to animate within a "wrapper" DIV on which I apply the animation. The wrapper will get its height changed from 0 to the height of the content DIV every time the label is clicked:
fiddle here
I know it requires some javascript, but the idea is to make the animation in CSS, and this is what it does. And if JS is disabled, the toggle will still work...
You can't currently animate on height when one of the heights involved is auto, you have to set two explicit heights. There's an extensive workaround posted as an answer to this similar question.
I made an alteration to your JS Fiddle, I beleive this is what you want; please see it here.
You need to specify a height on the div originally (0) and don't forget overflow:hidden; so that the content doesn't 'spil out' of the div. You will still need jQuery / Javascript however, to toggle a class but it means much less Javascript is required. (I toggled the class "change" which you will see on that fiddle)
input {
display:none;
}
label {
display:inline-block;
}
div {
white-space: pre;
background: #eee;
color: #333;
overflow:hidden;
height:0;
opacity:0;
-moz-transition:height 1s opacity 1s;
-webkit-transition:height 1s opacity 1s;
-o-transition:height 1s opacity 1s;
-ms-transition:height 1s opacity 1s;
transition:height 1s, opacity 1s;
}
.changed {
height:200px;
opacity: 1;
}
I added a few vendor prefixes to the transition CSS propery as I'm not sure what browser you'll be using and I'm on firefox so I need the -moz- prefix lol :)
The only problem I can see with this is that height:auto or height:100% doesn't animate, so you'll need to specify ems or px... If this is going to be a problem (like if the content will be dynamic), I would advise using jQuery for the height animation.
I have a DIV class setup as follows:
div.map_view{
height: 420px;
transition: height 2s;
-moz-transition: height 2s; /* Firefox 4 */
-webkit-transition: height 2s; /* Safari and Chrome */
-o-transition: height 2s; /* Opera */
}
The purpose is when I change the height of this DIV, it animates a scroll (up in this case). When I call this function in my script:
document.getElementById('map_view').style.height = '0px';, it just immediately disappears (doesn't animate). However, if I comment this out and call the exact same line in my JS debugger, the animation works.
Why is this? What am I missing that causes it to do nothing in my script?
I know I've cut a couple of corners with this by using jquery but here's what I got:
http://jsfiddle.net/qZ6J4/7/
Take a look at that.
I actually found a helpful tutorial here: CSS3 Transitions in JavaScript. I basically setup my two CSS3 class definitions and use jQuery's .toggleClass() function to change between the two.