请注意,我们使用CSS 3d变换仅支持的现代浏览器。
实现代码
HTML
<div id="grid-gallery" class="grid-gallery"> <section class="grid-wrap"> <ul class="grid"> <li class="grid-sizer"></li><!-- for Masonry column width --> <li> <figure> <img src="img/thumb/1.png" alt="img01"/> <figcaption><h3>Letterpress asymmetrical</h3><p>Chillwave hoodie ea gentrify aute sriracha consequat.</p></figcaption> </figure> </li> <li> <figure> <img src="img/thumb/2.png" alt="img02"/> <figcaption><h3>Vice velit chia</h3><p>Laborum tattooed iPhone, Schlitz irure nulla Tonx retro 90's chia cardigan quis asymmetrical paleo. </p></figcaption> </figure> </li> <li> <figure> <img src="img/thumb/3.png" alt="img03"/> <figcaption><h3>Brunch semiotics</h3><p>Ex disrupt cray yr, butcher pour-over magna umami kitsch before they sold out commodo.</p></figcaption> </figure> </li> <li> <figure> <img src="img/thumb/4.png" alt="img04"/> <figcaption><h3>Chillwave nihil occupy</h3><p>In post-ironic gluten-free deserunt, PBR&B non pork belly cupidatat polaroid. </p></figcaption> </figure> </li> <li> <figure> <img src="img/thumb/5.png" alt="img05"/> <figcaption><h3>Kale chips lomo biodiesel</h3><p>Pariatur food truck street art consequat sustainable, et kogi beard qui paleo. </p></figcaption> </figure> </li> <li> <figure> <img src="img/thumb/6.png" alt="img06"/> <figcaption><h3>Exercitation occaecat</h3><p>Street chillwave hoodie ea gentrify.</p></figcaption> </figure> </li> </ul> </section><!-- // grid-wrap --> <section class="slideshow"> <ul> <li> <figure> <figcaption> <h3>Letterpress asymmetrical</h3> <p>Kale chips lomo biodiesel stumptown Godard Tumblr, mustache sriracha tattooed cray aute slow-carb placeat delectus. Letterpress asymmetrical fanny pack art party est pour-over skateboard anim quis, ullamco craft beer.</p> </figcaption> <img src="img/large/1.png" alt="img01"/> </figure> </li> <li> <figure> <figcaption> <h3>Vice velit chia</h3> <p>Chillwave Echo Park Etsy organic Cosby sweater seitan authentic pour-over. Occupy wolf selvage bespoke tattooed, cred sustainable Odd Future hashtag butcher.</p> </figcaption> <img src="img/large/2.png" alt="img02"/> </figure> </li> <li> <figure> <figcaption> <h3>Brunch semiotics</h3> <p>IPhone PBR polaroid before they sold out meh you probably haven't heard of them leggings tattooed tote bag, butcher paleo next level single-origin coffee photo booth.</p> </figcaption> <img src="img/large/3.png" alt="img03"/> </figure> </li> <li> <figure> <figcaption> <h3>Chillwave nihil occupy</h3> <p>Vice cliche locavore mumblecore vegan wayfarers asymmetrical letterpress hoodie mustache. Shabby chic lomo polaroid, scenester 8-bit Portland Pitchfork VHS tote bag.</p> </figcaption> <img src="img/large/4.png" alt="img04"/> </figure> </li> <li> <figure> <figcaption> <h3>Kale chips lomo biodiesel</h3> <p>Chambray Schlitz pug YOLO, PBR Tumblr semiotics. Flexitarian YOLO ennui Blue Bottle, forage dreamcatcher chillwave put a bird on it craft beer Etsy.</p> </figcaption> <img src="img/large/5.png" alt="img05"/> </figure> </li> <li> <figure> <figcaption> <h3>Exercitation occaecat</h3> <p>Cosby sweater hella lomo Thundercats VHS occupy High Life. Synth pop-up readymade single-origin coffee, fanny pack tousled retro. Fingerstache mlkshk ugh hashtag, church-key ethnic street art pug yr.</p> </figcaption> <img src="img/large/6.png" alt="img06"/> </figure> </li> </ul> <nav> <span class="icon nav-prev"></span> <span class="icon nav-next"></span> <span class="icon nav-close"></span> </nav> <div class="info-keys icon">Navigate with arrow keys</div> </section><!-- // slideshow --> </div><!-- // grid-gallery --> <script src="js/imagesloaded.pkgd.min.js"></script> <script src="js/masonry.pkgd.min.js"></script> <script src="js/classie.js"></script> <script src="js/cbpGridGallery.js"></script> <script> new CBPGridGallery( document.getElementById( 'grid-gallery' ) ); </script>
css
@font-face { font-family: 'fontawesome'; src:url('../fonts/fontawesome/fontawesome.eot?-e43dk9'); src:url('../fonts/fontawesome/fontawesome.eot?#iefix-e43dk9') format('embedded-opentype'), url('../fonts/fontawesome/fontawesome.woff?-e43dk9') format('woff'), url('../fonts/fontawesome/fontawesome.ttf?-e43dk9') format('truetype'), url('../fonts/fontawesome/fontawesome.svg?-e43dk9#fontawesome') format('svg'); font-weight: normal; font-style: normal; } /* /* General style */ .grid-gallery ul { list-style: none; margin: 0; padding: 0; } .grid-gallery figure { margin: 0; } .grid-gallery figure img { display: block; width: 100%; } .grid-gallery figcaption h3 { margin: 0; padding: 0 0 0.5em; } .grid-gallery figcaption p { margin: 0; } /* Grid style */ .grid-wrap { max-width: 69em; margin: 0 auto; padding: 0 1em 1.875em; } .grid { margin: 0 auto; } .grid li { width: 25%; float: left; cursor: pointer; } .grid figure { padding: 15px; -webkit-transition: opacity 0.2s; transition: opacity 0.2s; } .grid li:hover figure { opacity: 0.7; } .grid figcaption { background: #e4e4e4; padding: 25px; } /* Slideshow style */ .slideshow { position: fixed; background: rgba(0,0,0,0.6); width: 100%; height: 100%; top: 0; left: 0; z-index: 500; opacity: 0; visibility: hidden; overflow: hidden; -webkit-perspective: 1000px; perspective: 1000px; -webkit-transition: opacity 0.5s, visibility 0s 0.5s; transition: opacity 0.5s, visibility 0s 0.5s; } .slideshow-open .slideshow { opacity: 1; visibility: visible; -webkit-transition: opacity 0.5s; transition: opacity 0.5s; } .slideshow ul { width: 100%; height: 100%; -webkit-transform-style: preserve-3d; transform-style: preserve-3d; -webkit-transform: translate3d(0,0,150px); transform: translate3d(0,0,150px); -webkit-transition: -webkit-transform 0.5s; transition: transform 0.5s; } .slideshow ul.animatable li { -webkit-transition: -webkit-transform 0.5s; transition: transform 0.5s; } .slideshow-open .slideshow ul { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); } .slideshow li { width: 660px; height: 560px; position: absolute; top: 50%; left: 50%; margin: -280px 0 0 -330px; visibility: hidden; } .slideshow li.show { visibility: visible; } .slideshow li:after { content: ''; position: absolute; width: 100%; height: 100%; top: 0; left: 0; background: rgba(255,255,255,0.8); -webkit-transition: opacity 0.3s; transition: opacity 0.3s; } .slideshow li.current:after { visibility: hidden; opacity: 0; -webkit-transition: opacity 0.3s, visibility 0s 0.3s; transition: opacity 0.3s, visibility 0s 0.3s; } .slideshow figure { width: 100%; height: 100%; background: #fff; border: 50px solid #fff; overflow: hidden; } .slideshow figcaption { padding-bottom: 20px; } .slideshow figcaption h3 { font-weight: 300; font-size: 200%; } /* Navigation */ .slideshow nav span { position: fixed; z-index: 1000; color: #59656c; text-align: center; padding: 3%; cursor: pointer; font-size: 2.2em; } .slideshow nav span.nav-prev, .slideshow nav span.nav-next { top: 50%; -webkit-transform: translateY(-50%); transform: translateY(-50%); } .slideshow nav span.nav-next { right: 0; } .slideshow nav span.nav-close { top: 0; right: 0; padding: 0.5em 1em; color: #31373a; } .icon:before, .icon:after { font-family: 'fontawesome'; speak: none; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } span.nav-prev:before { content: "\e601"; } span.nav-next:before { content: "\e600"; } span.nav-close:before { content: "\e602"; } /* Info on arrow key navigation */ .info-keys { position: fixed; top: 10px; left: 10px; width: 60px; font-size: 8px; padding-top: 20px; text-transform: uppercase; color: #fff; letter-spacing: 1px; text-align: center; } .info-keys:before, .info-keys:after { position: absolute; top: 0; width: 16px; height: 16px; border: 1px solid #fff; text-align: center; line-height: 14px; font-size: 12px; } .info-keys:before { left: 10px; content: "\e603"; } .info-keys:after { right: 10px; content: "\e604"; } /* Example media queries (reduce number of columns and change slideshow layout) */ @media screen and (max-width: 60em) { /* responsive columns; see "Element sizing" on http://masonry.desandro.com/options.html */ .grid li { width: 33.3%; } .slideshow li { width: 100%; height: 100%; top: 0; left: 0; margin: 0; } .slideshow li figure img { width: auto; margin: 0 auto; max-width: 100%; } .slideshow nav span, .slideshow nav span.nav-close { font-size: 1.8em; padding: 0.3em; } .info-keys { display: none; } } @media screen and (max-width: 35em) { .grid li { width: 50%; } } @media screen and (max-width: 24em) { .grid li { width: 100%; } }
JAVASCRIPT
( function( window ) { 'use strict'; var docElem = window.document.documentElement, transEndEventNames = { 'WebkitTransition': 'webkitTransitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd', 'msTransition': 'MSTransitionEnd', 'transition': 'transitionend' }, transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ], support = { transitions : Modernizr.csstransitions, support3d : Modernizr.csstransforms3d }; function setTransform( el, transformStr ) { el.style.WebkitTransform = transformStr; el.style.msTransform = transformStr; el.style.transform = transformStr; } // from http://responsejs.com/labs/dimensions/ function getViewportW() { var client = docElem['clientWidth'], inner = window['innerWidth']; if( client < inner ) return inner; else return client; } function extend( a, b ) { for( var key in b ) { if( b.hasOwnProperty( key ) ) { a[key] = b[key]; } } return a; } function CBPGridGallery( el, options ) { this.el = el; this.options = extend( {}, this.options ); extend( this.options, options ); this._init(); } CBPGridGallery.prototype.options = { }; CBPGridGallery.prototype._init = function() { // main grid this.grid = this.el.querySelector( 'section.grid-wrap > ul.grid' ); // main grid items this.gridItems = [].slice.call( this.grid.querySelectorAll( 'li:not(.grid-sizer)' ) ); // items total this.itemsCount = this.gridItems.length; // slideshow grid this.slideshow = this.el.querySelector( 'section.slideshow > ul' ); // slideshow grid items this.slideshowItems = [].slice.call( this.slideshow.children ); // index of current slideshow item this.current = -1; // slideshow control buttons this.ctrlPrev = this.el.querySelector( 'section.slideshow > nav > span.nav-prev' ); this.ctrlNext = this.el.querySelector( 'section.slideshow > nav > span.nav-next' ); this.ctrlClose = this.el.querySelector( 'section.slideshow > nav > span.nav-close' ); // init masonry grid this._initMasonry(); // init events this._initEvents(); }; CBPGridGallery.prototype._initMasonry = function() { var grid = this.grid; imagesLoaded( grid, function() { new Masonry( grid, { itemSelector: 'li', columnWidth: grid.querySelector( '.grid-sizer' ) }); }); }; CBPGridGallery.prototype._initEvents = function() { var self = this; // open the slideshow when clicking on the main grid items this.gridItems.forEach( function( item, idx ) { item.addEventListener( 'click', function() { self._openSlideshow( idx ); } ); } ); // slideshow controls this.ctrlPrev.addEventListener( 'click', function() { self._navigate( 'prev' ); } ); this.ctrlNext.addEventListener( 'click', function() { self._navigate( 'next' ); } ); this.ctrlClose.addEventListener( 'click', function() { self._closeSlideshow(); } ); // window resize window.addEventListener( 'resize', function() { self._resizeHandler(); } ); // keyboard navigation events document.addEventListener( 'keydown', function( ev ) { if ( self.isSlideshowVisible ) { var keyCode = ev.keyCode || ev.which; switch (keyCode) { case 37: self._navigate( 'prev' ); break; case 39: self._navigate( 'next' ); break; case 27: self._closeSlideshow(); break; } } } ); // trick to prevent scrolling when slideshow is visible window.addEventListener( 'scroll', function() { if ( self.isSlideshowVisible ) { window.scrollTo( self.scrollPosition ? self.scrollPosition.x : 0, self.scrollPosition ? self.scrollPosition.y : 0 ); } else { self.scrollPosition = { x : window.pageXOffset || docElem.scrollLeft, y : window.pageYOffset || docElem.scrollTop }; } }); }; CBPGridGallery.prototype._openSlideshow = function( pos ) { this.isSlideshowVisible = true; this.current = pos; classie.addClass( this.el, 'slideshow-open' ); /* position slideshow items */ // set viewport items (current, next and previous) this._setViewportItems(); // add class "current" and "show" to currentItem classie.addClass( this.currentItem, 'current' ); classie.addClass( this.currentItem, 'show' ); // add class show to next and previous items // position previous item on the left side and the next item on the right side if( this.prevItem ) { classie.addClass( this.prevItem, 'show' ); var translateVal = Number( -1 * ( getViewportW() / 2 + this.prevItem.offsetWidth / 2 ) ); setTransform( this.prevItem, support.support3d ? 'translate3d(' + translateVal + 'px, 0, -150px)' : 'translate(' + translateVal + 'px)' ); } if( this.nextItem ) { classie.addClass( this.nextItem, 'show' ); var translateVal = Number( getViewportW() / 2 + this.nextItem.offsetWidth / 2 ); setTransform( this.nextItem, support.support3d ? 'translate3d(' + translateVal + 'px, 0, -150px)' : 'translate(' + translateVal + 'px)' ); } }; CBPGridGallery.prototype._navigate = function( dir ) { if( this.isAnimating ) return; if( dir === 'next' && this.current === this.itemsCount - 1 || dir === 'prev' && this.current === 0 ) { this._closeSlideshow(); return; } this.isAnimating = true; // reset viewport items this._setViewportItems(); var self = this, itemWidth = this.currentItem.offsetWidth, // positions for the centered/current item, both the side items and the incoming ones transformLeftStr = support.support3d ? 'translate3d(-' + Number( getViewportW() / 2 + itemWidth / 2 ) + 'px, 0, -150px)' : 'translate(-' + Number( getViewportW() / 2 + itemWidth / 2 ) + 'px)', transformRightStr = support.support3d ? 'translate3d(' + Number( getViewportW() / 2 + itemWidth / 2 ) + 'px, 0, -150px)' : 'translate(' + Number( getViewportW() / 2 + itemWidth / 2 ) + 'px)', transformCenterStr = '', transformOutStr, transformIncomingStr, // incoming item incomingItem; if( dir === 'next' ) { transformOutStr = support.support3d ? 'translate3d( -' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px, 0, -150px )' : 'translate(-' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px)'; transformIncomingStr = support.support3d ? 'translate3d( ' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px, 0, -150px )' : 'translate(' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px)'; } else { transformOutStr = support.support3d ? 'translate3d( ' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px, 0, -150px )' : 'translate(' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px)'; transformIncomingStr = support.support3d ? 'translate3d( -' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px, 0, -150px )' : 'translate(-' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px)'; } // remove class animatable from the slideshow grid (if it has already) classie.removeClass( self.slideshow, 'animatable' ); if( dir === 'next' && this.current < this.itemsCount - 2 || dir === 'prev' && this.current > 1 ) { // we have an incoming item! incomingItem = this.slideshowItems[ dir === 'next' ? this.current + 2 : this.current - 2 ]; setTransform( incomingItem, transformIncomingStr ); classie.addClass( incomingItem, 'show' ); } var slide = function() { // add class animatable to the slideshow grid classie.addClass( self.slideshow, 'animatable' ); // overlays: classie.removeClass( self.currentItem, 'current' ); var nextCurrent = dir === 'next' ? self.nextItem : self.prevItem; classie.addClass( nextCurrent, 'current' ); setTransform( self.currentItem, dir === 'next' ? transformLeftStr : transformRightStr ); if( self.nextItem ) { setTransform( self.nextItem, dir === 'next' ? transformCenterStr : transformOutStr ); } if( self.prevItem ) { setTransform( self.prevItem, dir === 'next' ? transformOutStr : transformCenterStr ); } if( incomingItem ) { setTransform( incomingItem, dir === 'next' ? transformRightStr : transformLeftStr ); } var onEndTransitionFn = function( ev ) { if( support.transitions ) { if( ev.propertyName.indexOf( 'transform' ) === -1 ) return false; this.removeEventListener( transEndEventName, onEndTransitionFn ); } if( self.prevItem && dir === 'next' ) { classie.removeClass( self.prevItem, 'show' ); } else if( self.nextItem && dir === 'prev' ) { classie.removeClass( self.nextItem, 'show' ); } if( dir === 'next' ) { self.prevItem = self.currentItem; self.currentItem = self.nextItem; if( incomingItem ) { self.nextItem = incomingItem; } } else { self.nextItem = self.currentItem; self.currentItem = self.prevItem; if( incomingItem ) { self.prevItem = incomingItem; } } self.current = dir === 'next' ? self.current + 1 : self.current - 1; self.isAnimating = false; }; if( support.transitions ) { self.currentItem.addEventListener( transEndEventName, onEndTransitionFn ); } else { onEndTransitionFn(); } }; setTimeout( slide, 25 ); } CBPGridGallery.prototype._closeSlideshow = function( pos ) { // remove class slideshow-open from the grid gallery elem classie.removeClass( this.el, 'slideshow-open' ); // remove class animatable from the slideshow grid classie.removeClass( this.slideshow, 'animatable' ); var self = this, onEndTransitionFn = function( ev ) { if( support.transitions ) { if( ev.target.tagName.toLowerCase() !== 'ul' ) return; this.removeEventListener( transEndEventName, onEndTransitionFn ); } // remove classes show and current from the slideshow items classie.removeClass( self.currentItem, 'current' ); classie.removeClass( self.currentItem, 'show' ); if( self.prevItem ) { classie.removeClass( self.prevItem, 'show' ); } if( self.nextItem ) { classie.removeClass( self.nextItem, 'show' ); } // also reset any transforms for all the items self.slideshowItems.forEach( function( item ) { setTransform( item, '' ); } ); self.isSlideshowVisible = false; }; if( support.transitions ) { this.el.addEventListener( transEndEventName, onEndTransitionFn ); } else { onEndTransitionFn(); } }; CBPGridGallery.prototype._setViewportItems = function() { this.currentItem = null; this.prevItem = null; this.nextItem = null; if( this.current > 0 ) { this.prevItem = this.slideshowItems[ this.current - 1 ]; } if( this.current < this.itemsCount - 1 ) { this.nextItem = this.slideshowItems[ this.current + 1 ]; } this.currentItem = this.slideshowItems[ this.current ]; } // taken from https://github.com/desandro/vanilla-masonry/blob/master/masonry.js by David DeSandro // original debounce by John Hann // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/ CBPGridGallery.prototype._resizeHandler = function() { var self = this; function delayed() { self._resize(); self._resizeTimeout = null; } if ( this._resizeTimeout ) { clearTimeout( this._resizeTimeout ); } this._resizeTimeout = setTimeout( delayed, 50 ); } CBPGridGallery.prototype._resize = function() { &n