Tạo Flickr Photobar Gallery với jQuery

Đăng bởi Neo trong mục Hướng dẫn vào 30 tháng 7, 2010 | Comments

Bài viết hướng dẫn tạo một trang gallery ảnh sử dụng API của Flickr. Mục đích thêm một thanh ngang nằm phía dưới trang vào website. Khi click vào sẽ hiển thị các bộ ảnh của Flickr, và xem ảnh dưới dạng lightbox.

Người dịch: Neo
Link gốc: Flickr Photobar Gallery
Bạn không được tự ý copy và phát hành lại nội dung của bài viết này

Trang demo sử dụng ảnh của tibchris
Để đăng ký Flickr API, bạn có thể xem thêm tại Flickr Services website

Mã HTML

Dưới đây là tất cả mã HTML cho gallery, đã bao gồm các thẻ cho việc hiển thị ảnh lớn, thumbnail.

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

	<!-- Hình nền hiển thị khi xem ảnh kích thước lớn -->
	<div id="flickr_overlay" class="overlay" style="display:none;"></div>

	<!-- Xem ảnh lớn -->
	<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>
				<!-- Chúng ta sẽ thêm thẻ image tại đây -->
				<a href="#" class="img_next"></a>
				<a href="#" class="img_prev"></a>
			</div>
		</div>
	</div>

	<!-- thanh photobar ở phía dưới cùng -->
	<div id="photobar" class="photobar">	

		<!-- Ảnh thumbnail của 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>

		<!-- Ảnh thumbnail của các ảnh trong một set (hoặc album) -->
		<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>
			<!-- Thông tin hiển thị bên phải-->
			<span class="images_toggle">
				Set:
				<span id="setName"></span>
				<a id="images_toggle">Back to Sets</a>
			</span>

		</div>

		<!-- Nút điều khiển bên trái -->
		<a id="flickr_toggle" class="toggle">
			Flickr Photostream
			<span style="visibility:hidden;" class="loading_small"></span>
		</a>
	</div>
</div>

Phần lớn các phần tử được ẩn đi ngay từ đầu. Chúng ta sẽ dùng javascript để điều khiển cách hiển thị dựa trên các sự kiện từ người dùng. Nhưng tiếp theo hãy xem đến mã CSS

Mã CSS

Như đã nói ở phần trên, gallery sẽ hoạt động dưới dạng một phần của website, chúng ta sẽ để các phần tử có vị trí fixed và đặt lên trên tất cả các phần tử khác của website (nếu website của bạn có các phần tử đè lên trên, bạn có thể sử dụng thuộc tính z-index).

Trước tiên là mã CSS cơ bản: khai báo các thuộc tính về font chữ, bỏ qua outline của thẻ a

.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;
}

Thẻ div cho photobar sẽ có mã CSS như sau:

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

Photobar được ẩn đi nhờ thuộc tính bottom: -96px. Và chúng ta sẽ chuyển bottom: 0 để gọi Photobar lên.

Class .thumbs sẽ được sử dụng cho cả 2 phần: thumbnails cho thẻ div của album (hoặc set) và thumbnails cho ảnh của album đó:

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

Mã CSS cho nút previous và next:

.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;
}

Ở đây tác giả sử dụng thuộc tính box shadow để tạo hiệu ứng cho nút thumbs và previous. Bạn có thể chỉnh sửa hoặc thay đổi hình ảnh để có các nút riêng của mình

Khung bao ngoài tất cả thumbs có style như sau:

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

Danh sách các ảnh thumbnails được sử dụng style position: absolute và ẩn đi các ảnh ở ngoài khu vực hiển thị. Mã javascript sẽ tự động tính toán số lượng ảnh hiển thị và chiều rộng khu vực hiển thị dựa vào chiều rộng của trình duyệt.

.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;
}

Style border trắng và đổ bóng xẫm cho ảnh thumbnails:

.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;
}

Mô tả của mỗi album (set) hoặc ảnh sẽ xuất hiện phía bên trên ảnh khi bạn di chuột vào. Để ảnh hiển thị không bị tràn ra ngoài chúng ta sử dụng thuộc tính overflow:hidden với các trình duyệt sử dụng webkit bạn có thể sử dụng text-overflow:ellipsis (cắt các câu quá dài và thêm dấu ..):

.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);
}

Khi di chuột qua ảnh, chúng ta cho hiển thị thẻ span:

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

Style cho tên của album (hoặc set) xuất hiện bên phải khi click vào album:

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;
}

Ảnh cần đè lên trên bộ set, do đó chúng ta đặt thuộc tính z-index cao hơn:

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

Nền đen xuất hiện khi xem ảnh với kích thước lớn, có mã CSS:

.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);
}

Sử dụng thuộc tính position: fixed giúp các thành phần luôn ở màn hình cho dù người dùng cuộn chuột tới bất kỳ phần nào của trang:

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

Vì chúng ta muốn ảnh hiển thị ở giữa theo cả 2 chiều ngang và dọc nên cần sử dụng đoạn mã CSS sau:

.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;
}

Mô tả cho ảnh được đặt ở trên cùng trang:

.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;
}

Mã CSS cho thẻ div loading xuất hiện khi đang chờ bức ảnh tải từ Flickr về:

.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);
}

và nút close ở trên cùng bên phải:

.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);
}

Cuối cùng là style cho nút next và previous:

.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;
}

Mã JavaScript

Ngoài jQuery (1.4.2), trong bài hướng dẫn này chúng ta sẽ sử dụng thêm jQuery viewport script.
Đoạn mã javascript rất dài và xử lý ở nhiều phần, bạn có thể xem chi tiết (tiếng anh) khi tải về. Dưới đây là các phần quan trọng nhất.

Trước tiên là việc định nghĩa các biến:

var api_key 	= [your API Key];
var user_id  	= [your Flickr user ID];
/*
sử dụng:
Square,Thumbnail,Small,Medium hoặc Original
cho ảnh khi xem Full
*/
var large_image_size 	= 'Medium';

/*
Set id / Photo id hiện tại
*/
var photo_set_id,photo_id;
/*
Vị trí ảnh đang được xem
*/
var current	= -1;
var continueNavigation = false;

/*
Sử dụng flickr API để lấy danh sách các 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=?';

/*
Sử dụng flickr API để lấy danh sách các ảnh trong một Set
*/
var photos_service 		= 'http://api.flickr.com/services/rest/?&method=flickr.photosets.getPhotos' + '&api_key=' + api_key;

/*
Sử dụng flickr API để lấy kích thước ảnh
*/
var large_photo_service = 'http://api.flickr.com/services/rest/?&method=flickr.photos.getSizes' + '&api_key=' + api_key;

/*
Cache các phần tử...
*/
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;

Bước đầu tiên là tải tất cả các sets của user. Đoạn mã làm việc đó như sau::

/* start: open Flickr Photostream */
$('#flickr_toggle').toggle(function(){
	$('#photobar').stop().animate({'bottom':'0px'},200,function(){
		if($setsContainer.is(':empty')){
			/*
			nếu chưa tải sets thì bắt đầu tải
			*/
			LoadSets();
		}
	});
},function(){
	/*
	Thu nhỏ mainbar và photosbar
	để khi gọi ảnh lên thì các sets đã sẵn sàng hiển thị
	*/
	$('#photobar').stop().animate({'bottom':'-96px'},200,function(){
		$('#images').css('bottom','-125px');
	});
});

/*
Hàm gọi các sets của user
*/
function LoadSets(){
	$loadingStatus.css('visibility','visible');

	$.getJSON(sets_url,function(data){
		if(data.stat != 'fail') {
			var sets_count = data.photosets.photoset.length;
			/*
			tính toán chiều rộng của thẻ ul dựa trên số lượng các set
			*/
			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;
				/*
				link của các ảnh thumbnail
				*/
				var photoUrl		= 'http://farm'+farm+'.static.flickr.com/'+server+'/'+primary+'_'+secret+'_s.jpg';
				var $elem 			= $('<li />');
				var $link 			= $('<a class="toLoad" href="#" />');
				/*
				lưu các thông tin vảo thẻ li để sử dụng sau
				*/
				$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);
					/*
					Lưu set hiện tại vào biến photo_set_id 
					và tải toàn bộ ảnh của 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();
				});
			}
			/*
			bắt đầu tải ảnh
			*/
			LoadSetsImages();
		}
	});
}

/*
Hàm tải ảnh ở 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');
	});
}

Tiếp theo chúng ta sẽ tải các bức ảnh khi chúng được click:

/*
Tải các ảnh của bộ set
*/
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;
			/*
			Tính toán chiều dài của thẻ ul dựa trên số lượng của ảnh trả về
			*/
			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();
		}

	});
}

/*
Tải các ảnh ở trong 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();
				/*
				Nếu chúng ta click nút next và prev khi xem ảnh lớn:
				*/
				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');
	});
}

Cuối cùng là hàm tải ảnh lớn:

/*
Tải ảnh lớn
*/
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);
				/*
				Thay đổi kích thước ảnh để phù hợp với cửa sổ trình duyệt
				*/
				resize($this);

				$loading.hide();
				/*Đảm bảo là không hiển thị các ảnh khác:*/
				removeLargeImage();
				$photopreview.find('.preview').append($this);
				$('#large_phototitle').empty().html(photo_title);
			}).attr('src',largest_photo_src);
		}
	});
}

Bạn có thể không cần phải hiểu hết bài hướng dẫn mà vẫn có thể sử dụng được trong trang web của mình bằng cách áp dụng mã HTML, CSS và thêm file javascript vào trang web của mình. Để hiểu hết được bài hướng dẫn này bạn cần có khoảng 3 tiếng nghiên cứu kỹ (tất nhiên là sau khi nắm vững kiến thức về CSSjQuery), đừng nóng vội nhé Wink

Bạn có thích bài viết này?

Neo's picture

Neo

Nhìn mặt trời từ năm 1984 nhưng tới tận 2002 mới được thấy cái máy tính đầu tiên của mình. Đầu năm 2007 thì quyết định theo cái nghề cao quý là thiết kế web Big Grin. Hiện mình đang sống tại Hà Nội. Sở thích: làm website và giúp đỡ mọi người phát triển website theo chiều hướng tốt đẹp hơn.

Trang chủ - Twitter