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
Comments