Image gallery with forward and backward buttons to rotate through image list using JavaScript

Can anybody shine a bit of light on my darkest hour :slight_smile:

I’m remaking my web site in Hype and want to add an image gallery. I know how to achieve what I want using scenes and timelines, but I set myself the task of achieving it with JavaScript.

I want to take an existing image in the gallery scene and replace it with another image simply by clicking a button to go backwards or forwards through an amount of images in the resource folder.

I couldn’t believe my luck when I found an article, which describes exactly what I want to do.

This is the link to the article I found by a chap called Martin Webb:

http://www.irt.org/articles/js132/#2

I chose the latter option.

However, being a novice to JavaScript I am clearly missing something vital as I can’t get it to work in Hype. (In the article above there is a demonstration at the end clearly showing that it does work).

I’ve given the image in the scene a unique id of “myimage” as per the script.

Apart from changing new Object to new Array, I’m at a loss as to what I have done wrong.

If anyone could point me in the right direction I would be extremely grateful.

I will upload the Hype file and script – just in case.

ImageGalleryWithJS.zip (2.1 MB)

This is the script I’ve added my script to the Resources Folder:

${resourcesFolderName}

which_image_loaded = 0;

NUMBER_OF_IMAGES = 20;

ImageNames = new Array();
ImageNames.length = NUMBER_OF_IMAGES - 1;

for (counter = 0; counter < NUMBER_OF_IMAGES; counter++){
    file_number = counter + 1;
    filename = ("image" + file_number + ".jpg");
    ImageNames[counter] = filename;
}



function changeImage(direction) {
    which_image_loaded += direction;
    if (which_image_loaded < 0)
        which_image_loaded = NUMBER_OF_IMAGES - 1;
    if (which_image_loaded == NUMBER_OF_IMAGES)
        which_image_loaded = 0;
    if (document.images)
    	document.myimage).src = ImageNames[which_image_loaded];
}

And I’ve set the relevant functions associated with the corresponding arrows used as buttons

 On Mouse Click (Tap)
    Run JavaScript
    Function NextImage

changeImage(1);


On Mouse Click (Tap)
Run JavaScript
Function PreviousImage

changeImage(-1);

Your Javascript has several errors and a few things that don't really make sense.

I haven't really looked at the link you supplied for the article.

Where did you arrive at the code that you have written?

First of all:

${resourcesFolderName}

should not be in your script file. Also the script can be added inside of the document so no need for an external file.

There is an extra ")" that is not needed here after document.myimage

...

I'll come back to this with perhaps a better solution to your problem

Thanks for your reply and offer of assistance.
I can now see that copy and paste from the original was not a clever idea.
I appreciate you taking the time to look into this.
Keith

Hi @zebedee

Here is a function to call on scene load. I have commented the code to help explain.

Essentially, you create 3 elements. A rectangle and 2 arrows. Those elements you give Unique ID’s.

Then you create variables assigned to these elements. You can then use these throughout the code.
In my code I have created an <img> tag on the fly using javascript. You can alternatively add this code inside your rectangle element.

You then create an array of filenames corresponding to your images in your resources folder.

Then you add event listeners for the arrows to listen for a user click. The code inside this event listener is fired when the user clicks.

Here is the code:

	
	var picture_frame = document.getElementById("picture_frame");
	var img = document.createElement("IMG");
	picture_frame.appendChild(img);

	img.width = "400";  // images dimensions
	img.height = "267"; //

        img.src = "${resourcesFolderName}/Pasted.jpg"; // the first image to show on load
	
	var counter = 0;
	var images = ["Pasted","Pasted-1","Pasted-2"]; // array of image names in resource folder
	var arrow_right = document.getElementById("arrow_right"); // right button
	var arrow_left = document.getElementById("arrow_left"); // left button
	
	arrow_right.addEventListener("click", function () { // listening for user click on right arrow
		counter += 1; // when clicked counter variable increases by 1
		if (counter >= images.length) { // checks to see if counter variable is greater than the array of filenames
			counter = images.length-1; // if it is greater or equal to it then we set the counter to this value
		} else {
			img.src = "${resourcesFolderName}/" + images[counter] + ".jpg"; // if it isn't greater than it then we show the image corresponding to the array number
		}
		
	});
	
	arrow_left.addEventListener("click", function () { // listening for user click on left arrow
		counter -= 1;
		
		if (counter < 0) { // checks to see if counter variable is less than 0
			counter = 0; // if it is then set the counter to 0
		} else {
			img.src = "${resourcesFolderName}/" + images[counter] + ".jpg"; // if it isn't then show the corresponding image
		}
	});
	

Here is the document: simpleImageGallery.zip (387.4 KB)

2 Likes

Hi DBear,

While I wasn’t expecting you to do the complete thing more me ‘just like that’, I am extremely grateful that you did.
You have made such a simple and elegant solution that works perfectly.
Thank you very much indeed for your time and effort.
Keith

Hi DBear,

Just wanted to say thanks again for helping me over that rather large hurdle.

Keith

1 Like

Here is an updated version of @DBear 's excellent Simple Gallery.

Instead of creating an <img> tag , we create <div> Tags on the fly to hold images as their background image.
We insert the divs into a Group named as ‘ImageGroup’

The images are in the same array but we use a for loop to iterate over them to create and add the divs.

Now we can then move the ‘ImageGroup’ from left to right using the getter/setter API, this will in turn move the newly created divs that it holds. Making the background images slide across.
The ‘ImageGroup’ is inside another group named Frame. The Frame’s overflow is set to hidden.

We use the known width of the ‘ImageGroup’ 400px at the start as a base number to determine the first slide length.
We then use that number to calculate all left and right slides.

	var images = ["Pasted","Pasted-1","Pasted-2","Screen-Shot","flame2"]; // array of image names in resource folder
	 
	 //-- Get the picture frame details to use later in the code.
	 
	var imageGroup = hypeDocument.getElementById("imageGroup");
	var slideDistence = hypeDocument.getElementProperty(imageGroup, 'width');
	var imageGroupHeight  = hypeDocument.getElementProperty(imageGroup, 'height');
	
	
	
	var imageNumber = hypeDocument.getElementById("imageNumber");
	
	var startPosition =0;  //-- style position of first div
	 
	 //-- create div and set its attributes
for (i = 0; i < images.length; i++) { 
 	
 	var picture_frame = document.createElement("DIV");
	 	picture_frame.style.position= 'absolute';
		picture_frame.style.width = `${slideDistence}px`; //-we use the width of the imageGroup here for convenience .
		picture_frame.style.height = `${imageGroupHeight}px`; //-we use the height of the imageGroup here for convenience .
		picture_frame.style.left = `${startPosition}px`;
	 
		picture_frame.style.backgroundImage =  "url(${resourcesFolderName}/" + images[i]+ ".jpg)";
		picture_frame.style.backgroundSize = "contain"; 
		picture_frame.style.backgroundRepeat=  "no-repeat"; 
		picture_frame.style.backgroundPosition = "center";
		 
		
		 //-- add div to the group.
 		imageGroup.appendChild(picture_frame);
 		
 		
 startPosition +=  slideDistence // increase the position ready for the next div  (in this case  by 400  which is the width of the imageGroup)
 imageGroup.style.width = `${startPosition}px`; //-- increase the size of the holding group.
}
	

	var counter = 0;
	var imageCounter = 1;

	var arrow_right = document.getElementById("arrow_right"); // right button
	var arrow_left = document.getElementById("arrow_left"); // left button
	imageNumber.innerText = `1 of ${images.length}`;

	
	
	
	
	arrow_right.addEventListener("click", function () { // listening for user click on right arrow
	var imageGroupLeft = hypeDocument.getElementProperty(imageGroup, 'left');
	 
		counter += 1; // when clicked counter variable increases by 1
		if (counter >= images.length) { // checks to see if counter variable is greater than the array of filenames
			counter = images.length-1; // if it is greater or equal to it then we set the counter to this value
		} else {
		imageCounter++;
			 		hypeDocument.setElementProperty(imageGroup, 'left',imageGroupLeft - slideDistence, 1.0, 'easeinout') //-- slide the group
			 		
			 		imageNumber.innerText = `${imageCounter} of ${images.length}`;	
		}
		
	});
	
	arrow_left.addEventListener("click", function () { // listening for user click on left arrow
	var imageGroupLeft = hypeDocument.getElementProperty(imageGroup, 'left');
 
		counter -= 1;
		
		if (counter < 0) { // checks to see if counter variable is less than 0
			counter = 0; // if it is then set the counter to 0
		} else {
			 imageCounter--;
			 
			hypeDocument.setElementProperty(imageGroup, 'left', imageGroupLeft + slideDistence, 1.0, 'easeinout')  //-- slide the group
			imageNumber.innerText = `${imageCounter} of ${images.length}`;
		}
	});

There is also code to centre and size the images to the divs.

imageGallerySlide.v2.hypetemplate.zip (591.6 KB)

1 Like

Wow, that’s brilliant too.
I’ve already put DBear’s idea into practice – still getting my head around how that works.
I’m still at novice stage with js but looking to improve so I will revisit this when I’ve gained a bit more understanding and experience with it.
Thank you so much for your help.
Keith

1 Like

Here is a new version

This has a slideshow function as well as the mouse click arrows.

I tried to not repeat too much code to get the slide show working so it actually clicks through using the javascript click() function on the Arrows but there is some slight code changes and a new function. I have tried to keep it as basic as possible, and used Organic Reaction Coding
ORC. for short, which means I probably made it more complex than it needs to be while putting out fires. :stuck_out_tongue_winking_eye:

imageGallerySlide.v4.hypetemplate.zip (594.7 KB)

Hi Guys,

I have adapted the slider and used here:

http://www.toolfolks.com/dewalt/toughsystem.html

However I have noticed that if I click on the left or right arrows quickly the slider goes out of sync.

Is there a way to hide the arrows until the motion is finished.

Cheers

Steve Warby

if you used my version (latest one)

Then add something like this to the end of the changePicture() function.

	function arrowControls(){
	
	 arrow_right.style.display = 'none';
 arrow_left.style.display = 'none';
	 
	
	setTimeout(function(){ 
	
 arrow_right.style.display = 'block';
 arrow_left.style.display = 'block';
	
	}, 1050);
			 

		}

And call it in both arrow addEvents as the first line in the else clause.
i.e

window.counter = 0; // if it is then set the window.counter to 0
....		
} else {
		
		
		arrowControls();
		
		 window.imageCounter--;
....

I am a product designer helping out a violinist with a web site so I am truly a fish out of water on all of this, I have set up his site in hype with multiple breakpoints and it is also in two languages. Concert dates have posters and someone thought wouldn`t it be nice if a slide show augmented touch screen function for those who view on standard monitors. Your simpleimageGallery.v2 (sans automatic slide show, clicking on arrows is preferable) is an elegant solution and so I incorporated it into one of the pages: desktop breakpoint (+1200 pixels). My problem: I would like to also include it in some of the other breakpoints and on the parallel pages in the 2nd language (plus future years) but as best I can tell, cannot because of the necessity of unique ids. Even after a considerable amount of time playing with naming and trying to get things to correspond in the function code, I can only get the initial page/desktop breakpoint to function.

http://ciprian-marinescu-violin.com/#2016

I will deeply appreciate your advice and my apologies for taking advantage of your knowledge.

Sincerely,
Thomas Perry

If you need to include the exact same content (as in same sizes and everything) on multiple layouts, you could include it in a persistent symbol. Persistent symbols have only one running instance that is shared across scenes, and thus can use IDs.

If you plan to make changes but want the script to be roughly the same, you would need to modify the code to use class names instead. The calls would change from getElementById to getElementsByClassName and would also need to be scoped into just the specific scene/layout so you don’t get multiple elements in the return call. (This is a bit of code to change so it is an exercise left to the reader or author!)

@JTP

Is there a reason why You couldn’t use a Timeline based slide show? I’m not seeing anything in your description (or on your demo page) that would preclude this approach.

It could be very portable - anywhere You wished to place it - no ids, classes, etc. needed. It would be “static” in the sense that You will load the images You want to use in to the Hype project itself. If You are not updating the slide show frequently this might fill your needs in a simple fashion. No coding required.

thank you for your reply Jonathan - I shall investigate and let you know how it turns out - Thomas Perry

thank you for your reply Jim - I shall investigate and compare to the idea of a persistent symbol as suggested by Jonathan Deutsch and I will let you know how it turns out - Thomas Perry

This was very helpfull. But, is it possible to code in a way that the aspect ratio of the pictures are respected? Thank you very mush!