For a website I made a JS, to animate a div upon scroll. I was animating the CSS top value, but since I had a transition applied to that property, I had to come up with a way of animating "top" without the lag that CSS transitions results in.
I came up with this inside a function:
//Get the Div Element
var div = document.querySelector('.someclass');
//Disable Transition
div.style.transition= 'none';
//Change the top value - value was connected to scroll
div.style.top = anumber + 'px';
//Reset Transition to the CSS's file default
div.style.transition = '';
But this resulted in the unwanted lag again, because it somehow ignored the "Disable Transition" step.
To make sure that each step would be executed, I came up with the Idea to wrap the "Reset" step into a setTimeout function. I changed this line:
//Reset Transition to the CSS's file default
div.style.transition = '';
to
//Reset Transition to the CSS's file default
setTimeout(function () {
div.style.transition = '';
},1);
And Tadaa, it worked. But now I'm wondering, if there was a cleaner way to prevent the first line of not being executed, and of course an explenation why it even happened.
I appreciate all the help!
Changes to the DOM only get reflected to the underlying model when the CSS engine runs, which only happens if JS stopped running. By using setTimeout the execution of JS ends, the CSS engine has time to run, then the timer fires and JS runs again.
You could solve ut a bit more elegantly with:
const tick = () => new Promise(resolve => setTimeout(resolve));
(async function() {
//Get the Div Element
var div = document.querySelector('.someclass');
//Disable Transition
div.style.transition= 'none';
//Change the top value - value was connected to scroll
div.style.top = anumber + 'px';
await tick();
//Reset Transition to the CSS's file default
div.style.transition = '';
})();
Related
I built a basic picture carousel a while back, and I'm finally getting around to transferring it from MooTools over to jQuery so I can drop MooTools. I've got the script completely functional, but for whatever reason when the carousel slides in one direction, you can see a "pop" where it resets itself.
I've tried playing around with the order it handles everything, but no matter what it seems to always desync for just a fraction of a section.
Here's a copy of my code: https://jsfiddle.net/Chaosxmk/pf6dzchm/
The offending section of code is this:
styles['position'] = 'absolute';
styles[self.params.axis] = -32768;
$(self.list[0]).css(styles).hide();
$(self.list[0]).appendTo(self.carousel);
$(self.list[conf.mi]).css(self.params.axis, (100-conf.pr)+'%');
styles = {};
styles['position'] = 'relative';
styles[self.params.axis] = 'auto';
$(self.list[conf.mi]).css(styles);
Issue is that $.fadeOut() sets display:none on the element, which causes some strange rendering issues in your setTimeout() callback. Works better if you use $.fadeTo() instead:
if (self.params.direction) {
// Go forward
self.carousel.css(self.params.axis, '-'+conf.pr+'%');
$(self.list[0]).fadeTo(400, 0);
$(self.list[conf.mi]).css(self.params.axis, '100%').fadeTo(400, 1);
} else {
// Go backward
self.carousel.css(self.params.axis, conf.pr+'%');
$(self.list[conf.mi-1]).fadeTo(400, 0);
self.list.last().css(self.params.axis, '-'+conf.pr+'%').fadeTo(400, 1);
}
For simplicity I used a 400ms duration, but you can set this to whatever you need.
JSFiddle
So I made an animation where a div called "slider" appears from the left side of the screen, expands to the right and then disappears on the right side of the screen. What I have done is start with width: 0; then transform to the full width, change position to right and have the width go back to 0 and change display to none. The animation works fine, but probably with excessive use of the setTimeout. Is there a better way to write this piece of JavaScript and have one animation start when the previous has finished without the use of setTimout?
window.addEventListener("DOMContentLoaded", function(){
slider.style.width = "60%";
slider.style.position = "absolute";
setTimeout(function(){
slider.style.right = "25%";
slider.style.width = "0px";}, 750);
setTimeout(function(){
slider.style.display = "none";}, 1500);
});
I am dynamically creating divs that have a height of 0% which is specified in the css rule.
I then have an option/select menu that has an event listener listening for a change. When this change is applied, I want it to apply a height of 75%, which works fine.
Though what I also want it to do is to transition to this new height with a duration of 2 seconds. This transition is specified in the css rule for the element.
I cannot make this work however, it goes straight to the full 75% height, no transition.
I understand that I need to make the code 'live' in the DOM, so I have tried separating the appending and the application of height style in two different functions.
function makeBar() {
container.innerHTML = '';
let element = document.createElement('div');
element.classList.add('bar');
element.setAttribute('class', 'bar');
element.style.backgroundColor = select.value;
container.appendChild(element);
}
select.addEventListener('change', function() {
makeBar();
})
select.addEventListener('change', function() {
let bar = document.getElementsByClassName('bar')[0];
bar.style.height = '75%';
})
It works fine however if I were to replace my second function which applies the height with an event listener that listens out for a click on a button. Please see https://jsfiddle.net/tdmitchell/3eu48zw7/30/ to see what I mean.
Though what I actually want to happen is for the bar to smoothly transition from 0% to its new height of 75% when the dropdown menu is changed.
All the 'results' of the browser's render process are occurring at the same time - all at once. You must tell the browser to wait until the next rendering cycle before 'growing' the element.
window.requestAnimationFrame(() => {
bar.style.height = '75%';
});
Will accomplish this.
So I'm trying to add a progress bar to the "Superslides" slider. I've used setInverval to trigger this when a slide finishes animating and for the most part it works.
I also added a little bit that resets the interval if someone clicks one of the links on the slider (next slide, prev, or pagination) and that also works however the slider has touch support using hammer.js and when I try to do the same thing after a swipe event it doesn't seem to reset properly. It resets the progess bar's width back to 0 but the interval continues despite trying to clear it.
I'm probably doing something fairly stupid but I've been scratching my head for a while so I thought I'd ask what I'm doing wrong.
$(document).on('animated.slides', function() {
var progressBar = $('#progress-bar');
width = 0;
progressBar.width(width);
var interval = setInterval(function() {
width += 1;
progressBar.css('width', width + '%');
if (width >= 100) {
clearInterval(interval);
}
//Trying to reset on swipe (I've also tried putting this below outside of interval
Hammer($slides[0]).on("swipeleft swiperight", function(e) {
width = 0;
clearInterval(interval);
});
}, 160);
//reset on anchor click
$("#slides a").click(function(){
clearInterval(interval);
});
});
I believe I solved the issue myself. The slider's implementation uses $slides as a variable for "#slider" and then uses that to trigger the swipe animations. I copied and pasted this into my interval function as is. When I used the id rather than the variable it seems to have worked.
I am trying to create a very simple slideshow.
I would like the slideshow to pause on hover and resume when the user moves their mouse off the slideshow (#slide_container). While the slideshow works fine, and so does the hover (to an extent), if I flick my mouse on and off the slideshow repeatedly, it completely messes the slide and starts animating sporadically (check the fiddle below to see what I mean).
I tried adding promise, so that before animating it completes any queued animation, but despite this, the behaviour remains.
How should I go about fixing this?
Here is the fiddle: http://jsfiddle.net/hR6wZ/
my js code:
$(document).ready(function() {
//get variables
var slide_width = $('.slider_container').width();
var number_of_slides = $('.slider_container .slide').length;
var slider_width = slide_width*number_of_slides;
//set element dimensions
$('.slide').width(slide_width);
$('.slider').width(slider_width);
var i = 0;
var hover_switch = 0;
$('.slider_container').hover(function() {
//Hover mode on
hover_switch = 1;
}, function() {
//Hover mode off
hover_switch = 0;
sliderLoop()
});
function animateSlider() {
$(":animated").promise().done(function() {
$('.slider').finish().animate({ marginLeft: -(slide_width * i) });
});
}
function sliderLoop() {
setTimeout(function(){
//Only runs if hover mode is off;
if(i < number_of_slides-1 && !hover_switch) {
i++;
animateSlider();
sliderLoop();
}
else if (!hover_switch)
{
i = 0;
animateSlider();
sliderLoop()
}
},4000);
}
sliderLoop();
});
EDIT: also I did try using stop instead of finish() but this didn't fix the issue..
Becuase your calling the slider loop each time you hover out everytime even if you hover back and forth like ten times it tells it and queus it up to animate the x amount of times you did that.
What you can try is Only have the slider loop check right then and their when it's about to animate if it is being hovered or not and don't call the slider loop from the every time that it hovers out.
Another way is right before it begins animating set a variable for the duration of the animation to tell it not to call the animateSlider function if Currently_animating
so add && !currently_animating to each if statement within the sliderloop function.
That method however will help but probably the first way would be better as then it will never animate more then every x miliseconds or however long you set it to be.
But in General you probably should not call the sliderloop function if hover_switch = 0 becuase then it will just queue up the slider loop each time you hover out.