本教程主要介绍类似谷歌图片搜索中网格缩略图的预览功能的实现。如果你最近使用过google的图片搜索功能,相信你一定注意到了当你点击缩略图的时候,旁边会非常有趣的出现预览的大图。这是一个非常不错并且实用的效果。今天我们将要想你展示如何实现类似效果的网状缩略图。我们的想法是当点击一个缩略图时显示一个较大的图像和一些其他的内容如标题,描述和链接。
计算预览图像的高度和页面应滚动到的位置是一个有趣的事情。我们将显示当前所点击缩略图那一行图片,并且用页面其他的空间显示稍大预览图像。因为我们不使用非常大的预览图,所以你可能会在页面看到很多空白空间。演示中的一些非常棒的图片是由Jaime Martinez制作的。
ok,让我们开始教程吧。
HTML标签
我们将使用无序列表ul实现网状缩略图集合。每一个li都将有一个包含一些特定属性的超链接:
超链接中的href值用来给预览图中的链接地址使用(当然要用javascript禁止超链接的默认跳转行为),data-largesrc属性值是预览模块中大图的地址,data-title和data-description是标题和描述。
当点击一个缩略图的时候将在他的下面出现它的预览模块,为此我们需要向网状缩略图集合中添加一个div。我们就把它加到上面提到的超链接后面。
到这里页面的html部分就完成了。
CSS
注意下面的css不会出现任何前缀,在下载的代码中你会看到那些前缀。
我们将ul的宽度设置成100%,并且使里面的缩略图居中对齐,所以需要设置display: inline-block
.og-grid {
list-style: none;
padding: 20px 0;
margin: 0 auto;
text-align: center;
width: 100%;
}
.og-grid li {
display: inline-block;
margin: 10px 5px 0 5px;
vertical-align: top;
height: 250px;
}
去除一些默认的设置,并让超链接和图片以块结构显示
.og-grid li > a,
.og-grid li > a img {
border: none;
outline: none;
display: block;
position: relative;
}
当单击一个缩略图的时候,给当前的li添加一个og-expanded的class,我们将会给超链接添加一个小三角的指示器。
.og-grid li.og-expanded > a::after {
top: auto;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-bottom-color: #ddd;
border-width: 15px;
left: 50%;
margin: -20px 0 0 -15px;
}
同时给预览模块div添加一个class og-expander,使他绝对定位。div的初始高度为0,并且设置成隐藏超出内容overflow:hidden.
.og-expander {
position: absolute;
background: #ddd;
top: auto;
left: 0;
width: 100%;
margin-top: 10px;
text-align: left;
height: 0;
overflow: hidden;
}
.og-expander-inner {
padding: 50px 30px;
height: 100%;
}
预览模块的关闭按钮将使用伪元素,两个旋转的线。
.og-close {
position: absolute;
width: 40px;
height: 40px;
top: 20px;
right: 20px;
cursor: pointer;
}
.og-close::before,
.og-close::after {
content: ”;
position: absolute;
width: 100%;
top: 50%;
height: 1px;
background: #888;
transform: rotate(45deg);
}
.og-close::after {
transform: rotate(-45deg);
}
.og-close:hover::before,
.og-close:hover::after {
background: #333;
}
预览大图和旁边的标题描述并排各占50%的宽度:
.og-fullimg,
.og-details {
width: 50%;
float: left;
height: 100%;
overflow: hidden;
position: relative;
}
为了使图片的大小随着容器变化设置图片的最大宽高为100%:
.og-details {
padding: 0 40px 0 20px;
}
.og-fullimg {
text-align: center;
}
.og-fullimg img {
display: inline-block;
max-height: 100%;
max-width: 100%;
}
下面是文字部分的css:
.og-details h3 {
font-weight: 300;
font-size: 52px;
padding: 40px 0 10px;
margin-bottom: 10px;
}
.og-details p {
font-weight: 400;
font-size: 16px;
line-height: 22px;
color: #999;
}
.og-details a {
font-weight: 700;
font-size: 16px;
color: #333;
text-transform: uppercase;
letter-spacing: 2px;
padding: 10px 20px;
border: 3px solid #333;
display: inline-block;
margin: 30px 0 0;
outline: none;
}
.og-details a::before {
content: ‘\2192′;
display: inline-block;
margin-right: 10px;
}
.og-details a:hover {
border-color: #999;
color: #999;
}
加载预览大图的动画和图片放在一个容器中。不使用图片仅仅用css实现加载动画。我们将创建一个小圆圈,并设置三个框阴影:一个小圆本身看起来更加平滑一些,另外两个用于“复制”的元素。然后我们创建一个动画,有序的改变背景颜色和框阴影的颜色。
.og-loading {
width: 20px;
height: 20px;
border-radius: 50%;
background: #ddd;
box-shadow: 0 0 1px #ccc, 15px 30px 1px #ccc, -15px 30px 1px #ccc;
position: absolute;
top: 50%;
left: 50%;
margin: -25px 0 0 -25px;
animation: loader 0.5s infinite ease-in-out both;
}
@keyframes loader {
0% { background: #ddd; }
33% { background: #ccc; box-shadow: 0 0 1px #ccc, 15px 30px 1px #ccc, -15px 30px 1px #ddd; }
66% { background: #ccc; box-shadow: 0 0 1px #ccc, 15px 30px 1px #ddd, -15px 30px 1px #ccc; }
}
最后添加两个media查询,网页大小改变的时候改变字体并且隐藏预览大图模块:
@media screen and (max-width: 830px) {
.og-expander h3 { font-size: 32px; }
.og-expander p { font-size: 13px; }
.og-expander a { font-size: 12px; }
}
@media screen and (max-width: 650px) {
.og-fullimg { display: none; }
.og-details { float: none; width: 100%; }
}
css就这么多了,接下来看看javascript
JavaScript
首先定义一些变量,并初始化一些值。
// list of items
var $grid = $( '#og-grid' ),
// the items
$items = $grid.children( 'li' ),
// current expanded item´s index
current = -1,
// position (top) of the expanded item
// used to know if the preview will expand in a different row
previewPos = -1,
// extra amount of pixels to scroll the window
scrollExtra = 0,
// extra margin when expanded (between the preview element and the next item row)
marginExpanded = 10,
$window = $( window ), winsize,
$body = $( 'html, body' ),
// transitionend events
transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd',
'MozTransition' : 'transitionend',
'OTransition' : 'oTransitionEnd',
'msTransition' : 'MSTransitionEnd',
'transition' : 'transitionend'
},
transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ],
// support for csstransitions
support = Modernizr.csstransitions,
// default settings
settings = {
minHeight : 500,
speed : 350,
easing : 'ease'
};
在缩略图加载完成之后,保存每个li的offsetTop和height,计算当前窗口的大小,并且初始化一些事件。
function init( config ) {
// the settings..
settings = $.extend( true, {}, settings, config );
// preload all images
$grid.imagesLoaded( function() {
// save item´s size and offset
saveItemInfo( true );
// get window´s size
getWinSize();
// initialize some events
initEvents();
} );
}
// saves the item´s offset top and height (if saveheight is true)
function saveItemInfo( saveheight ) {
$items.each( function() {
var $item = $( this );
$item.data( ‘offsetTop’, $item.offset().top );
if( saveheight ) {
$item.data( ‘height’, $item.height() );
}
} );
}
function getWinSize() {
winsize = { width : $window.width(), height : $window.height() };
}
我们将在initEvents()中绑定为每个超链接和关闭按钮(当预览div打开的时候)绑定click事件。当预览大图模块div已经显示的时候,点击缩略图隐藏预览大图模块di,点击关闭按钮的时候同样关闭预览。当页面的大小发生变化的时候,预览也会关闭。
function initEvents() {
// when clicking an item, show the preview with the item´s info and large image;
// close the item if already expanded.
// also close if clicking on the item´s cross
$items.on( ‘click’, ‘span.og-close’, function() {
hidePreview();
return false;
} ).children( ‘a’ ).on( ‘click’, function(e) {
var $item = $( this ).parent();
// check if item already opened
current === $item.index() ? hidePreview() : showPreview( $item );
return false;
} );
// on window resize get the window´s size again
// reset some values..
$window.on( ‘debouncedresize’, function() {
scrollExtra = 0;
previewPos = -1;
// save item´s offset
saveItemInfo();
getWinSize();
var preview = $.data( this, ‘preview’ );
if( typeof preview != ‘undefined’ ) {
hidePreview();
}
} );
}
showPreview()实现了显示预览大图的功能。当点击一个缩略图的时候,如果当前行的缩略图没有显示预览的则关闭其他行的(如果其他行有显示大图预览的),显示自己的大图预览div,如果当前行有显示预览的则替换为当前点击的缩略图的预览信息。我们使用每个item的offset top判断是不是在同一行。
function showPreview( $item ) {
var preview = $.data( this, ‘preview’ ),
// item´s offset top
position = $item.data( ‘offsetTop’ );
scrollExtra = 0;
// if a preview exists and previewPos is different (different row) from item´s top, then close it
if( typeof preview != ‘undefined’ ) {
// not in the same row
if( previewPos !== position ) {
// if position > previewPos then we need to take the current preview´s height in consideration when scrolling the window
if( position > previewPos ) {
scrollExtra = preview.height;
}
hidePreview();
}
// same row
else {
preview.update( $item );
return false;
}
}
// update previewPos
previewPos = position;
// initialize new preview for the clicked item
preview = $.data( this, ‘preview’, new Preview( $item ) );
// expand preview overlay
preview.open();
}
Preview有一个属性$item(存储当前显示的li),还有一个属性expandedIdx(存储当前的展开的预览div)。注意当前显示的li不一定是对应的预览展开的div,例如,当一个缩略图的预览模块已经展开了,这时候我们点击和这个缩略图同一行的缩略图,这时候使用的预览模块div还是以前的那个div,我们只是去更新里面的内容。Preview.expandedIdx不是新的缩略图li的index,而是以前的,所以当关闭预览的时候,我们知道去关闭哪一个。
// the preview obj / overlay
function Preview( $item ) {
this.$item = $item;
this.expandedIdx = this.$item.index();
this.create();
this.update();
}
当Preview创建的时候,会初始化li里面的内容,构造一些html包含标题,描述和预览图。并把这些添加到当前的li里面。
create : function() {
// create Preview structure:
this.$title = $( '
‘ );
this.$description = $( ‘
‘ );
this.$href = $( ‘Visit website‘ );
this.$details = $( ‘
‘ ).append( this.$title, this.$description, this.$href );
this.$loading = $( ‘
‘ );
this.$fullimage = $( ‘
‘ ).append( this.$loading );
this.$closePreview = $( ” );
this.$previewInner = $( ‘
‘ ).append( this.$closePreview, this.$fullimage, this.$details );
this.$previewEl = $( ‘
‘ ).append( this.$previewInner );
// append preview element to the item
this.$item.append( this.getEl() );
// set the transitions for the preview and the item
if( support ) {
this.setTransition();
}
}
我们用超链接的data属性值填充上面构造的html里面的内容。
update : function( $item ) {
// update with new item´s details
if( $item ) {
this.$item = $item;
}
// if already expanded, remove class “og-expanded” from current item and add it to new item
if( current !== -1 ) {
var $currentItem = $items.eq( current );
$currentItem.removeClass( ‘og-expanded’ );
this.$item.addClass( ‘og-expanded’ );
// position the preview correctly
this.positionPreview();
}
// update current value
current = this.$item.index();
// update preview´s content
var $itemEl = this.$item.children( ‘a’ ),
eldata = {
href : $itemEl.attr( ‘href’ ),
largesrc : $itemEl.data( ‘largesrc’ ),
title : $itemEl.data( ‘title’ ),
description : $itemEl.data( ‘description’ )
};
this.$title.html( eldata.title );
this.$description.html( eldata.description );
this.$href.attr( ‘href’, eldata.href );
var self = this;
// remove the current image in the preview
if( typeof self.$largeImg != ‘undefined’ ) {
self.$largeImg.remove();
}
// preload large image and add it to the preview
// for smaller screens we don´t display the large image (the last media query will hide the wrapper of the image)
if( self.$fullimage.is( ‘:visible’ ) ) {
this.$loading.show();
$( ‘‘ ).load( function() {
self.$loading.hide();
self.$largeImg = $( this ).fadeIn( 350 );
self.$fullimage.append( self.$largeImg );
} ).attr( ‘src’, eldata.largesrc );
}
}
为了显示预览图片需要设置预览div的高度和li的高度(这样可以使下面一行缩略图向下移动)。预览div的高度等于当前窗口的高度减去缩略图li的高度。为了避免高度太小我们为预览div添加minHeight属性。当预览div打开的时候,我们将滚动网页的内容,让预览图片全部显示出来。
open : function() {
setTimeout( $.proxy( function() {
// set the height for the preview and the item
this.setHeights();
// scroll to position the preview in the right place
this.positionPreview();
}, this ), 25 );
}
setHeights : function() {
var self = this,
onEndFn = function() {
if( support ) {
self.$item.off( transEndEventName );
}
self.$item.addClass( ‘og-expanded’ );
};
this.calcHeight();
this.$previewEl.css( ‘height’, this.height );
this.$item.css( ‘height’, this.itemHeight ).on( transEndEventName, onEndFn );
if( !support ) {
onEndFn.call();
}
}
calcHeight : function() {
var heightPreview = winsize.height – this.$item.data( ‘height’ ) – marginExpanded,
itemHeight = winsize.height;
if( heightPreview < settings.minHeight ) {
heightPreview = settings.minHeight;
itemHeight = settings.minHeight + this.$item.data( ‘height’ ) + marginExpanded;
}
this.height = heightPreview;
this.itemHeight = itemHeight;
}
positionPreview : function() {
// scroll page
// case 1 : preview height + item height fits in window´s height
// case 2 : preview height + item height does not fit in window´s height and preview height is smaller than window´s height
// case 3 : preview height + item height does not fit in window´s height and preview height is bigger than window´s height
var position = this.$item.data( ‘offsetTop’ ),
previewOffsetT = this.$previewEl.offset().top – scrollExtra,
scrollVal = this.height + this.$item.data( ‘height’ ) + marginExpanded <= winsize.height ? position : this.height < winsize.height ? previewOffsetT – ( winsize.height – this.height ) : previewOffsetT;
$body.animate( { scrollTop : scrollVal }, settings.speed );
}
在关闭预览之后,我们将恢复li和预览div高度。同时从DOM中移除掉我们构造的那些html。
close : function() {
var self = this,
onEndFn = function() {
if( support ) {
$( this ).off( transEndEventName );
}
self.$item.removeClass( ‘og-expanded’ );
self.$previewEl.remove();
};
setTimeout( $.proxy( function() {
if( typeof this.$largeImg !== ‘undefined’ ) {
this.$largeImg.fadeOut( ‘fast’ );
}
this.$previewEl.css( ‘height’, 0 );
// the current expanded item (might be different from this.$item)
var $expandedItem = $items.eq( this.expandedIdx );
$expandedItem.css( ‘height’, $expandedItem.data( ‘height’ ) ).on( transEndEventName, onEndFn );
if( !support ) {
onEndFn.call();
}
}, this ), 25 );
return false;
}
教程到此结束,希望你能够喜欢。
TYStudio翻译,转载请注明出住,谢谢。英文原版:THUMBNAIL GRID WITH EXPANDING PREVIEW