Set correct timer for slideshow - javascript

I'm new to this and can't find a way to do it.
I have 4 images but can't set a rule that moves changes correctly that slideshow image.
Example: 1 - 2 - 3 - 4, if I go from 1 and click to go to 3, timer makes me go to 2 because it was in 1.
onload = start;
function start(){
var i = 1;
function Move(){
i = (i%4)+1;
document.getElementById('i'+i).checked = true;
}
setInterval(Move,10000);
}

You need to keep the current element index out of the start function and change it when you select an element manually:
onload = start;
var currIndex = 1;
function start() {
function Move(){
currIndex = (currIndex % 4) + 1;
document.getElementById('i' + currIndex).checked = true;
}
setInterval(Move,10000);
}
// better add a class here for simpler selector
document.querySelectorAll('#i1, #i2, #i3, #i4').addEventListener('whatever-event-you-get-on-selection', function() {
currIndex = yourSelectedId; // use data attributes or something for easier index extraction
});

Related

How do I use JavaScript to change the src of an <img> in a set cycle of multiple images

I need to have an image change through a set series as a button is clicked. How the JavaScript works is that it checks for which the current src matches and then changes it to whatever image is assigned to it. Where is the mistake?
function cycleImage() {
var currentImage = document.getElementById("img-slide").src;
if(currentImage == "/images/image-1.jpg") {
document.getElementById("img-slide").src = "/images/image-2.jpg";
}
else if(currentImage == "/images/image-2.jpg") {
document.getElementById("img-slide").src = "/images/image-3.jpg";
}
else if(currentImage == "/images/image-3.jpg") {
document.getElementById("img-slide").src = "/images/image-4.jpg";
}
else if(currentImage == "/images/image-4.jpg") {
document.getElementById("img-slide").src = "/images/image-1.jpg";
}
}
If you look at currentSource, you'll see that it's a fully-resolved URL (like https://example.com/images/image-1.jpg, not a relative URL (like /images/image-1.jpg), so it won't match any of the strings you're comparing it with. That's because the src property provides a full-resolved URL, not the literal value of the src attribute.
While you could use endsWith to do the comparison instead (or use getAttribute to get the actual attribute value), I wouldn't do it that way. Instead, I'd keep track in a variable:
let imageIndex = 1;
setImage();
function setImage() {
document.getElementById("img-slide").src = `/images/image-${imageIndex}.jpg`;
}
function cycleImage() {
imageIndex = (imageIndex % 4) + 1;
setImage();
}
Live Example (using div elements instead of images):
let imageIndex = 1;
setImage();
function setImage() {
document.getElementById("img-slide").textContent = `/images/image-${imageIndex}.jpg`;
}
function cycleImage() {
imageIndex = (imageIndex % 4) + 1;
setImage();
}
setInterval(cycleImage, 800);
<div id="img-slide"></div>
That imageIndex = (imageIndex % 4) + 1; thing is a useful trick: it increments a 1-based number, wrapping back around to 1 after it's had a given maximum value (in this case, 4). So we get 1 2 3 4 1 2 3 4.... (You don't strictly need the () in it, but it's useful for understanding how it works.)
Or if the image paths weren't easily generated like that, I'd use an array of images paths and loop through it:
const images = [
"/something.jpg",
"/something-else.png",
"/a-different-thing.jpg",
"/yet-another-thing.jpg",
// ...
];
let imageIndex = 0;
setImage();
function setImage() {
document.getElementById("img-slide").src = images[imageIndex];
}
function cycleImage() {
imageIndex = (imageIndex + 1) % images.length;
setImage();
}
Live Example (using div elements instead of images):
const images = [
"/something.jpg",
"/something-else.png",
"/a-different-thing.jpg",
"/yet-another-thing.jpg",
// ...
];
let imageIndex = 0;
setImage();
function setImage() {
document.getElementById("img-slide").textContent = images[imageIndex];
}
function cycleImage() {
imageIndex = (imageIndex + 1) % images.length;
setImage();
}
setInterval(cycleImage, 800);
<div id="img-slide"></div>
That imageIndex = (imageIndex + 1) % images.length is another useful trick similar to the one above. It increments a 0-based value, wrapping it around back to 0 when (before) it reaches a maximum value (in this case, images.length, which is 4). So we get 0 1 2 3 0 1 2 3.... (You do need the ()in this one, because%has a higher precedence than+`.)

How to create animation class library for p5js

I am working on an application where I'd like to provide overlays of different animations onto a range of videos using p5js. I'm looking to organize my classes of animation types so that each animation has a similar structure to update and destroy objects during each loop. My plan is to have an array of animations that are currently "active" update them each iteration of the loop and then destroy them when they are completed. I built a class to fade text in this manner but I'm getting some weird flashy behavior that seems to occur every time a new animation is triggered in the middle of another animation. I've been trying to debug it but have been unsuccessful. Do you have any suggestions as to:
(1) if this is due to my code structure? (and maybe you have a suggestion of a better way),
or
(2) I'm doing something else incorrectly?
Here is the code:
// create an array of currently executing animations to update
// each animation class needs to have one function and one attribute:
// (1) update() -- function to move the objects where ever they need to be moved
// (2) done -- attribute to determine if they should be spliced out of the array
var animations = [];
//////////////////////////////////////////
// Global Variables for Animations //
//////////////////////////////////////////
let start = false;
let count = 0;
function setup(){
let canv = createCanvas(1920, 1080);
canv.id = "myP5canvas";
background(0);
}
function draw(){
background(0);
// Check things to see if we should be adding any animations to the picture
var drawText = random(100);
if (drawText > 98) {
//if (start == false) {
let r = 255;
let g = 204;
let b = 0;
let x = random(width-10);
let y = random(height-10);
animations.push(new TextFader("Wowwwzers!", 100, 'Georgia', r, g, b, x, y, count));
start = true;
count += 1;
}
// Update animations that exist!
for (var i=0; i < animations.length; i++) {
// update the position/attributes of the animation
animations[i].update();
// check if the animation is done and should be removed from the array
if (animations[i].done) {
console.log("SPLICE: " + animations[i].id);
animations.splice(i, 1);
}
}
}
// EXAMPLE ANIMATION
// TEXT FADE
let TextFader = function(words, size, font, red, green, blue, xloc, yloc, id) {
this.id = id;
console.log("create fader: " + this.id);
// translating inputs to variables
this.text = words;
this.size = size;
this.font = font;
// To Do: separating out each of the values until I figure out how to fade separately from the color constructor
this.red = red;
this.green = green;
this.blue = blue;
this.xloc = xloc;
this.yloc = yloc;
// Maybe add customization in the future for fading...
this.fade = 255;
this.fadeTime = 3; // in seconds
this.fadeIncrement = 5;
// Variables to use for destruction
this.createTime = millis();
this.done = false;
}
TextFader.prototype.update = function() {
// Update the fade
// If the fade is below zero don't update and set to be destroyed
this.fade -= this.fadeIncrement;
if (this.fade <= 0) {
this.done = true;
} else {
this.show();
}
}
TextFader.prototype.show = function() {
textFont(this.font);
textSize(this.size);
fill(this.red, this.green, this.blue, this.fade);
text(this.text, this.xloc, this.yloc);
console.log("Drawing: " + this.id + " fade: " + this.fade + " done: " + this.done);
}
Yay, I've got you an answer! It works like expected when you reverse the for loop that loops over the animations.
Because you splice elements of the same array inside the loop, some elements are skipped. For example; animations[0].done = true and gets removed. That means that animations[1] is now in the spot of animations[0] and animations[2] is now in the spot of animations[1].
The i variable is incremented to 1, so on the next loop, you update animations[1] (and skip the animation that is now in animation[0]).
When you reverse the loop, everything before the element you splice stays the same and nothing is skipped.
For example; animations[2].done = true and gets removed. That means that animations[1] is still in the spot of animations[1].
The i variable is decremented to 1, so on the next loop, you update animations[1] and don't skip any elements.
// Update animations that exist!
for (var i = animations.length - 1; i >= 0; i--) {
// update the position/attributes of the animation
animations[i].update();
// check if the animation is done and should be removed from the array
if (animations[i].done) {
//console.log("SPLICE: " + animations[i].id);
animations.splice(i, 1);
}
}

make timeline-slider looping

I am wondering how to loop this auto-slide function? Its for this timeline here: https://bm-translations.de/#sprachrichtungen
I tried with if condition to check if next slide has items, but maybe because I am a beginner, its not working.
function timeLineAutoPlay() {
var current = 0;
setInterval(function() {
current++;
var current_elem = $($('.events a')[current - 1]);
if (current == 11) {
timeLineAutoPlay();
}
$($('.events a')[current]).trigger('click');
if ((current + 1) % 4 == 0) {
$('.next').trigger('click');
}
}, 8000);
}
timeLineAutoPlay();
Slider is frome here: https://codyhouse.co/gem/horizontal-timeline/
2nd problem:
If I click on a date, the next autoslide isnt the date after. Do you know how to adjust this code?
Tried this, but its not working:
timelineComponents['eventsWrapper'].on('click', 'a', function(event){
current = items.length;
}
The modulo operator is useful for wrapping at a specific number and creating a loop:
Edit: Included example for manual slider control.
Edit 2: Fixed typo in function call
// The interval id for the auto-slider if currently running.
var intervalId;
// The current slide index
var current = 0;
// Query document for the DOM nodes
var items = $('.events a');
// Clicks on `a` will bubble up to their `li` element and then to `div.events`.
// We can use this to listen to the click on li and then see which one was
// targeted.
$('.events').on('click', 'li', function(event) {
// If the third `li` was clicked, there are 2 in front of it so 2 is the index
// of the item that was clicked.
var count = $(this).prevAll('li').length;
// Update the current item based on the clicked element.
current = count;
// Reset the interval so the next slide does not occur immediately after the
// user has clicked.
if (intervalId != null) {
// Clear previous auto-play
clearInterval(intervalId);
// And start a new one which will first trigger 8000ms from now.
intervalId = timeLineAutoPlay();
}
});
function timeLineAutoPlay() {
// Return the interval id so that we can can cancel it later.
return setInterval(function() {
// Increment `current` but wrap at `items.length`, thus looping through the
// list of items.
//
// E.g. for 4 items; items.length === 4:
// current : 0 -> 1 -> 2 -> 3 -> 0 -> 1 -> ...
current = (current + 1) % items.length;
// Get the item at the current index
var item = items.eq(current);
// This would lead to multiple interval loops being spawned whenever we
// reach 11. That seems unintentional.
//
// if (current == 11) {
// timeLineAutoPlay();
// }
item.trigger('click');
}, 8000);
}
// Start the initial auto-slide and store the id
intervalId = timeLineAutoPlay();
Something like the following:
setInterval(function(){
var current_elem = $($('.events a')[current]);
if(!current_elem.hasClass("selected")){
$(".events a").each( function(index, elem) {
if($(this).hasClass("selected")){
current = index;
}
});
}
current++;
if(current >= $('.events a').length) {
current = 0;
}
$($('.events a')[current]).trigger('click');
}, 7000);

Increment counter then reset that counter when reached a certain limit

I have 18 images from which I only wish to show 5 at any given time. I'm swapping out a new image every two seconds or so without showing duplicates. My HTML is hardcoded and pulling from an s3 bucket.
<img class="activeImg" src="img_1.jpg />
<img src="img_2.jpg />
<img src="img_3.jpg />
<img src="img_4.jpg />
...
<img src="img_9.jpg />
I have a recursive setTimeout function that is calling moveCounter. moveCounter simply increments by 1, and replaces an image, every time it gets called. I hook into the element by adding the activeImg class to the img, then remove it before moving onto the next image.
The end goal is to count from 0...5, then back down 5...0, infinitely. All I'm doing is appending the value to an image name, thereby replacing images without showing a duplicate.
The problem is there is overlap and I'm getting a duplicate whenever counter === imgId.
// my recursive settimeout function fires off and adds an activeImg class to a random image element, then also fires off the replaceImage function
var counter = 0;
var imgId = 18;
// increment counter and append new img
function replaceImage(imgId){
if (counter >= 9) {
counter--;
var imgId = imgId - counter;
} else {
counter++;
var imgId = imgId - counter;
}
jQuery('.activeImg').attr('src', "image/img_"+imgId+".jpg");
console.debug(counter)
console.debug(imgId)
}
I feel like there is a better way to approach this.
Seems that what you're looking for is a simple triangle wave.
You can generate one rather easily:
var a = 5;
var output = [];
for (var i = 0; i < 20; i++) {
output.push(a - Math.abs(i % (2 * a) - a));
};
out.innerHTML = output;
<span id="out"></span>
var counter = 1;
var imgId = 18;
var op = 1; // the operation -- 1 for addition, -1 for subtraction
function replaceImage(imgId) {
// increment or decrement the counter, depending
// on the current direction
counter += op;
// use the original poster's snippet to set the active image
var imgId0 = imgId + counter;
$('.activeImg').attr('src', 'image/img_'+imgId0+'.jpg');
// if we hit the upper or lower bound, reverse the direction
// by flipping the operation
if (counter === 5 || counter === 1)
op *= -1;
}

changing images automatically with javascript

I have the current JavaScript problem. I have four divisions next to each other on my website that constantly rotate images on a 10 seconds interval. I need these intervals to keep rotating images at the current interval but start 5 seconds apart from each other in order to obtain a nice waterfall effect. How can I accomplish this using JavaScript?
image of how it looks on my websites' header
This is an example of the code I am currently using to display a single division and handle the rotation of the images.
<div class = "TestRotator">
<img src="http://bushveld.info/wp-content/uploads/2013/07/image1.png" alt="rotating" width="100" height="232" id="rotator">
<script type="text/javascript">
(function () {
var rotator = document.getElementById('rotator'); // change to match image ID
var imageDir = 'http://bushveld.info/wp-content/uploads/2013/07/';
var delayInSeconds = 5;
// set number of seconds delay
// list image names
var images = ['image2.png', 'image3.png', 'image4.png'];
var num = 0;
var changeImage = function () {
var len = images.length;
rotator.src = imageDir + images[num++];
if (num == len) {
num = 0;
}
};
setInterval(changeImage, delayInSeconds * 1000);
})();
</script>
</div>
I've fiddled it a lot! (I changed it big time.)
chenged setInterval() with setTimeout() and many others.
Is this what you wanted?
PS: state holds the 1st image to which the imgs change. and the difference in the timeout (200 milliseconds is in order to just to make some difference in between them, yuo can change it to a round number if you want to).
If I've understood your question correctly, you need something like this:
window.onload = function () {
var // Customable parameters
imageDir = 'http://bushveld.info/wp-content/uploads/2013/07/',
interval = 2, // Interval between "flushes" in seconds. Must be > speed * maxScreens
speed = 0.1, // "Flush" speed in seconds
maxScreens = 4, // amount of used image tags
images = 4, // amount of image sources, can be > maxScreens
dir = 1, // 1 = from left to right, -1 = from right to left
// Program
flush,
target = (dir > 0) ? 1 : maxScreens,
targetOrigo = target,
changeImage = function() {
var img = document.getElementById('rotator' + target),
id = parseInt(img.src.substr(img.src.length - 5, 1), 10) - dir;
if (id < 1) {
id = images;
}
if (id > images) {
id = 1;
}
img.src = imageDir + 'image' + id + '.png';
if (target !== maxScreens - targetOrigo + 1) {
target += dir;
setTimeout(changeImage, speed * 1000);
} else {
target = targetOrigo;
setTimeout(runRotation, interval * 1000);
}
return;
},
runRotation = function () {
setTimeout(changeImage, speed * 1000);
};
setTimeout(runRotation, 1000);
}
A live demo at jsFiddle
Notice, that I've put the function at window.onload, looks better when all the images are already loaded, before the rotation starts.
The snippet doesn't use setInterval() at all, instead it's based on nested setTimeout()s. This way you can avoid a mess, which you might get (depends on used browser), if user visits at other tab and then comes back to your site.
You can play with interval, "flush" speed, number of images you have on the rotation and even how many different images you like to use (max. = 9). You can also switch the direction of the rotation.
If you want to avoid two similar images to be shown at the same time, you can add image5.png to your image folder, and set images = 5.
Also version using an image source array available.
Thanx alot for the input. I solved this issue by adapting the code in this manner...
(function() {
var rotator3 = document.getElementById('rotator3'); // change to match image ID
var imageDir = 'http://bushveld.info/wp-content/uploads/2013/07/';
// set number of seconds delay
// list image names
var images = ['image2.png', 'image3.png', 'image4.png', 'image1.png'];
// don't change below this line
var num = 0;
var changeImage = function()
{
var len = images.length;
rotator3.src = imageDir + images[num++];
if (num == len)
{
num = 0;
}
};
function SwImg() {
var rotate = setInterval(changeImage, 20000);
}
setTimeout(SwImg,15000);
})();
This tweak basically creates an initial delay of 5++ seconds at each division with the standard 20 seconds delay interval between switches, rotating each image in each division 5 seconds after the other. Here is a link to the website , will be done end of this week. Thanks again for the input, really awesome and creative ways of solving this issue!
Cheers

Categories