lunes, 10 de octubre de 2011

Draggable Image Boxes Grid

Draggable Image Boxes Grid:
Draggable Image Boxes Grid
View demo Download source
Today we want to create a template with a fullscreen grid of images and content areas. The idea is to have a draggable grid that shows boxes of thumbnails and menu like items. Once clicked, the thumbnail will expand to the full size image and the menu item box will expand to a fullscreen content area.
The main idea for this template and its effects comes from the beautiful Flash-based website of Stephen Hamilton.
To make the grid draggable, we used jQuery.kinetic by Dave Taylor. The jQuery plugin allows smooth drag scrolling and it’s just what we need in our grid.
The beautiful images in the demo are by Ibrahim Iujaz. Check out his Flickr photostream.
So, let’s begin!

The Markup

The main container will be a div with the class and ID ib-main-wrapper. We will add another div element inside since we will need this structure for applying the jQuery.kinetic plugin. Inside of that div which has the class ib-main, we will place two types of link elements: the thumbnail links and the content links. The content links are the menu like boxes that will expand to a fullscreen content area:
<div id="ib-main-wrapper" class="ib-main-wrapper">
<div class="ib-main">
<a href="#">
<img src="images/thumbs/1.jpg" data-largesrc="images/large/1.jpg" alt="image01"/>
<span>Crabbed Age and Youth</span>
</a>
<a href="#">
<img src="images/thumbs/2.jpg" data-largesrc="images/large/2.jpg" alt="image02"/>
<span>Cannot live together</span>
</a>
<a href="#" class="ib-content">
<div class="ib-teaser">
<h2>Welcome <span>Howdy, Stranger</span></h2>
</div>
<div class="ib-content-full">
<!-- Some content -->
</div>
</a>
...
</div>
</div>
The boxes for the content will have the class ib-content. The path to the large image for the thumbnails will be saved in the data attribute data-largesrc.
For the content and image preview we will use jQuery templates. The template for the large image preview is the following:
<div id="ib-img-preview" class="ib-preview">
<img src="${src}" alt="" class="ib-preview-img"/>
<span class="ib-preview-descr" style="display:none;">${description}</span>
<div class="ib-nav" style="display:none;">
<span class="ib-nav-prev">Previous</span>
<span class="ib-nav-next">Next</span>
</div>
<span class="ib-close" style="display:none;">Close Preview</span>
<div class="ib-loading-large" style="display:none;">Loading...</div>
</div>
We will have the large image, the navigation buttons, a closing cross and a loading element.
The template for the fullscreen content preview looks like this:
<div id="ib-content-preview" class="ib-content-preview">
<div class="ib-teaser" style="display:none;">{{html teaser}}</div>
<div class="ib-content-full" style="display:none;">{{html content}}</div>
<span class="ib-close" style="display:none;">Close Preview</span>
</div>
Now, let’s style the grid.

The CSS

First, we will style the wrapping container. It will occupy all the page’s width and we will set the height dynamically to fit the visible area:
.ib-main-wrapper{
width: 100%;
overflow: hidden;
margin-top: 40px;
outline: none;
/*height dynamic*/
}
The main container will have a preset width. We chose this value because we want most visitors to be able to actually drag the grid. If you would set it to less and the user has a larger screen, then the container would be too small:
.ib-main{
position: relative;
width: 2546px;
}
Each box element will be floating and have a background image (for the thumbs) that we’ll stretch to be a bit larger than the box. We’ll add a nice transition which will make the background image contract on hover:
.ib-main a{
float: left;
width: 210px;
height: 210px;
position: relative;
overflow: hidden;
margin: 0px 0px 2px 2px;
cursor: move;
background: #fff url(../images/thumb_bg.jpg) no-repeat center center;
background-size: 110% 110%;
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
-ms-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
}
.ib-main a:hover{
background-size: 100% 100%;
}
The thumbnail will be slightly transparent so that we can see the background image (which is a thick border). We’ll also add some CSS3 transition here to animate the opacity on hover:
.ib-main a img{
opacity: 0.95;
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
-ms-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
}
.ib-main a:hover img{
opacity: 0.8;
}
The description will be positioned absolutely and we’ll place it out of the box. Then, on hover, we will make it slide in:
.ib-main > a > span{
display: block;
position: absolute;
width: 100%;
height: 20px;
line-height: 22px;
text-align: center;
font-size: 11px;
bottom: -20px;
left: 0px;
text-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
-ms-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
}
.ib-main a:hover > span{
bottom: 0px;
}
When we click on the thumbnail box, we’ll change the background image in order to show the loading image:
.ib-main a.ib-loading,
.ib-main a.ib-loading:hover{
background: #fff url(../images/ajax-loader.gif) no-repeat center center;
background-size: 31px 31px;
}
.ib-main a.ib-loading img,
.ib-main a.ib-loading:hover img{
opacity: 0.5;
}
When the thumbnail is in the loading state, we don’t want to show the description:
.ib-main > a.ib-loading > span,
.ib-main a.ib-loading > span{
display: none;
}
Now we’ll style the content boxes. The background is going to be dark:
.ib-content{
background: #f9f9f9;
}
The teaser is the text we’ll be showing in each content box. On hover we want to animate the background color:
.ib-content .ib-teaser{
text-align: center;
background: #333;
width: 100%;
height: 100%;
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
-ms-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
}
.ib-content .ib-teaser:hover{
background: #000;
}
The headline and the subline will have the following style:
.ib-teaser h2{
color: #fff;
font-size: 26px;
line-height: 26px;
padding-top: 40%;
text-shadow: 1px 0px 2px rgba(0,0,0,0.2);
}
.ib-teaser h2 span{
text-transform: none;
font-size: 16px;
font-family: Georgia, serif;
font-style: italic;
display: block;
}
When the content area gets expanded, we will show the preview container and it will have an absolute position and a dynamic height:
.ib-content-preview{
position: absolute;
top: 44px;
left: 0px;
background: #000;
width: 100%;
/* height dynamic*/
display: none;
}
The text elements will get some styling:
.ib-content-preview .ib-teaser h2{
font-size: 50px;
padding: 85px 40px 20px 40px;
}
.ib-content-preview .ib-teaser span{
padding: 20px 0px 0px 5px;
font-size: 22px;
}
.ib-content-full{
font-family:'Oswald', serif;
text-transform: none;
line-height: 26px;
margin: 0px 40px;
border-top: 1px solid #333;
padding: 20px 0px;
font-size: 16px;
}
.ib-content-full p{
padding: 5px 0px;
}
The large preview for the thumbnails will also be of absolute position
.ib-preview{
overflow: hidden;
position: absolute;
top: 40px;
display: none;
}
The description for the large image will be placed in the bottom left corner. We want big letters:
.ib-preview-descr{
position: absolute;
bottom: 30px;
left: 10px;
z-index: 999;
font-size: 50px;
text-shadow: 1px 0px 2px rgba(0,0,0,0.2);
}
The image itself will be absolute and the width and height will be set dynamically in the JavaScript:
.ib-preview img{
position: absolute;
}
The navigation for the images will be placed on the left and the right side of the screen:
.ib-nav span{
width: 53px;
height: 87px;
position: absolute;
top: 50%;
margin-top: -43px;
cursor: pointer;
text-indent: -9000px;
opacity: 0.6;
z-index: 999;
background: transparent url(../images/nav.png) no-repeat top right;
right: 10px;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
user-select: none;
}
.ib-nav span.ib-nav-prev{
background-position: top left;
left: 10px;
right: auto;
}
The closing element will be at the top right corner:
.ib-close{
top: 7px;
right: 7px;
background: transparent url(../images/close.png) no-repeat center center;
position: absolute;
width: 24px;
height: 24px;
cursor: pointer;
opacity: 0.2;
z-index: 999;
text-indent: -9000px;
}
.ib-nav span:hover, .ib-close:hover{
opacity: 1;
}
Last, but not least, we’ll style the loading element for the large image. We’ll place it in the center of the screen with our 50% and negative margin trick and give it some rounded borders:
.ib-loading-large{
text-indent: -9000px;
width: 60px;
height: 60px;
background: #fff url(../images/ajax-loader.gif) no-repeat center center;
position: absolute;
top: 50%;
left: 50%;
margin: -30px 0 0 -30px;
z-index: 999;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px 10px 10px 10px;
opacity: 0.9;
}
And that was all the style. Now, let’s take a look at the JavaScript.

The JavaScript

Let’s define our template function:
var $ibWrapper = $('#ib-main-wrapper'),

Template = (function() {
...
})();

Template.init();
First, we will set some variables and cache some elements:
var kinetic_moving    = false,
// current index of the opened item
current      = -1,
// true if the item is being opened / closed
isAnimating     = false,
// items on the grid
$ibItems     = $ibWrapper.find('div.ib-main > a'),
// image items on the grid
$ibImgItems     = $ibItems.not('.ib-content'),
// total image items on the grid
imgItemsCount    = $ibImgItems.length,
The init function will add a class to all the image items and call the jQuery.kinetic plugin and initialize our main events:
init      = function() {

// add a class ib-image to the image items
$ibImgItems.addClass('ib-image');
// apply the kinetic plugin to the wrapper
loadKinetic();
// load some events
initEvents();

},
loadKinetic will set the size of the main wrapper and apply the jQuery.kinetic plugin:
loadKinetic     = function() {

setWrapperSize();

$ibWrapper.kinetic({
moved : function() {

kinetic_moving = true;

},
stopped : function() {

kinetic_moving = false;

}
});

},
setWrapperSize will set the height of the main wrapper by taking the window height and removing the top bar and the bottom bar (which is our demo header actually):
setWrapperSize    = function() {

var containerMargins = $('#ib-top').outerHeight(true) + $('#header').outerHeight(true) + parseFloat( $ibItems.css('margin-top') );
$ibWrapper.css( 'height', $(window).height() - containerMargins )

},
initEvents calls the openItem function when we click on a box (unless we are dragging) and takes care of the resizing when we change the window size:
initEvents     = function() {

// open the item only if not dragging the container
$ibItems.bind('click.ibTemplate', function( event ) {

if( !kinetic_moving )
openItem( $(this) );

return false; 

});

// on window resize, set the wrapper and preview size accordingly
$(window).bind('resize.ibTemplate', function( event ) {

setWrapperSize();

$('#ib-img-preview, #ib-content-preview').css({
width : $(window).width(),
height : $(window).height()
})

});

},
When we click on an item, depending on which item we clicked, we’ll load a full image or a content area:
openItem     = function( $item ) {

if( isAnimating ) return false;

// if content item
if( $item.hasClass('ib-content') ) {

isAnimating = true;
current = $item.index('.ib-content');
loadContentItem( $item, function() { isAnimating = false; } );

}
// if image item
else {

isAnimating = true;
current = $item.index('.ib-image');
loadImgPreview( $item, function() { isAnimating = false; } );

}

},
If we click on a thumbnail, we’ll load and expand to the respective large image. We will use the jQuery template for the large image preview and set it to the same position like the currently clicked item. Initially having the same size, it will then expand first in width to fit the window and then in height:
loadImgPreview    = function( $item, callback ) {

var largeSrc  = $item.children('img').data('largesrc'),
description  = $item.children('span').text(),
largeImageData = {
src   : largeSrc,
description : description
};

// preload large image
$item.addClass('ib-loading');

preloadImage( largeSrc, function() {

$item.removeClass('ib-loading');

var hasImgPreview = ( $('#ib-img-preview').length > 0 );

if( !hasImgPreview )
$('#previewTmpl').tmpl( largeImageData ).insertAfter( $ibWrapper );
else
$('#ib-img-preview').children('img.ib-preview-img').attr( 'src', largeSrc );

//get dimentions for the image, based on the windows size
var dim = getImageDim( largeSrc );

$item.removeClass('ib-img-loading');

//set the returned values and show/animate preview
$('#ib-img-preview').css({
width : $item.width(),
height : $item.height(),
left : $item.offset().left,
top  : $item.offset().top
}).children('img.ib-preview-img').hide().css({
width : dim.width,
height : dim.height,
left : dim.left,
top  : dim.top
}).fadeIn( 400 ).end().show().animate({
width : $(window).width(),
left : 0
}, 500, 'easeOutExpo', function() {

$(this).animate({
height : $(window).height(),
top  : 0
}, 400, function() {

var $this = $(this);
$this.find('span.ib-preview-descr, span.ib-close').show()
if( imgItemsCount > 1 )
$this.find('div.ib-nav').show();

if( callback ) callback.call();

});

});

if( !hasImgPreview )
initImgPreviewEvents();

} );

},
With the same effect of expanding, we’ll open a content area, too. Here we have to take care of the text elements:
loadContentItem    = function( $item, callback ) {

var hasContentPreview = ( $('#ib-content-preview').length > 0 ),
teaser    = $item.children('div.ib-teaser').html(),
content    = $item.children('div.ib-content-full').html(),
contentData   = {
teaser  : teaser,
content  : content
};

if( !hasContentPreview )
$('#contentTmpl').tmpl( contentData ).insertAfter( $ibWrapper );

//set the returned values and show/animate preview
$('#ib-content-preview').css({
width : $item.width(),
height : $item.height(),
left : $item.offset().left,
top  : $item.offset().top
}).show().animate({
width : $(window).width(),
left : 0
}, 500, 'easeOutExpo', function() {

$(this).animate({
height : $(window).height(),
top  : 0
}, 400, function() {

var $this = $(this),
$teaser = $this.find('div.ib-teaser'),
$content= $this.find('div.ib-content-full'),
$close = $this.find('span.ib-close');

if( hasContentPreview ) {
$teaser.html( teaser )
$content.html( content )
}

$teaser.show();
$content.show();
$close.show();

if( callback ) callback.call();

});

});

if( !hasContentPreview )
initContentPreviewEvents(); 

},
A little helper function for preloading images:
// preloads an image
preloadImage    = function( src, callback ) {

$('<img/>').load(function(){

if( callback ) callback.call();

}).attr( 'src', src );

},
The next function takes care of loading the events for the large image preview : navigation, close button, and window resize:
initImgPreviewEvents  = function() {

var $preview = $('#ib-img-preview');

$preview.find('span.ib-nav-prev').bind('click.ibTemplate', function( event ) {

navigate( 'prev' );

}).end().find('span.ib-nav-next').bind('click.ibTemplate', function( event ) {

navigate( 'next' );

}).end().find('span.ib-close').bind('click.ibTemplate', function( event ) {

closeImgPreview();

});

//resizing the window resizes the preview image
$(window).bind('resize.ibTemplate', function( event ) {

var $largeImg = $preview.children('img.ib-preview-img'),
dim   = getImageDim( $largeImg.attr('src') );

$largeImg.css({
width : dim.width,
height : dim.height,
left : dim.left,
top  : dim.top
})

});

},
For the content preview we’ll also have to load the event for the closing functionality:
initContentPreviewEvents = function() {

$('#ib-content-preview').find('span.ib-close').bind('click.ibTemplate', function( event ) {

closeContentPreview();

});

},
Next, we’ll define the function that takes care of navigating through the large images in fullscreen:
// navigate the image items in fullscreen mode
navigate     = function( dir ) {

if( isAnimating ) return false;

isAnimating  = true;

var $preview = $('#ib-img-preview'),
$loading = $preview.find('div.ib-loading-large');

$loading.show();

if( dir === 'next' ) {

( current === imgItemsCount - 1 ) ? current = 0 : ++current;

}
else if( dir === 'prev' ) {

( current === 0 ) ? current = imgItemsCount - 1 : --current;

}

var $item  = $ibImgItems.eq( current ),
largeSrc = $item.children('img').data('largesrc'),
description = $item.children('span').text();

preloadImage( largeSrc, function() {

$loading.hide();

//get dimentions for the image, based on the windows size
var dim = getImageDim( largeSrc );

$preview.children('img.ib-preview-img')
.attr( 'src', largeSrc )
.css({
width : dim.width,
height : dim.height,
left : dim.left,
top  : dim.top
})
.end()
.find('span.ib-preview-descr')
.text( description );

$ibWrapper.scrollTop( $item.offset().top )
.scrollLeft( $item.offset().left );

isAnimating = false;

});

},
The following function is for closing the fullscreen image preview:
closeImgPreview    = function() {

if( isAnimating ) return false;

isAnimating = true;

var $item = $ibImgItems.eq( current );

$('#ib-img-preview').find('span.ib-preview-descr, div.ib-nav, span.ib-close')
.hide()
.end()
.animate({
height : $item.height(),
top  : $item.offset().top
}, 500, 'easeOutExpo', function() {

$(this).animate({
width : $item.width(),
left : $item.offset().left
}, 400, function() {

$(this).fadeOut(function() {isAnimating = false;});

} );

});

},
For the content preview we will also need such a function:
// closes the fullscreen content item
closeContentPreview   = function() {

if( isAnimating ) return false;

isAnimating = true;

var $item = $ibItems.not('.ib-image').eq( current );

$('#ib-content-preview').find('div.ib-teaser, div.ib-content-full, span.ib-close')
.hide()
.end()
.animate({
height : $item.height(),
top  : $item.offset().top
}, 500, 'easeOutExpo', function() {

$(this).animate({
width : $item.width(),
left : $item.offset().left
}, 400, function() {

$(this).fadeOut(function() {isAnimating = false;});

} );

});

},
getImageDim will get the size of an image and make it centered in fullscreen:
getImageDim     = function( src ) {

var img      = new Image();
img.src      = src;

var w_w = $(window).width(),
w_h = $(window).height(),
r_w = w_h / w_w,
i_w = img.width,
i_h = img.height,
r_i = i_h / i_w,
new_w, new_h,
new_left, new_top;

if( r_w > r_i ) {

new_h = w_h;
new_w = w_h / r_i;

}
else {

new_h = w_w * r_i;
new_w = w_w;

}

return {
width : new_w,
height : new_h,
left : (w_w - new_w) / 2,
top  : (w_h - new_h) / 2
};

};
And that’s it! I hope you enjoyed making this template with me and find it useful!
View demo Download source
Share and Enjoy:Diggdel.icio.usFacebookLinkedInRedditRSSTwitterGoogle BookmarksStumbleUponTechnoratiDZoneemailblogmarksDesign FloatDiigoFriendFeedGoogle BuzzIdenti.caMisterWongNewsVinePing.fmPlurkPosterousSlashdotSuggest to Techmeme via TwitterTumblr

Circle Navigation Effect with CSS3

Circle Navigation Effect with CSS3:
Circle Navigation Effect with CSS3
View demo Download source
Today we want to show you how to create a beautiful hover effect for an image navigation using CSS3. The idea is to expand a circular navigation with an arrow and make a bubble with a thumbnail appear. In our example we will be showing the thumbnail of the next and previous slider image on hovering the arrows. The effect is done with CSS3 transitions.
The beautiful images are by Andrey & Lili and they are licensed under the CC BY-NC 3.0 License.

The Markup

For this little CSS3 effect we will have a navigation structure that looks like the following:
<div class="cn-nav">
<a href="#" class="cn-nav-prev">
<span>Previous</span>
<div style="background-image:url(../images/thumbs/1.jpg);"></div>
</a>
<a href="#" class="cn-nav-next">
<span>Next</span>
<div style="background-image:url(../images/thumbs/3.jpg);"></div>
</a>
</div>
In our demo we will make a jQuery template out of this and dynamically add the thumbnails for the previous and next images of the slider. But to show you how the expanding circle effect is done, we will only show you this part.

The CSS

Let’s see now, how to add the style for this navigation.
Assuming that we have some relative surrounding container, we will set the link elements’ position to absolute. The height and width will be 70 pixels so that we have a not too small area for the hover effect:
.cn-nav > a{
position: absolute;
top: 0px;
height: 70px;
width: 70px;
}
a.cn-nav-prev{
left: 0px;
}
a.cn-nav-next{
right: 0px;
}
The span element, which will contain the arrows as a background image will have an initial height and width of 46 pixel. In order to make it look like a circle, we need to set the border radius to half of its width/height. With the 50% and negative margin trick, we set it into the center of the link element. Then we define the transition which will take all properties into account with a duration of 400ms and with ease as the easing function:
.cn-nav a span{
width: 46px;
height: 46px;
display: block;
text-indent: -9000px;
-moz-border-radius: 23px;
-webkit-border-radius: 23px;
border-radius: 23px;
cursor: pointer;
opacity: 0.9;
position: absolute;
top: 50%;
left: 50%;
background-size: 17px 25px;
margin: -23px 0 0 -23px;
-webkit-transition: all 0.4s ease;
-moz-transition: all 0.4s ease;
-o-transition: all 0.4s ease;
-ms-transition: all 0.4s ease;
transition: all 0.4s ease;
}
The spans’ background image (righ and left arrow):
.cn-nav a.cn-nav-prev span{
background: #666 url(../images/prev.png) no-repeat center center;
}
.cn-nav a.cn-nav-next span{
background: #666 url(../images/next.png) no-repeat center center;
}
The div with the thumbnail as background image is initially going to have a height and width of 0 pixel. It’s going to be absolute and also positioned in the center of the link element. Border radius and margins are going to be 0 initially. The background image will fill all the element, hence we will give the background-size of 100% width and height. The transition for this element will be for all properties with a duration of 200ms and with the ease-out easing function:
.cn-nav a div{
width: 0px;
height: 0px;
position: absolute;
top: 50%;
left: 50%;
overflow: hidden;
background-size: 100% 100%;
background-position: center center;
background-repeat: no-repeat;
margin: 0px;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
border-radius: 0px;
-webkit-transition: all 0.2s ease-out;
-moz-transition: all 0.2s ease-out;
-o-transition: all 0.2s ease-out;
-ms-transition: all 0.2s ease-out;
transition: all 0.2s ease-out;
}
Now, let’s define what the elements will look like on hover.
The span will increase to 100 pixels in width and height and accordingly, we will set the negative margins and the border radius to half of that. We’ll increase the size of the background image a little bit. Additionally, we’ll change the background color and the opacity:
.cn-nav a:hover span{
width: 100px;
height: 100px;
-moz-border-radius: 50px;
-webkit-border-radius: 50px;
border-radius: 50px;
opacity: 0.6;
margin: -50px 0 0 -50px;
background-size: 22px 32px;
background-color:#a8872d;
}
And finally, the little thumbnail div element will expand to 90 pixels, so that we can still see the span element around, appearing as a border to the thumb. We’ll also increase the background size a bit and set the negative margins and the border radius to half of the element’s width:
.cn-nav a:hover div{
width: 90px;
height: 90px;
background-size: 120% 120%;
margin: -45px 0 0 -45px;
-moz-border-radius: 45px;
-webkit-border-radius: 45px;
border-radius: 45px;
}
And that’s it! A slick effect with CSS3 only! Check out the demo to see how it looks integrated in a slider with showing the previous and next thumbail (with a modern browser, sorry IE9 is not included)! I hope you enjoyed this little tutorial and find it useful.
View demoDownload source
Share and Enjoy:Diggdel.icio.usFacebookLinkedInRedditRSSTwitterGoogle BookmarksStumbleUponTechnoratiDZoneemailblogmarksDesign FloatDiigoFriendFeedGoogle BuzzIdenti.caMisterWongNewsVinePing.fmPlurkPosterousSlashdotSuggest to Techmeme via TwitterTumblr