What I'm trying to do is to have on hover transition or animation (can be triggered via javascript with onmouseover or onmouseenter) that will also be reversible (so the opposite animation should happen on mouse leave) but
the opposite animation should have a delay
it should be able to reverse in the middle of animation without delay
It's hard to describe without showing so please check this codepen that is pretty close to what I'm trying to achieve: http://codepen.io/anon/pen/xROOqO
There are two problems here:
I need to check for elapsed time in transitionend handler, so I would need to update both css and js to update transition time
It still has delay when you quickly hover in and out on the reverse animation - looks like it's stuck in the middle
Is this even possible using css transitions (perhaps keyframes animation) or should I stick to setting timers in javascript and leave out the delay from css?
Not sure if what I'm going to present is simpler, but it seems to address some of your issues, and matches my taste.
The main idea is to admit that the problem is complicated due to multiple states, and address it using a state machine.
This allows for a declarative approach like this one:
const TRANSITIONS = {
'small-inside' : {
'transitionend' : 'big-inside',
'mouseover' : 'small-inside',
'mouseout' : 'small-outside',
},
'small-outside' : {
'transitionend' : 'small-outside',
'mouseover' : 'small-inside',
'mouseout' : 'small-outside',
},
'big-inside' : {
'transitionend' : 'big-inside',
'mouseover' : 'big-inside',
'mouseout' : 'big-outside',
},
'big-outside' : {
'transitionend' : 'small-outside',
'mouseover' : 'big-inside',
'mouseout' : 'big-outside',
},
}
And quite simple handling of events:
function step(e){
box.className = TRANSITIONS[box.className][e.type];
}
box.addEventListener('transitionend', step);
box.addEventListener('mouseover', step);
box.addEventListener('mouseout', step);
Another insight is that you can specify the delay using CSS transition-delay:3s property:
div.small-inside,
div.big-inside {
width: 300px;
}
div.small-outside,
div.big-outside {
width: 150px;
}
div.big-outside {
transition-delay:3s;
}
The proof of concept is here: http://codepen.io/anon/pen/pNNMWM.
What I do not like about my solution is that it assumes that the initial state is small-outside while actually the mouse pointer could be well located within the div when the page loads.
You've mentioned ability to trigger state transitions manually from JS. I believe this is possible as long as you keep track of two separate boolean variables: "is mouse inside?" and "does js asked to grow?". You can not mix them into a one state and expect correct "counting". As you see I already have 2*2=4 states because I'm trying to keep track of {small,big}x{inside,outside} - one could imagine extending it to {small,big}x{inside,outside}x{js-open,js-close} in similar manner, with some extra "events" like 'open' and 'close'.
Related
Context:
I am trying to achieve smooth movement(top,left,bottom,right) of div using arrow keys.
Problem:
The movement of div as you can see above is slightly glitchy, I have tried to use lodash throttle which seems like the correct approach to make it look smooth but it doesn't seem to solve the problem, not sure where am I going wrong, any suggestions would be helpful.
DemoRef -throttle vs debounce vs regular
Demo Ref 2
Code
move: throttle(function (type) {
if (type === "up") {
this.moveUp(0);
} else if (type === "down") {
this.moveDown(0);
} else if (type === "left") {
this.moveLeft(0);
} else if (type === "right") {
this.moveRight(0);
}
}, 500),
What I have tried till now --> Codesandbox
This seems to be down to the your keyboard repeat rate. When you hold down a key it will fire, and then after a certain delay set in your OS it will then fire again and again. The delay between the first and second firing can be different from the delay between all the subsequent repeats. What you want to do is have the box animate smoothly across the screen, but if you add or subtract 10px to or from its position in response to the key firing you will get this kind of behaviour. I don't think that it will be easy to get a completely consistent movement because you need to make assumptions about the user's keyboard repeat rate. You could try adding a CSS transition to smooth it out, but it still might not give the best results.
What I would suggest is to listen for keydown and keyup events, and add a loop (maybe using requestAnimationFrame to animate the div while the key is down. Once the key is lifted you halt the animation.
I'm having some major headache trying to apply CSS3 transitions to a slideshow trough JavaScript.
Basically the JavaScript gets all of the slides in the slideshow and applies CSS classes to the correct elements to give a nice animated effect, if there is no CSS3 transitions support it will just apply the styles without a transition.
Now, my 'little' problem. All works as expected, all slides get the correct styles, the code runs without bugs (so far). But the specified transitions do not work, even though the correct styles where applied. Also, styles and transitions work when I apply them myself trough the inspector.
Since I couldn't find a logical explanation myself I thought someone here could answer it, pretty please?
I've put together a little example of what the code is right now: http://g2f.nl/38rvma
Or use JSfiddle (no images): http://jsfiddle.net/5RgGV/1/
To make transition work, three things have to happen.
the element has to have the property explicitly defined, in this case: opacity: 0;
the element must have the transition defined: transition: opacity 2s;
the new property must be set: opacity: 1
If you are assigning 1 and 2 dynamically, like you are in your example, there needs to be a delay before 3 so the browser can process the request. The reason it works when you are debugging it is that you are creating this delay by stepping through it, giving the browser time to process. Give a delay to assigning .target-fadein:
window.setTimeout(function() {
slides[targetIndex].className += " target-fadein";
}, 100);
Or put .target-fadein-begin into your HTML directly so it's parsed on load and will be ready for the transition.
Adding transition to an element is not what triggers the animation, changing the property does.
// Works
document.getElementById('fade1').className += ' fade-in'
// Doesn't work
document.getElementById('fade2').className = 'fadeable'
document.getElementById('fade2').className += ' fade-in'
// Works
document.getElementById('fade3').className = 'fadeable'
window.setTimeout(function() {
document.getElementById('fade3').className += ' fade-in'
}, 50)
.fadeable {
opacity: 0;
}
.fade-in {
opacity: 1;
transition: opacity 2s;
}
<div id="fade1" class="fadeable">fade 1 - works</div>
<div id="fade2">fade 2 - doesn't work</div>
<div id="fade3">fade 3 - works</div>
Trick the layout engine!
function finalizeAndCleanUp (event) {
if (event.propertyName == 'opacity') {
this.style.opacity = '0'
this.removeEventListener('transitionend', finalizeAndCleanUp)
}
}
element.style.transition = 'opacity 1s'
element.style.opacity = '0'
element.addEventListener('transitionend', finalizeAndCleanUp)
// next line's important but there's no need to store the value
element.offsetHeight
element.style.opacity = '1'
As already mentioned, transitions work by interpolating from state A to state B. If your script makes changes in the same function, layout engine cannot separate where state A ends and B begins. Unless you give it a hint.
Since there is no official way to make the hint, you must rely on side effects of some functions. In this case .offsetHeight getter which implicitly makes the layout engine to stop, evaluate and calculate all properties that are set, and return a value. Typically, this should be avoided for performance implications, but in our case this is exactly what's needed: state consolidation.
Cleanup code added for completeness.
Some people have asked about why there is a delay. The standard wants to allow multiple transitions, known as a style change event, to happen at once (such as an element fading in at the same time it rotates into view). Unfortunately it does not define an explicit way to group which transitions you want to occur at the same time. Instead it lets the browsers arbitrarily choose which transitions occur at the same time by how far apart they are called. Most browsers seem to use their refresh rate to define this time.
Here is the standard if you want more details:
http://dev.w3.org/csswg/css-transitions/#starting
This is certainly going to be an easy one but I can't get my head around what I am doing wrong...
I am trying to do a hover effect on a UL that affects a link within one of the UL LI's.
My current code looks like this:
$("ul.punchlines").hover(function () {
$(this).find("li a.light-grey-gradient").animate({'width' : '60%','top':'-65px'});
});
$("ul.punchlines").mouseleave(function () {
$(this).find("li a.light-grey-gradient").animate({'width' : '30%','top':'0px'});
});
This technically works as it gives the effect that the base of the element to be scaled remains in place and scales up from the bottom however it does it in two stages, I am trying to get this effect to happen all in one motion so it is a seamless scale and move.
I can do this easily with basic CSS3 transitions but as it is not supported in IE9 I am trying to use jQuery to allow for maximum browser support.
Can anyone offer a little support firstly about how I get the animation to happen in one motion (not staggered) and secondly if this is the right approach? I am new to jquery and only just getting my hands dirty with it :-)
Please see JQuery hover api:
http://api.jquery.com/hover/
also make sure that your "li" have absolute position.
$("ul.punchlines").hover(function () {
$(this).find("li a.light-grey-gradient").animate({'width' : '60%','top':'-65px'});
}, function () {
$(this).find("li a.light-grey-gradient").animate({'width' : '30%','top':'0px'});
});
So here is the scenario. I'm trying to make a infinite image carousel, and everytime it will show an image that has a class of special .special I want to slow down the animation duration or the scrolling of the animation. So users can see the special image longer. Here is my code.
$photoGalleryList.animate({
left : '-' + (computedWidth) + 'px'
},
{
duration : 10000,
easing : 'linear',
step : function(now, fx) {
if(visibleSpecialImage()) {
// SLOW ANIMATION DURATION
// Tried setting fx.options.duration still no effect
}
}
});
I'm not sure if my approach is right (doing it with step), jquery animate() documentation says
step
Type: Function( Number now, Tween tween )
A function to be called for each animated property of each animated element. This function provides an opportunity to modify the Tween object to change the value of the property before it is set.
I'm not sure if I understood the documentation clearly, But base on what I read it's possible using step, I tried googling my problem and never found any concrete answer So now I'm stackoverflowing and hopefully solve this problem. Thanks
I am using JavaScript to dynamically add an element to the DOM. I want to use CSS3 transitions to "fade in" the element as it is added.
I am using something like the following to achieve this:
function add(el) {
el.className += ' fader';
el.style.opacity = 0;
document.getElementById('parent-element').appendChild(el);
//setTimeout(function () { el.style.opacity = 1; }, 5);
el.style.opacity = 1;
}
And the CSS:
.fader {
-webkit-transition: opacity 0.5s;
}
This does not work as expected - the element does not fade in. If I replace the line el.style.opacity = 1; with setTimeout(function () { el.style.opacity = 1; }, 5);, as seen commented-out above, it does work as expected.
I am guessing that the first case does not work as there is some delay between adding the element and the appropriate CSS rules being applied to it. The 5ms delay created by the setTimeout in the second case gives enough time for these rules to be applied, therefore the fade takes place as expected.
Firstly, is this a correct assumption? Secondly, is there a better way to solve this? The setTimout feels like a hack. Is there perhaps some event that is fired once the element has had all its styles applied?
For a CSS3 transition to work, the object has to exist in a particular state and then you have to make a change to the object that triggers the transition.
For a variety of reasons, all of my experience with CSS3 transitions has shown me that a state that counts for this is only a state that it exists in when your javascript returns and the browser goes back to its event loop. It's as if, the only way you can tell the browser to loop at your object now and remember it's state for future transitions is to go back to the browser event loop. There are some programming reasons why this may be the case (so it's not trying to execute transitions as you're programmatically building your object and changing it), but those issues could have been solved a different way (like with a specific method call to codify the object now), but it wasn't done that way.
As such, your solution is the way I've found to do it. Create the object in it's initial state. Set a timer for a very short duration. Return from all your javascript so the object will get codified in its initial state and so the timer can fire. In the timer event, add a class to the object that triggers the CSS3 transition.
I don't honestly know if CSS3 transitions are specified this way in the specification, but my experience in Safari, Firefox and Chrome has been that this is how they work.