Stop loop on progressbar - javascript

I'm working with this progressbar:
https://codepen.io/thegamehasnoname/pen/JewZrm
The problem I have is it loops and what I want to achieve is:
stop on last progress slide (stop loop).
if the user is on last progressbar slide after it has stopped and I click on a .button-prev button it should start from the previous slide, not the first slide of the progressbar.
here is the code:
// swiper custom progressbar
const progressContainer = document.querySelector('.progress-container');
const progress = Array.from(document.querySelectorAll('.progress'));
const status = document.querySelector('.status');
const playNext = (e) => {
const current = e && e.target;
let next;
if (current) {
const currentIndex = progress.indexOf(current);
if (currentIndex < progress.length) {
next = progress[currentIndex+1];
}
current.classList.remove('active');
current.classList.add('passed');
}
if (!next) {
progress.map((el) => {
el.classList.remove('active');
el.classList.remove('passed');
})
next = progress[0];
}
next.classList.add('active');
}
progress.map(el => el.addEventListener("animationend", playNext, false));
playNext();
I tried adding this:
if (current) {
if (!next) {
$('.progress-container div').addClass('passed');
}
}
But the class passed gets deleted and the progressbar starts again.
This is the js of the previous button I have:
$(document).on('click', ".button-prev", function() {
$('.progress-container div.active').prev().removeClass('passed').addClass('active');
$('.progress-container div.active').next().removeClass('active');
});
any ideas on how to achieve this?

You almost have the fix ! Simply add a return statement to avoid to set the active calss to the first progress element.
if (current) {
const currentIndex = progress.indexOf(current);
if (currentIndex < progress.length) {
next = progress[currentIndex+1];
}
current.classList.remove('active');
current.classList.add('passed');
if (!next) {
$('.progress-container div').addClass('passed');
return;
}
}

Related

Interactive carousel with react

I've been trying to create a component similar to instagram stories for a few hours. That is nothing more than an interactive carousel, where you can move forward and backward. The thing is, my strategy with setTimeOut fires every time I interact and does not cancel the state, so if the user is in the first photo and clicks 5x in a row to go to the sixth photo, in a few seconds the settimeout will be like a tsunami and the component advances 5x stories in a row starting from 6. I wanted somehow to reset my timer every time the user clicks on a new image. Seems crazy or is it possible?
I don't know if there is anyone with enough patience to see the code, if not, if someone at least knows a new approach. Thank you very much, I have been trying to resolve this for some time.
function Stories(){
const files = [ 'image1.jpg', 'image2.jpg' , 'video3.mp4' ]
const videoRef = useRef()
const imageRef = useRef()
const [ index , setIndex ] = useState(0); // the index starts at 0... first file...
const [ focus, setFocus ] = useState(null);
// Every time the index of the storie is changed. This effect happens.
useEffect(() => {
const video = videoRef.current;
const image = imageRef.current;
if( files[index].includes('mp4')){
video.style.display = "inline"
video.src = files[index];
// when i put it i put something in the "setFocus"
// triggers a useEffect for the "focus"
// that makes the setTimeOut which in the end runs
// this same script again after a certain time.
setFocus(files[index]);
// if there is any image in the DOM, hide it
if( image ) {
image.style.display = "none";
}
//In case the files [index] is an image.
} else {
image.style.display = "inline"
image.src = files[index];
setFocus(files[index])
// if there is any video in the DOM, hide it
if ( video ){
video.style.display = 'none';
video.muted = true
}
}
},[index])
function back(){
if( index <= 0 ) return
setIndex( index - 1);
}
function next(){
if ( index < files.length - 1) setIndex( index + 1 );
}
// This useeffect fires every time a file comes into focus.
useEffect(() => {
const timelapse = 5000;
// I wait for the video to finish to advance to the next index
if( files[index].includes('mp4')){
videoRef.current.addEventListener('ended',() => setIndex( index + 1 ));
}
// If it's an image, so..
else {
setTimeout(() => {
if( focus !== null && index < files.length - 1){
setIndex(index + 1);
}
}, timelapse )
}
},[focus])
// HTML
return <>
<video ref={videoRef}/>
<img ref={imageRef} />
<button onClick={back}> back </button>
<button onClick={next}> next </button>
</>
}
You can capture the ID of the timeout and cancel it like this:
const timeout = setTimeout(() => { console.log("woo") }, 5000)
window.clearTimeout(timeout)
So one approach would be to store that and cancel the timeout when your back or next functions are invoked.
Another approach would be to use state to record the remaining time and use a timer (setInterval) that counts down every second and advances the slide if it reaches zero. Then onClick you can just re-set the time remaining to 5 seconds.
const CarouselExample = () => {
const [time, setTime] = useState(5)
const [slide, setSlide] = setState(1)
useEffect(() => {
const countdown = window.setInterval(() => {
setTime(timeRemaining => {
if (timeRemaining - 1 < 0) {
setSlide(prevSlide => prevSlide + 1)
return 5
} else {
return timeRemaining - 1
}
})
}, 1000)
return () => {
window.clearInterval(countdown)
}
}, [])
return (
<div>
<div>Current Slide: {slide}</div>
<button
onClick={() => {
setTime(5)
setSlide(prev => prev + 1)
}}
>
Next
</button>
</div>
)
}

Vanilla JS image slideshow play/pause button

My play/pause button works the first time I press pause and the first time I press play. But after that if I want to pause the slideshow again, it just plays the slideshow at a faster speed and I am no longer able to pause it.
I am thinking maybe it is to do with the fact I have two separate functions: one for the play/pause button icon toggle, and another for the actual play pause behaviour? (update: this has now been fixed, but still doesn't work)
Sorry, I am struggling with javascript, I have a lot to learn.
My script:
const playPause = document.querySelector('.pause');
let slideId;
// FUNCTION TO MOVE TO NEXT SLIDE
const moveToNextSlide = () => {
slides = getSlides();
if (index >= slides.length - 1) return;
index++;
slideGroup.style.transform = `translateX(${-slideWidth * index}px)`;
slideGroup.style.transition = '.8s';
}
// FUNCTION TO START SLIDESHOW
const startSlide = () => {
slideId = setInterval(() => {
moveToNextSlide();
}, interval);
playing = true;
};
// START AUTOMATIC SLIDESHOW UPON ENTERING THE PAGE
startSlide();
//PLAY PAUSE BUTTON - slideshow start/stop
playPause.addEventListener('click', () => {
if(!slideId) {
slideId = startSlide();
console.log('started');
} else {
clearInterval(slideId);
slideId = null;
console.log('stopped');
}
});
//PLAY PAUSE BUTTON - image change
function toggle(button) {
if (button.className != 'pause') {
button.src = 'img/pause.png';
button.className = 'pause';
}
else if (button.className == 'pause') {
button.src = 'img/play.png';
button.className = 'play';
}
return false;
}
HTML:
<input type='image' src='img/pause.png' class='pause' onclick='toggle(this);' />
This is what the console looks like when I try to pause the slideshow for a second time:
There are some details in your code that are missing and would be helpful to have, but I guess that you can get rid of the onclick handler and attach two event listeners:
let slideId;
// FUNCTION TO START SLIDESHOW
const startSlide = () => {
let interval = 2000; // using just as sample
playing = true;
return setInterval(() => {
console.log('moveToNextSlide();') // replaced just to test without missing code
}, interval);
};
//PLAY PAUSE BUTTON - slideshow start/stop
playPause.addEventListener('click', () => {
if(!slideId) {
slideId = startSlide();
console.log('started');
} else {
clearInterval(slideId);
slideId = null;
console.log('stopped');
}
});
//PLAY PAUSE BUTTON - image change
playPause.addEventListener('click', function toggle() { // the arrow function would not work in this case
var button = this;
if (button.className != 'pause') {
button.src = 'img/pause.png';
button.className = 'pause';
}
else if (button.className == 'pause') {
button.src = 'img/play.png';
button.className = 'play';
}
return false;
});

How can I change slides with scroll event infinitely. Have it working with set interval function, but how to scroll function?

I have this https://codepen.io/mikun/pen/YWgqEX and. Instead of it infinity changing slides, I want it to wait for the scroll event.
I have added this to it but it isn't working the right way.
$('.slide-nav').on('click', function(e) {
e.preventDefault();
// get current slide
var current = $('.flex--active').data('slide'),
// get button data-slide
next = $(this).data('slide');
$('.slide-nav').removeClass('active');
$(this).addClass('active');
if (current === next) {
return false;
} else {
$('.slider__wrapper').find('.flex__container[data-slide=' + next +
']').addClass('flex--preStart');
$('.flex--active').addClass('animate--end');
setTimeout(function() {
$('.flex--preStart').removeClass('animate--start flex--
preStart').addClass('flex--active');
$('.animate--end').addClass('animate--
start').removeClass('animate-
-end flex--active');
}, 800);
}
});
window.onscroll = function Onscrollfunction() {
if (current === next) {
return false;
} else {
$('.slider__wrapper').find('.flex__container[data-slide=' + next
+ ']').addClass('flex--preStart');
$('.flex--active').addClass('animate--end');
}
}

how to get slide indicator to match current slide?

I have made this vanilla js image slider with three images. In the html i have three indicator dots at the bottom of the slider and a css active class for the active indicator. Can't figure out how to get the class to add to the current slide of the slide show, any help?
let sliderImages = document.querySelectorAll('.slides'),
prevArrow = document.querySelector('#prevBtn'),
nextArrow = document.querySelector('#nextBtn'),
dots = document.querySelectorAll('.indicator__dot'),
current = 0;
reset = () => {
for(let i = 0; i <sliderImages.length; i++) {
sliderImages[i].style.display = 'none';
}
}
startSlide = () => {
reset();
sliderImages[0].style.display = 'block'
}
prevSlide = () => {
reset();
sliderImages[current - 1].style.display = 'block';
current -- ;
}
nextSlide = () => {
reset();
sliderImages[current + 1].style.display = 'block';
current++;
}
prevArrow.addEventListener('click', () => {
if(current === 0 ) {
current = sliderImages.length;
}
prevSlide();
});
nextArrow.addEventListener('click', () => {
if(current === sliderImages.length - 1 ) {
current = -1
}
nextSlide();
});
startSlide()
No sorry I should have been more clear and included some html. I have 3 dots at the bottom of the slider. When slide one is the current image the first dot will have the css class added to it, when the second image is showing the second do will have the active class and the first dot wont any more. Hope that makes more sense?
<div class="indicator">
<span class="indicator__dot">&nbsp</span>
<span class="indicator__dot">&nbsp</span>
<span class="indicator__dot">&nbsp</span>
</div>
Haven't tested this so may have some bugs. setAnchor() function resets all the dots and add active class to the current dot.
setAnchor = () => {
for(let i = 0; i <dots.length; i++) {
sliderImages[i].classList.remove("active");
if(current === i){
sliderImages[i].classList.add("active");
}
}
}
startSlide = () => {
reset();
sliderImages[current].style.display = 'block'
setAnchor();
}
prevSlide = () => {
reset();
current -- ;
sliderImages[current].style.display = 'block';
setAnchor();
}
nextSlide = () => {
reset();
current++;
sliderImages[current].style.display = 'block';
setAnchor();
}

jQuery fade in/out between divs

I have this code:
var next = null;
var outer = jQuery('.banner .container');
var current = outer.find('.banner-word:first');
current.fadeIn();
function fade() {
if (current.next('div.banner-word').length > 0) {
next = current.next('div.banner-word');
} else {
next = outer.find('.banner-word:first');
}
current.fadeOut();
next.fadeIn();
current = next;
setTimeout(fade, 11000);
}
// start the process
fade();
A few problems with it - 1) It seems to ignore the first banner-word div 2) On load it shows quickly shows the first 2 banner-word divs and then starts with the second banner-word div
Am I missing something obvious?
Try changing:
if (current.next('div.banner-word').length > 0) {
next = current.next('div.banner-word');
} else {
next = outer.find('.banner-word:first');
}
to:
if (current.next().is('div.banner-word')) {
next = current.next();
} else {
next = outer.find('.banner-word:first');
}
Edit:
try adding a delay to the initial fade() call.
Change
fade();
to
setTimeout(fade, 5000);

Categories