Robust way to event handle after the *last* CSS3 transition? - javascript

Say I have a CSS class that transitions in several settings, with different timings to boot:
.hidden {
opacity:0;
height:0px;
transition:opacity 1s ease;
transition:height 2s ease;
}
I want to handle some logic after ALL the transitions are done. I know there's a transitionend event for that:
$('#content').on('transitionend', function(e) {
mandatoryLogic()
})
$('#content').addClass('hidden');
But, how do I ensure this event handle happens once and only once, at the end of the last transition (after the 2s height transition in my case)?
I've seen a few examples that involve checking for the type of transitionend, but this closely couples the JS to a particular CSS definition. So far I'm unable to come up with a JS solution that would survive say:
opacity changed to 3s, now the longest.
new width transition to 0px over 4s introduced
extreme case, all transitions removed (so the effects are instant)

I'm not entirely sure what you're asking, but if you want to make sure it only happens once, then use a variable set to false initially, and once the transition takes place it gets set to true. For example:
var transition = false ;
if( !transition ){
// do what you need to do
transition = true ;
}
If you want to make sure it happens once all transitions are complete, then check for the values of #content or .hidden. Whichever you prefer.
var height = $(element).height() ;
var opacity = $(element).css('opacity') ;
var transition = false ;
if( !transition && opacity = 'wanted value' && height = 'wanted value' ){
// do what you need to do
transition = true ;
}
This way, with var transition being set to true it won't happen again.

Related

Latency with animationiteration

So I've got this modal that I've designed to be opened via a checkbox input, but the problem with that was that I have my divs and section arranged in such a manner that #yt-modal-cont can't be in the same container as my input due to z-index issues. So I used javascript:
if(modalToggle.checked === true) {
oLay.style.display = 'block';}
Simple enough fix, however, that's not my issue. For the animation, the route I chose was:
#yt-modal-cont {
animation: fade-before 0.3s ease-out 0s 2 alternate-reverse;
}
#keyframes fade-before {
0%{opacity:1;}
100%{opacity:0;}
}
<script>
var oLay = document.getElementById('yt-modal-cont');
var modalToggle = document.getElementById('vids');
function fadein(){
oLay.style['animation-play-state'] = 'paused';
}
function isOpen(){
if(modalToggle.checked === true) {
oLay.style.display = 'block';}
oLay.addEventListener('animationiteration', fadein);
}
}
modalToggle.addEventListener('click', isOpen);
</script>
and then for the closing function I have it setting animation-play-state=running to play through to the end, but that's not relevant. My issue is that javascript seems to be catching the end of the first iteration--and thusly adding the style--too late, as when the modal opens it's opacity is faded slightly. What's weirder, is that it's only sometimes; I open it once and it's kinda faded, I open it again and it's full opacity, then again and it's really faded... I tried adding oLay.style.opacity = 1 to the fadein function to try and override the CSS but it's still hit-or-miss. After looking at
Lag in animated image array
I feel that requestAnimationframe might be the answer, but my js newb-iness makes it so that I don't really understand what that's all about... Is that the right track or is there a different solution?

css transition only works within setInterval? why?

At a certain point in a script the following occurs
CSS-CODE
.tt{ opacity: 0; transition: all 1s;}
JS-CODE (THIS DOES NOT TRANSITION)
this.element.insertAdjacentHTML('beforeend',`<div class="tt">this is a message</div>`);
this.tooltip = this.element.querySelector('.tt');
this.tooltip.style.opacity = 1; => THIS DOES NOT TRANSITION
BUT WHEN I CHANGE MY CODE IN (THIS DOES TRANSITION)
this.element.insertAdjacentHTML('beforeend',`<div class="tt">${this.element.dataset.tooltip}</div>`);
this.tooltip = this.element.querySelector('.tt');
setTimeout(function(){
this.tooltip.style.opacity = 1 => THIS DOES TRANSITION
}.bind(this),0);
WHY? (even if the setTimeoutdelay is set to 0)
Transitions occur between one rendered state and another.
Your first code block changes the opacity property before the element has been rendered at opacity 0.
Adding timeout introduces a delay in which the element can be rendered at opacity 0.
even if the setTimeoutdelay is set to 0)
setTimeout has a minimum delay on it, and even if it didn't, the queue of jobs would probably have repaint above the next timed action.

Css3 transition queue

I am trying to queue css transition with same properties. Basically I want to translate an element to certain position (so transition duration 0) before I make another translate.
This is a mockup, click on move (box should move 100px right, before translate 100px left)
this doesnt work because second transition overwrites first?
https://jsfiddle.net/aqwaypoh/3/
This works (I needed transition duration non zero (0.01) otherwise transitionend doesnt fire).
https://jsfiddle.net/dpv3xzth/5/
There is another problem that transition end fires 2 times on chrome, but I could fix that, I was just wondering is there a better way to write this?
I would prefer if I could write this without end event or timer if possible?
<div class="box"></div>
move</a>
Update:
You can use CSS3 animation property with #keyframes.
.box.animate {
animation: move 2s;
}
#keyframes move {
0% {
transform: translate(100px);
}
100% {
transform: translate(0px);
}
}
And to make the box move you can add class animate to your element. Or you can set animation property yourself in javascript, it's up to you.
box.addClass('animate')
jsfiddle: https://jsfiddle.net/aqwaypoh/7/
Adding a timeout on the second condition gives you what you want I believe.
var box = $('.box'),
move = $('.move').click(function() {
box.css({
"transform": "translate(100px)",
'transition-duration': '0s'
});
setTimeout(function(){
box.css({
"transform": "translate(0px)",
'transition-duration': '0.5s'
});
}, 1);
})

Raphael js - animate opacity on group while retaining individual elements opacities

How can I animate the opacity on a bunch of Raphael objects as one object while maintaining the individual elements opacity states? I can't animate on sets without affecting each individual element, so how do I create one object to handle—I'm thinking in a jQuery mindset if that helps answer.
If you keep global variable, then you can do this. Look at the DEMO.
var p = new Raphael(10,10, 500, 500);
var x = 0.5;
var r = p.rect(20, 20, 100, 80, 5).attr({fill: 'red', opacity: x}),
c = p.circle(200, 200, 80).attr({fill: 'orange'}),
s = p.set(r, c);
s.click(function() {
s[0].attr({opacity: x - 0.3});
s[1].attr({opacity: 0.3});
});
A general solution that works for any number of elements, any number of changes and any number of sets is to use .customAttributes which calculate and store the correct opacity level for each element in the set based on a value attached to that individual element and a value for the set.
For example (in the demo, click multiple times to see the opacity of the set of three circles change one way and the individual circle clicked on change the other way):
http://jsbin.com/oxeyih/9/edit
Using something like this, which essentially adds this feature to all existing and future sets and elements in this Raphael paper instance:
// apply this using .attr or .animate to sets or each in a set
// to set the opacity of the set as a whole, maintaining relative opacity
paper.customAttributes.setOpacity = function( setOpacity ){
// elemOpacity might not be set yet
if (typeof this.attr('elemOpacity') == 'undefined') {
this.attr('elemOpacity', this.attr('opacity'));
}
return {opacity: setOpacity * this.attr('elemOpacity')};
}
// apply this using .attr or .animate to indiviual elements
// to set that element's opacity, factoring in the opacity of the set
paper.customAttributes.elemOpacity = function( elemOpacity ){
// setOpacity might not be set yet; setting it could create infinite loop
var setOpacity = this.attr('setOpacity');
setOpacity = typeof setOpacity == 'undefined' ? 1 : setOpacity;
return {opacity: setOpacity * elemOpacity };
}
You may want to add some kind of validation if your code could go wrong if either of the custom attributes go below 0.0 or above 1.0.
Any time you set the opacity of an element, use animate({elemOpacity: xx}, time); (or .attr()) and it'll take the set opacity into account, and any time you want to set the opacity of a set, call animate({setOpacity: xx}, time); on a set, and it'll take each element's opacity into account.
In some cases with complex sets, rather than animating many elements which can be slow in many browsers, it's much better for performance, and simpler, to just plonk an overlay on top... This is a good idea if the elements in question can be safely sent .toBack() and you don't need to interact (click, hover) with them further.
Just add an overlay rectangle, set to match the inherited colour of the container element (here's a way to get inherited background colour dynamically), send it then the set .toBack(), and animate the opacity of that overlay.

How do you animate a twitter-bootstrap progress bar smoothly?

I have multi-player game with a 30 second timer at the bottom of the screen.
If none of the players make a move for 30 seconds, the form submits.
var ProgressValue = 0;
function showProgress() {
ProgressValue += 100/30;
if (ProgressValue > 100) {
$('form').submit();
}
// Ajax is done here to see if anyone has made a move.
$('.progress .bar').css('width',ProgressValue + '%');
setTimeout(showProgress, 1000);
}
setTimeout(showProgress, 1000);
Each second, I check the Application scope to see if anyone has changed the value of
Application.LastMove
I want the progress bar to animate smoothly, but I don't want to do it by reducing the timeout value. I think that checking to see if anyone has taken a move every second is enough load on the server already.
I've heard of WebSockets, but my current server is on ColdFusion 8, so (I think) I'm satisfied with doing an ajax call every second, unless you feel that ajax is not as elegant and from a less civilized age.
Q: How do you animate a twitter-bootstrap progress bar smoothly from 3.3% to 6.6%?
Don't animate using jQuery, prefer CSS animation, unless you have to support old browsers.
I've made this copying from Bootstrap style:
.bar {
-webkit-transition: width 30.0s ease !important;
-moz-transition: width 30.0s ease !important;
-o-transition: width 30.0s ease !important;
transition: width 30.0s ease !important;
}
For so long transition, I suggest you to try different animations: http://www.the-art-of-web.com/css/timing-function/
In my example I've added two things that could be usefull:
Button text changes when the animation starts and when it ends (just to check animation timings)
Check if the browser support this animation: you can use your jQuery code as fallback mode
For more information about how to detect CSS animation support: https://developer.mozilla.org/en-US/docs/CSS/CSS_animations/Detecting_CSS_animation_support
Example: http://jsfiddle.net/CUbgr/5/

Categories

Resources