Advertisement
Creative Coding

Image Gallery With Custom Sized Images (Bonus jQuery Plugin)

by

As I promised in my previous article, here is the tutorial on creating a gallery out of the custom sized images. As a bonus you'll learn how to create a basic jQuery plugin to present the larger sized image of the thumbnail in a more appealing way.

If you did not read my previous article about custom image sizes, please read it so it will be easier to understand this tutorial.


Step 1 Decide on Image Sizes

Depending on your theme's layout, and what grid system it uses, you can decide on different image sizes for thumbnails. Before deciding think about how many images you want to have in a row, what is the width of the container where the thumbnails will be displayed, margins, paddings, etc.

I will use a 940 pixel wide, 2 column layout (8/4 ratio) as an example, where the content will be 620 pixels and the sidebar 300 pixels with a 20 pixel left margin. The content area and the sidebar area will have inner padding of 20 pixels. Now a little math: content is 620px wide with padding of 20px, leaving 580px for thumbnails; and displaying 5 thumbnails per line, each having a right margin of 10px so they are not stuck together; 5th image in each line will not have a right margin; thumbnails will be 108px in width and height, and cropped.

The larger sized image will be maximum of 660px wide and 660px tall, they will be resized proportionally without cropping.

You can choose whatever sizes fit your layout, if you decide to change sizes they can be easily regenerated (see previous post on how to do it), and they don't have to be square shaped.


Step 2 Setting Up the Custom Sizes

Edit the functions.php file so it looks something like this:

add_action( 'after_setup_theme', 'setup' );
function setup() {
	// ...

	add_theme_support( 'post-thumbnails' );
	add_image_size( 'preview', 108, 108, true ); // thumbnail
	add_image_size( 'zoomed', 660, 600 ); // large

	// ...
}

Step 3 Generate Thumbnail List, Excluding the Image Set as Featured

Still in functions.php add the method generate_thumbnail_list:

function generate_thumbnail_list( $post_id = null ) {
	if ( $post_id == null ) return;
	$images = get_posts(
		array(
			'numberposts' => -1,
			'post_type' => 'attachment',
			'post_mime_type' => 'image/jpeg, image/jpg, image/png, image/gif',
			'post_parent' => $post_id,
			'orderby' => 'menu_order',
			'order' => 'ASC',
			'exclude' => get_post_thumbnail_id( $post_id )
		)
	);
	if ( count( $images ) > 0 ) {
		echo '<ul class="gallery">';
		foreach ( $images as $image ) {
			$src = wp_get_attachment_image_src($image->ID, 'zoomed');
			echo '<li><a href="' . $src[0] .'">' . wp_get_attachment_image( $image->ID, 'preview' ) . '</a></li>';
		}
		echo '</ul>';
		echo '<div class="clear"></div>';
	}
}

In content-single.php call the generate_thumbnail_list method, passing the post ID as a parameter.

<?php the_content(); ?>
<h3>Images</h3>
<?php generate_thumbnail_list( get_the_ID() ); ?>

The above will output an unordered list, containing links to the larger files and the thumbnail images.


Step 4 Styling the Gallery

Obviously the thumbnails need to be styled, otherwise it is just a plain list of images. Add the following to your existing stylesheet or create a new one and enqueue it:

.clear {
	clear: both;
}
.gallery {
	list-style-type: none;
	padding: 0;
	margin: 0;
}
.gallery li {
	float: left;
	margin: 0 10px 10px 0;
}
.gallery li:nth-child(5n) {
	margin-right: 0;
}
.gallery a {
	float: left;
	cursor: pointer;
	text-decoration: none;
}
.gallery img {
	float: left;
	border: 0;
}

This will put images next to each other, leaving some space around them.

At this point clicking on the thumbnail will open the large image on a blank page. This is a good fallback in case the JavaScript is disabled.


Step 5 Open the Images With a jQuery Image Gallery

Style Zoomed Image Gallery

Before writing any JavaScript it would be desirable to know how the large image will be displayed. Here is what I had in mind:

Note: all this will be generated from the jQuery plugin. This is just to show the process of creation.

A transparent overlay on top of all the content, with the image in the middle and the controls on the top right corner. While the image is loading there will be a spinner showing. In the root of the document body a wrapper div will be added, which will contain the links for navigation to the next and previous, a link to close the gallery, and the wrapper around the image where the image will be loaded. This is the minimal HTML code that will be used for the gallery.

<div id="zoom">
	<a href="#next" class="next">Next</a>
	<a href="#previous" class="previous">Previous</a>
	<div class="close"></div>
	<div class="content"></div>
</div>

Adding the following style will style the above elements as in the image above (comments are included to explain parts that may not be immediately obvious):

#zoom {
	z-index: 99990; /* high index so it stays on top of all other elements */
	position: fixed; /* is fixed so if content is scrolled this stays in the same place */
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	/* creates a transparent background, so the content under it will be visible,
	transparency can be adjusted */
	background: rgba(0, 0, 0, 0.8);
}
#zoom .content {
	z-index: 99991; /* higher index so the image will stay on top of the overlay */
	position: absolute;
	/* start initial positioning: will be centered horizontally and vertically */
	top: 50%;
	left: 50%;
	width: 200px;
	height: 200px;
	margin: -100px 0 0 -100px;
	/* end positioning */
	/* an animated spinner as background will be visible while the image is loading */
	background: #ffffff url('../img/spinner.gif') no-repeat 50% 50%;
	border: 20px solid #ececec;
	padding: 0;
}
#zoom img {
	display: block;
	max-width: none;
	background: #ececec;
	-moz-box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
	-webkit-box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
	box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4);
}
#zoom .close {
	z-index: 99993; /* higher index so the close will stay above the overlay and image */
	position: absolute;
	top: 0;
	right: 0;
	width: 49px;
	height: 49px;
	cursor: pointer;
	background: transparent url('../img/icons/close.png') no-repeat 50% 50%;
	opacity: 1;
	filter: alpha(opacity=100);
}
#zoom .previous,
#zoom .next {
	z-index: 99992; /* higher index so the close will stay above the overlay and image */
	position: absolute;
	top: 0;
	overflow: hidden;
	display: block;
	width: 49px;
	height: 49px;
	text-indent: 100%;
}
#zoom .previous {
	right: 100px;
	background: url('../img/icons/arrows.png') no-repeat 0 0;
}
#zoom .next {
	right: 50px;
	background: url('../img/icons/arrows.png') no-repeat 100% 0;
}
#zoom .close:hover {
	background-color: #da4f49; /* adds a red tint on hover */
}
#zoom .previous:hover,
#zoom .next:hover {
	background-color: #0088cc; /* adds a blue tint on hover */
}

The outcome of the above:

Now Some JavaScript

The HTML code above will not be needed, it will be generated with JavaScript. Events will be attached for opening, navigating and closing the gallery. Navigating and closing the gallery can be done from the keyboard or using the mouse.

The JavaScript below is also commented to explain what's going on:

(function($) {
	$.zoom = function() {
		 // append a gallery wrapper to the document body
		$('body').append('<div id="zoom"></div>');
		var zoomedIn = false,  // a flag to know if the gallery is open or not
		    zoom = $('#zoom'),
		    zoomContent = null,
		    opened = null; // the opened image element

		function setup() {
			zoom.hide(); // hide it
			// add the inner elements, image wrapper, close and navigation links
			zoom.prepend('<div class="content"></div>');
			zoom.prepend('<div class="close"></div>');
			zoom.prepend('<a href="#previous" class="previous">Previous</a>');
			zoom.prepend('<a href="#next" class="next">Next</a>');
			
			zoomContent = $('#zoom .content');
			// attach events to the added elements
			$('#zoom .close').on('click', close);
			$('#zoom .previous').on('click', openPrevious);
			$('#zoom .next').on('click', openNext);
			
			// observe keyboard events for navigation and closing the gallery
			$(document).keydown(function(event) {
				if (!opened) {
					return;
				}
				if (event.which == 27) {
					$('#zoom .close').click();
					return;
				}
				if (event.which == 37) {
					$('#zoom .previous').click();
					return;
				}
				if (event.which == 39) {
					$('#zoom .next').click();
					return;
				}
				return;
			});
			
			if ($('.gallery li a').length == 1) {
				// add 'zoom' class for single image so the navigation links will hide
				$('.gallery li a')[0].addClass('zoom');
			}
			// attach click event observer to open the image
			$('.zoom, .gallery li a').on('click', open);
		}
		
		function open(event) {
			event.preventDefault(); // prevent opening a blank page with the image
			var link = $(this),
			    src = link.attr('href'),
				// create an image object with the source from the link
			    image = $(new Image()).attr('src', src).hide();
			if (!src) {
				return;
			}
			$('#zoom .previous, #zoom .next').show();
			if (link.hasClass('zoom')) {
				$('#zoom .previous, #zoom .next').hide();
			}
			
			// show the gallery with loading spinner, navigation and close buttons
			if (!zoomedIn) {
				zoomedIn = true;
				zoom.show();
			}
			
			// clean up and add image object for loading
			zoomContent.empty().prepend(image);
			// event observer for image loading, render() will be
			// called while image is loading
			image.load(render);
			opened = link;
		}
		
		function openPrevious(event) {
			event.preventDefault();
			if (opened.hasClass('zoom')) {
				return;
			}
			var prev = opened.parent('li').prev();
			if (prev.length == 0) {
				prev = $('.gallery li:last-child');
			}
			prev.children('a').trigger('click');
		}
		
		function openNext(event) {
			event.preventDefault();
			if (opened.hasClass('zoom')) {
				return;
			}
			var next = opened.parent('li').next();
			if (next.length == 0) {
				next = $('.gallery li:first-child');
			}
			next.children('a').trigger('click');
		}
		
		function render() {
			// if the image is not fully loaded do nothing
			if (!this.complete) {
				return;
			}
			var image = $(this);
			// if image has the same dimensions as the gallery
			// just show the image don't animate
			if (image.width() == zoomContent.width() && 
			    image.height() == zoomContent.height()) {
				show(image);
				return;
			}
			var borderWidth = parseInt(zoomContent.css('borderLeftWidth'));
			// resize the gallery to the image dimensions before
			// displaying the image
			zoomContent.animate({
				width: image.width(),
				height: image.height(),
				marginTop: -(image.height() / 2) - borderWidth,
				marginLeft: -(image.width() / 2) - borderWidth
			}, 300, function(){
				show(image);
			});

			function show(image) {
				image.fadeIn('fast');
			}
		}
		
		function close(event) {
			event.preventDefault();
			zoomedIn = false;
			zoom.hide();
			zoomContent.empty();
		}
		
		setup();
	};
})(jQuery);

After including the above plugin, initialize it by adding the plugin call into the generate_thumbnail_list method:

function generate_thumbnail_list( $post_id = null ) {
	// ...
	if ( count( $images ) > 0 ) {
		// ...
		echo '<script type="text/javascript">jQuery.zoom();</script>';
	}
}

Example

A real life example of how this works and how it can be used: Zoom jQuery Photo Gallery Plugin demo


References

Related Posts
  • Web Design
    Complete Websites
    Building the Responsive Timeline Portfolio PagePortfolio thumb
    During this tutorial we will be building the fantastic Timeline Portfolio as seen in an earlier tutorial by Tomas Laurinavicius. We will be using some responsive techniques as well as CSS3 animations, Sass and a little bit of jQuery.Read More…
  • Web Design
    Case Studies
    How They Did It: Typekit's New HomepageTypekit retina
    Typekit recently redesigned their homepage with some new services in mind. When Typekit joined Adobe, they set out to bring us a new way to handle fonts on the web. Not only did they create a fairly simple way to embed fonts on the web, but they have now officially launched a desktop sync option, which allows Creative Cloud subscribers to sync fonts to their computer directly from Typekit. This has been in a beta form for a while now, and provides a much easier route to local fonts than finding them elsewhere!Read More…
  • Web Design
    UX
    Walk Users Through Your Website With Bootstrap TourTour retina
    When you have a web application which requires some getting used to from your users, a walkthrough of the interface is in order. Creating a walkthrough directly on top of the interface makes things very clear, so that's what we're going to build, using Bootstrap Tour.Read More…
  • Code
    PHP
    Creating a Photo Tag Wall With Twilio Picture Messaging & PHPProcedural to oop php retina preview
    Twilio's recently announced Picture Messaging has vastly opened up what we can do with text messaging, now we can attach photos to our text messages and have them get used in different ways. In our case, we are going to build a Photo Tag Wall, which will contain photos linked to tags that will be displayed on a website.Read More…
  • Code
    Theme Development
    Creating a WordPress Theme From Static HTML: Creating Template FilesCreating wordpress theme from html 400
    In the first part of this series, I showed you how to prepare your HTML and CSS files for WordPress, ensuring the structure would work, the code was valid and that the correct classes were being used. In this tutorial you'll learn how to take your index.html file and split it up into a set of template files for use by WordPress.Read More…
  • Web Design
    HTML/CSS
    How to Customize the Foundation 4 Top BarFoundation preview
    Zurb's Foundation 4 features a brilliant top bar which has become almost symbolic of a Foundation site build. Today we're going to look at how you can implement it in a different way, placing it elsewhere on the page, giving you a custom and responsive horizontal navigation menu.Read More…