Flickr Photobar Gallery

After we got a lot of great feedback for our image galleries we decided to follow some of the suggestions and create a gallery that uses the Flickr API. The aim was to build a bottom photobar that one can easily integrate into a website. It is hidden initially and slides up […]

View demoDownload source

After we got a lot of great feedback for our image galleries we decided to follow some of the suggestions and create a gallery that uses the Flickr API. The aim was to build a bottom photobar that one can easily integrate into a website. It is hidden initially and slides up when the handle is clicked. First, the photo sets are shown and when one of them is chosen, all its images can be viewed as thumbnails. When a thumbnail is clicked, a full image view appears as an overlay.

For our demo we used the awesome photostream by tibchris.

To get familiar with the Flickr API, you can find some information on the Flickr Services website.

So, let’s begin!

The Markup

The HTML consists of one main wrapper for the whole photobar. Inside of that we will have the div for the full image view and the div for the photobar with the album thumbnail wrapper and the images thumbnail wrapper. Besides that, we will also have our visible handle and a div for the overlay:

<!-- main wrapper: flickr_photobar -->
<div class="flickr_photobar">		

	<!-- overlay for the full image view -->
	<div id="flickr_overlay" class="overlay" style="display:none;"></div>

	<!-- full image view -->
	<div id="flickr_photopreview" class="photopreview" style="display:none;">
		<div class="preview_wrapper">
			<div class="preview">
				<div class="loading"></div>
				<div id="preview_close" class="close"></div>
				<span id="large_phototitle"></span>
				<!-- here we will insert the image-->
				<a href="#" class="img_next"></a>
				<a href="#" class="img_prev"></a>
			</div>
		</div>
	</div>

	<!-- the bottom photobar -->
	<div id="photobar" class="photobar">	

		<!-- the thumbnail view of the albums/sets -->
		<div class="thumbs albums" id="sets">
			<a href="#" class="prev"></a>
			<div class="thumbsWrapper">
				<ul></ul>
			</div>
			<a href="#" class="next"></a>
		</div>

		<!-- the thumbnail view of the images of a set -->
		<div class="thumbs images" id="images" style="bottom:-125px;">
			<a href="#" class="prev"></a>
			<div class="thumbsWrapper">
				<ul></ul>
			</div>
			<a href="#" class="next"></a>
			<!-- the right handle for the info-->
			<span class="images_toggle">
				Set:
				<span id="setName"></span>
				<a id="images_toggle">Back to Sets</a>
			</span>
		</div>

		<!-- the left handle for the main photobar -->
		<a id="flickr_toggle" class="toggle">
			Flickr Photostream
			<span style="visibility:hidden;" class="loading_small"></span>
		</a>
	</div>

</div>

As you can see, a lot of elements will be hidden in the beginning. In the JavaScript function we will control the visibility and appearance of these elements.
Let’s have a look at the styling.

The CSS

Since we want this gallery to work as an integrated part of any website, we will give most of the elements a fixed position. That means, we will add them on top of everything else in the website. If you come across some problems concerning other items on you website that are on top of this gallery, you might want to adjust the z-indexes.

Ok, so let’s define some general styles first:

.flickr_photobar{
	font-family:Arial,Helvetica,sans-serif;
	text-transform:uppercase;
	font-size:11px;
}
.flickr_photobar a{
	outline:none;
}
.flickr_photobar a:hover{
	outline:none;
}

Since we will be abusing a lot of link elements for other purposes than links (OK, don’t beat me up now, I’ll try to get rid of that habit), we want to remove the outline, so that the ugly dotted line does not appear in Firefox.

The photobar div will have the following style:

.photobar{
	position:fixed;
	bottom:-96px;
	left:0px;
	width:100%;
	height:95px;
}

We will hide the photobar by setting it’s bottom to a negative value. The toggle (or handle) will still be visible because we will pull it up by setting it’s top value to a negative value. We will look into that class later.

The thumbs class will be applied to both, the div of the album (or set) thumbnails and the div of the image thumbnails belonging to the respective sets:

.thumbs{
	position:absolute;
	bottom:0px;
	left:0px;
	width:100%;
	height:95px;
	border-top:1px solid #222;
	background-color:#3D3D3D;
}

The styling of the previous and next arrows will be as follows:

.thumbs a.prev,
.thumbs a.next{
	width:20px;
	height:83px;
	position:absolute;
	top:4px;
	margin:0px;
	z-index:10;
	border:1px solid #222;
	-moz-box-shadow:0px 0px 1px #777 inset;
	-webkit-box-shadow:0px 0px 1px #777 inset;
	box-shadow:0px 0px 1px #777 inset;
}
.thumbs a.prev:hover,
.thumbs a.next:hover{
	background-color:#404040;
}
.thumbs a.prev{
	left:0px;
	background:#333 url(../prev.png) no-repeat center center;
}
.thumbs a.next{
	right:0px;
	background:#333 url(../next.png) no-repeat center center;
}

With the inset property in the box shadow, we can create a nice effect: if you have a dark background and add a light inset box shadow, the element will look slightly embossed.

The wrapper for the thumbs is going to have the following style:

.thumbs .thumbsWrapper{
	height:95px;
	left:22px;
	right:22px;
	overflow:hidden;
	position:absolute;
	top:0;
}

The unordered list for the thumbnails will be positioned absolutely and we will hide any overflow. It’s width is going to be calculated dynamically and it depends on the amount of thumbnails.

.thumbs ul{
	list-style:none;
	margin:0px;
	padding:0px;
	height:90px;
	overflow:hidden;
	position:absolute;
	left:0px;
	top:0px;
}
.thumbs ul li a{
	position:relative;
	float:left;
	margin:6px 2px 0px 2px;
	color:#fff;
	text-shadow:1px 1px 1px #000;
	text-decoration:none;
	height:81px;
	width:81px;
}

We will give the albums/sets thumbnails a dark border and the images thumbnails a light border:

.albums ul li a img{
	border:3px solid #111111;
	-moz-box-shadow:1px 1px 3px #000;
	-webkit-box-shadow:1px 1px 3px #000;
	box-shadow:1px 1px 3px #000;
}
.images ul li a img{
	border:3px solid #f9f9f9;
	-moz-box-shadow:1px 1px 3px #000;
	-webkit-box-shadow:1px 1px 3px #000;
	box-shadow:1px 1px 3px #000;
}

The description for each set or image is going to appear on top of each thumbnail. We will make it slightly transparent and not allow the text to overflow. While in webkit browsers we can use text-overflow:ellipsis (which cuts off too long words and adds two dots) we need to set overflow:hidden for the other browsers:

.thumbs a span{
	position:absolute;
	bottom:3px;
	left:3px;
	right:3px;
	background-color:#333;
	font-size:9px;
	padding:2px 2px;
	border-top:1px solid #111;
    display:none;
	text-align:center;
	overflow:hidden;
	text-overflow:ellipsis;
	max-height:70px;
	opacity:0.8;
	filter:progid:DXImageTransform.Microsoft.Alpha(opacity=80);
}

This is how we actually show the description span – when we hover over the image link:

.thumbs a:hover span{
	display:block;
}

The handle for the images that appears on the right side and the main handle for the photobar will have the following style:

span.images_toggle{
	position:absolute;
	top:-26px;
	right:20px;
	background-color:#3D3D3D;
	border:1px solid #222;
	color:#EEEEEE;
	font-size:10px;
	padding:0px 6px 0px 12px;
	height:24px;
	line-height:24px;
	text-transform:uppercase;
	text-shadow:1px 1px 2px #000;
	-moz-box-shadow:0px -1px 3px #ccc;
	-webkit-box-shadow:0px -1px 3px #ccc;
	box-shadow:0px -1px 3px #ccc;
	-moz-border-radius:5px 5px 0px 0px;
	-webkit-border-top-left-radius:5px;
	-webkit-border-top-right-radius:5px;
	border-top-left-radius:5px;
	border-top-right-radius:5px;
}
span.images_toggle a{
	background-color:#222;
	border:1px solid #000;
	cursor:pointer;
	line-height:16px;
	padding:0px 5px;
	-moz-border-radius:5px;
	-webkit-border-radius:5px;
	border-radius:5px;
}
span.images_toggle a:hover{
	background-color:#000;
}
.photobar a.toggle{
	position:absolute;
	top:-26px;
	left:20px;
	background-color:#3D3D3D;
	border:1px solid #222;
	color:#EEEEEE;
	font-size:10px;
	padding:0px 36px 0px 36px;
	line-height:24px;
	height:24px;
	text-transform:uppercase;
	text-shadow:1px 1px 2px #000;
	-moz-box-shadow:0px -1px 3px #ccc;
	-webkit-box-shadow:0px -1px 3px #ccc;
	box-shadow:0px -1px 3px #ccc;
	-moz-border-radius:5px 5px 0px 0px;
	-webkit-border-top-left-radius:5px;
	-webkit-border-top-right-radius:5px;
	border-top-left-radius:5px;
	border-top-right-radius:5px;
	cursor:pointer;
}
span.loading_small{
	background:transparent url(../loading_small.gif) no-repeat center center;
	position:absolute;
	right:10px;
	top:0px;
	width:16px;
	height:24px;
}
.photobar a.toggle:hover{
	background-color:#111;
}

The images thumbnails container needs to have a higher z-index than the one of the sets:

.photobar .images{
	z-index:20;
}

The overlay that appears when we show one full image is going to have the following style:

.flickr_photobar .overlay{
	z-index:90;
	background-color:#000;
	width:100%;
	height:100%;
	position:fixed;
	top:0px;
	left:0px;
	opacity:0.9;
	filter:progid:DXImageTransform.Microsoft.Alpha(opacity=90);
}

We make the position fixed so that when the user scrolls the page, it always stays in the visible area of the page:

.photopreview{
	text-align:center;
	position:fixed;
	width:100%;
	height:100%;
	top:0px;
	left:0px;
	z-index:91;
}

The following wrappers for the full image view will need the following styling because we want the image to be centered vertically and horizontally:

.photopreview .preview_wrapper{
	position:relative;
	text-align:center;
	margin:0 auto;
}
.photopreview .preview{
	display:table-cell;
	text-align:center;
	width:0px;
	height:0px;
	padding-top:25px;
	vertical-align:middle;
}
.photopreview .preview img{
	vertical-align:middle;
	background-color:#555;
	padding:1px;
	border:8px solid #f9f9f9;
	-moz-box-shadow:1px 1px 5px #222;
	-webkit-box-shadow:1px 1px 5px #222;
	box-shadow:1px 1px 5px #222;
}

The description for the image is going to be put into a span that is fixed at the top of the page:

.photopreview .preview span{
	background-color: #111111;
	color:#FFFFFF;
	height:20px;
	left:0;
	line-height:20px;
	position:fixed;
	text-align:center;
	text-shadow:1px 1px 1px #000000;
	top:0;
	width:100%;
	-moz-box-shadow:1px 1px 5px #000000;
	-webkit-box-shadow:1px 1px 5px #000000;
	box-shadow:1px 1px 5px #000000;
}

The little loading div that appears when an image is loaded is going to have the following style:

.loading{
	width:50px;
	height:50px;
	position:fixed;
	top:50%;
	left:50%;
	z-index:95;
	margin:-25px 0px 0px -25px;
	-moz-border-radius:10px;
	-webkit-border-radius:20px;
	border-radius:10px;
	background:#000 url(../loading.gif) no-repeat center center;
	opacity:0.8;
	filter:progid:DXImageTransform.Microsoft.Alpha(opacity=80);
}

We will add a close element at the top right corner that allows the user to close the image preview:

.close{
	background:#000 url(../close.png) no-repeat center center;
	cursor:pointer;
	height:20px;
	position:fixed;
	right:-11px;
	top:0;
	width:90px;
	z-index:1000;
	cursor:pointer;
	-moz-border-radius:10px;
	-webkit-border-radius:10px;
	border-radius:10px;
	opacity:0.8;
	filter:progid:DXImageTransform.Microsoft.Alpha(opacity=80);
}

The next and previous controls will be styled as follows:

.photopreview a.img_next,
.photopreview a.img_prev{
	position:fixed;
	top:50%;
	height:60px;
	width:50px;
	margin-top:-30px;
	background-color:#000;
	background-repeat:no-repeat;
	background-position:center center;
}
.photopreview a.img_next{
	background-image:url(../next.png);
	-moz-border-radius:20px 0px 0px 20px;
	-webkit-border-top-left-radius:20px;
	-webkit-border-bottom-left-radius:20px;
	border-top-left-radius:20px;
	border-bottom-left-radius:20px;
	right:0px;
}
.photopreview a.img_prev{
	background-image:url(../prev.png);
	-moz-border-radius:0px 20px 20px 0px;
	-webkit-border-top-right-radius:20px;
	-webkit-border-bottom-right-radius:20px;
	border-top-right-radius:20px;
	border-bottom-right-radius:20px;
	left:0px;
}

And that’s all the style!
Let’s add some JavaScript magic!

The JavaScript

For our script we will be using the jQuery viewport script.
In the following we will show some important snippets of our script since it is a very large script. You can view the whole commented script when you download the ZIP file.

Let’s start at the beginning of the script where we need to define some variables:

var api_key 	= [your API Key];
var user_id  	= [your Flickr user ID];
/*
use:
Square,Thumbnail,Small,Medium or Original for
the large image size you want to load!
*/
var large_image_size 	= 'Medium';

/*
the current Set id / the current Photo id
*/
var photo_set_id,photo_id;
/*
the current position of the image being viewed
*/
var current	= -1;
var continueNavigation = false;

/*
flickr API Call to get List of Sets
*/
var sets_service 		= 'http://api.flickr.com/services/rest/?&method=flickr.photosets.getList' + '&api_key=' + api_key;
var sets_url			= sets_service + '&user_id=' + user_id + '&format=json&jsoncallback=?';

/*
flickr API Call to get List of Photos from a Set
*/
var photos_service 		= 'http://api.flickr.com/services/rest/?&method=flickr.photosets.getPhotos' + '&api_key=' + api_key;

/*
flickr API Call to get List of Sizes of a Photo
*/
var large_photo_service = 'http://api.flickr.com/services/rest/?&method=flickr.photos.getSizes' + '&api_key=' + api_key;

/*
elements caching...
*/
var $setsContainer 		= $('#sets').find('ul');
var $photosContainer 	= $('#images').find('ul');
var $photopreview		= $('#flickr_photopreview');
var $flickrOverlay		= $('#flickr_overlay');
var $loadingStatus		= $('#flickr_toggle').find('.loading_small');

var ul_width,spacefit,fit;

Our first step is to load all the sets of the respective user. The following code we will do that:

/* start: open Flickr Photostream */
$('#flickr_toggle').toggle(function(){
	$('#photobar').stop().animate({'bottom':'0px'},200,function(){
		if($setsContainer.is(':empty')){
			/*
			if sets not loaded, load them
			*/
			LoadSets();
		}
	});
},function(){
	/*
	minimize the main bar, and minimize the photos bar.
	next time we maximize, the view will be on the sets
	*/
	$('#photobar').stop().animate({'bottom':'-96px'},200,function(){
		$('#images').css('bottom','-125px');
	});
});

/*
Loads the User Photo Sets
*/
function LoadSets(){
	$loadingStatus.css('visibility','visible');

	$.getJSON(sets_url,function(data){
		if(data.stat != 'fail') {
			var sets_count = data.photosets.photoset.length;
			/*
			adapt ul width based on number of results
			*/
			ul_width = sets_count * 85 + 85;
			$setsContainer.css('width',ul_width + 'px');

			for(var i = 0; i < sets_count; ++i){
				var photoset		= data.photosets.photoset[i];
				var primary 		= photoset.primary;
				var secret			= photoset.secret;
				var server			= photoset.server;
				var farm			= photoset.farm;
				/*
				source for the small thumbnail
				*/
				var photoUrl		= 'http://farm'+farm+'.static.flickr.com/'+server+'/'+primary+'_'+secret+'_s.jpg';
				var $elem 			= $('<li />');
				var $link 			= $('<a class="toLoad" href="#" />');
				/*
				save the info of the set in the li element,
				we will use it later
				*/
				$link.data({
					'primary'	:primary,
					'secret'	:secret,
					'server'	:server,
					'farm'		:farm,
					'photoUrl'	:photoUrl,
					'setName'	:photoset.title._content,
					'id'		:photoset.id
				});

				$setsContainer.append($elem.append($link));
				$link.bind('click',function(e){
					var $this = $(this);
					/*
					save the current Set id in the photo_set_id variable
					and load the photos of that Set
					*/
					$('#images').stop().animate({'bottom':'0px'},200);
					if(photo_set_id!=$this.data('id')){
						photo_set_id = $this.data('id');
						$('#setName').html($this.data('setName'));
						LoadPhotos();
					}
					e.preventDefault();
				});
			}
			/*
			now we load the images
			(the ones in the viewport)
			*/
			LoadSetsImages();
		}
	});
}

/*
loads the images of the sets that are in the viewport
*/
function LoadSetsImages(){
	var toLoad 			= $('.toLoad:in-viewport').size();
	if(toLoad > 0)
		$loadingStatus.css('visibility','visible');
	var images_loaded 	= 0;
	$('.toLoad:in-viewport').each(function(i){
		var $space			= $setsContainer.find('.toLoad:first');
		var $img 			= $('<img style="display:none;" />').load(function(){
			++images_loaded;
			if(images_loaded == toLoad){
				$loadingStatus.css('visibility','hidden');
				$setsContainer.find('img').fadeIn();
			}
		}).attr('src',$space.data('photoUrl')).attr('alt',$space.data('id'));
		var $set_name		= $('<span />',{'html':$space.data('setName')});
		$space.append($set_name).append($img).removeClass('toLoad');
	});
}

The next functions are for loading the photos of a specific set that is clicked:

/*
Loads the Set's Photos
*/
function LoadPhotos(){
	$photosContainer.empty();
	$loadingStatus.css('visibility','visible');
	var photos_url	= photos_service + '&photoset_id=' + photo_set_id + '&media=photos&format=json&jsoncallback=?';

	$.getJSON(photos_url,function(data){
		if(data.stat != 'fail') {
			var photo_count = data.photoset.photo.length;
			/*
			adapt ul width based on number of results
			*/
			var photo_count_total = photo_count + $photosContainer.children('li').length;
			ul_width = photo_count_total * 85 + 85;
			$photosContainer.css('width',ul_width + 'px');

			for(var i = 0; i < photo_count; ++i){
				var photo			= data.photoset.photo[i];
				var photoid			= photo.id;

				var secret			= photo.secret;
				var server			= photo.server;
				var farm			= photo.farm;

				var photoUrl		= 'http://farm'+farm+'.static.flickr.com/'+server+'/'+photoid+'_'+secret+'_s.jpg';

				var $elem 			= $('<li />');
				var $link 			= $('<a class="toLoad" href="#" />');

				$link.data({
					'photoid'		:photoid,
					'secret'		:secret,
					'server'		:server,
					'farm'			:farm,
					'photoUrl'		:photoUrl,
					'photo_title'	:photo.title
				});
				$photosContainer.append($elem.append($link));

				$link.bind('click',function(e){
					var $this	= $(this);
					current		= $this.parent().index();
					photo_id 	= $this.data('photoid');
					LoadLargePhoto();
					e.preventDefault();
				});
			}
			LoadPhotosImages();
		}

	});
}

/*
loads the images of the set's
photos that are in the viewport
*/
function LoadPhotosImages(){
	var toLoad 			= $('.toLoad:in-viewport').size();
	if(toLoad > 0)
		$loadingStatus.css('visibility','visible');
	var images_loaded 	= 0;

	$('.toLoad:in-viewport').each(function(i){
		var $space			= $photosContainer.find('.toLoad:first');
		var $img 			= $('<img style="display:none;" />').load(function(){
			++images_loaded;
			if(images_loaded == toLoad){
				$loadingStatus.css('visibility','hidden');
				$photosContainer.find('img').fadeIn();
				/*
				if we were navigating through the large images set:
				*/
				if(continueNavigation){
					continueNavigation 	= false;
					var $thumb 			= $photosContainer.find('li:nth-child(' + parseInt(current + 1) + ')').find('img');
					photo_id 			= $thumb.attr('alt');
					LoadLargePhoto();
				}
			}
		}).attr('src',$space.data('photoUrl'))
		  .attr('alt',$space.data('photoid'));

		var $photo_title	= $('<span/>',{'html':$space.data('photo_title')});
		$space.append($photo_title).append($img).removeClass('toLoad');
	});
}

Now we need a function that loads the full image:

/*
loads the main image
*/
function LoadLargePhoto(){
	removeLargeImage();

	var $theThumb 	= $photosContainer.find('li:nth-child(' + parseInt(current + 1) + ')').find('img');
	var photo_title = $theThumb.parent().data('photo_title');

	var $loading	= $photopreview.find('.loading');
	$loading.show();
	$photopreview.show();
	$flickrOverlay.show();
	$('#preview_close').show();

	var large_photo_url = large_photo_service + '&photo_id=' + photo_id + '&format=json&jsoncallback=?';
	$.getJSON(large_photo_url,function(data){
		if(data.stat != 'fail') {
			var count_sizes 		= data.sizes.size.length;
			var largest_photo_src;
			for(var i = 0; i < count_sizes; ++i){
				if(data.sizes.size[i].label == large_image_size)
					largest_photo_src 	= data.sizes.size[i].source;
			}
			$('<img />').load(function(){
				var $this = $(this);
				/*
				resize the image to fit in the browser's window
				*/
				resize($this);

				$loading.hide();
				/*just to make sure there's no image:*/
				removeLargeImage();
				$photopreview.find('.preview').append($this);
				$('#large_phototitle').empty().html(photo_title);
			}).attr('src',largest_photo_src);
		}
	});
}

The next functions and events (that we will not discuss in more detail here) control the basic navigation through the thumbnails and the preview images. We also add a resize function that adapts the size of the image to the current viewport. Check out the script in the ZIP file where you can see the comments.

And that’s it! I hope you enjoyed this tutorial and find it useful!

View demoDownload source

Message from TestkingLearn how to create flickr photobar gallery with our testking CISA course. Download the testking 642-262 tutorials and testking 642-661 guides to learn JQuery and other web application is simple and easy way.
Previous:
Next:

Tagged with:

Mary Lou (Manoela Ilic) is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.

View all contributions by

Website: http://tympanus.net/

Related Articles

Feedback 75

Comments are closed.
  1. 2

    In the source code, I noticed there is 2 within the tags, just before and after the prev/next buttons when previewing a photo. I’m wanting to remove these tags, however I cannot find where these tags are located. I don’t see them in the js files or html file.

    Any help?

  2. 3

    Nevermind, I figured out what the problem was. It was my editor, not the script. I love this script!

  3. 5

    if you want your flickr sets of images to be automatically loaded on pageload you need to add one line in jquery.FlickrPhotobar.js file after line 38:
    var ul_width,spacefit,fit;
    add:
    LoadSets();

    that’s it :)

  4. 7

    Hi! Great Job.
    On iPad and iPhone there is a problem: when I rotate the device, the plugin doesn’t replace correctly (safari for iPhone/iPad), going over the content.

  5. 8

    Hi.. I am just wondering if anybody figured out the problem on FF and chrome as the next and previous in the scrollbar do not seem to work but do in IE9. Any help would be appreciated on this as I am pulling my hair out over it. Also as per the above request.. Is there any way to fix it for Iphone and Ipad as it seems to jump from the bottom of the page..

    Thanks

  6. 12

    why can’t show out the large image when i change the “medium” to “original”?
    change “small” and others is normal worked but “original”…
    help me please:)
    —————————————————
    use:Square,Thumbnail,Small,Medium or Original forthe large image size you want to load!
    var large_image_size = ‘Medium';

  7. 13

    Very nice!

    Had to change;

    <a id=”preview_img_next” href=”#” class=”img_next”></a>

    <a id=”preview_img_prev” href=”#” class=”img_prev”></a>

    for it to work in FF.

    Thanks,
    Mike

  8. 14

    I used this in my tumblr code and the thumbnails are not loading. Can anyone help me with this?
    Thanks

  9. 15

    When I set

    var large_image_size= ‘Medium';

    to “Original” it doesn’t work.

    Any help please?

Comments are closed.