From 5c5dac452bf345662383c2c17d5ab1cec68a8f52 Mon Sep 17 00:00:00 2001 From: Victor Häggqvist Date: Mon, 9 Jun 2014 16:16:31 +0200 Subject: the stuff --- js/imagelightbox.js | 307 ++++++++++++++++++++++++++++++++++++++++++++++++ sass/imagelightbox.scss | 216 ++++++++++++++++++++++++++++++++++ 2 files changed, 523 insertions(+) create mode 100644 js/imagelightbox.js create mode 100644 sass/imagelightbox.scss diff --git a/js/imagelightbox.js b/js/imagelightbox.js new file mode 100644 index 0000000..ad46eb1 --- /dev/null +++ b/js/imagelightbox.js @@ -0,0 +1,307 @@ +;( function( $, window, document, undefined ) +{ + 'use strict'; + + var cssTransitionSupport = function() + { + var s = document.body || document.documentElement, s = s.style; + if( s.WebkitTransition == '' ) return '-webkit-'; + if( s.MozTransition == '' ) return '-moz-'; + if( s.OTransition == '' ) return '-o-'; + if( s.transition == '' ) return ''; + return false; + }, + + isCssTransitionSupport = cssTransitionSupport() === false ? false : true, + + cssTransitionTranslateX = function( element, positionX, speed ) + { + var options = {}, prefix = cssTransitionSupport(); + options[ prefix + 'transform' ] = 'translateX(' + positionX + ')'; + options[ prefix + 'transition' ] = prefix + 'transform ' + speed + 's linear'; + element.css( options ); + }, + + hasTouch = ( 'ontouchstart' in window ), + hasPointers = window.navigator.pointerEnabled || window.navigator.msPointerEnabled, + wasTouched = function( event ) + { + if( hasTouch ) + return true; + + if( !hasPointers || typeof event === 'undefined' || typeof event.pointerType === 'undefined' ) + return false; + + if( typeof event.MSPOINTER_TYPE_MOUSE !== 'undefined' ) + { + if( event.MSPOINTER_TYPE_MOUSE != event.pointerType ) + return true; + } + else + if( event.pointerType != 'mouse' ) + return true; + + return false; + }; + + $.fn.imageLightbox = function( options ) + { + var options = $.extend( + { + selector: 'id="imagelightbox"', + allowedTypes: 'png|jpg|jpeg|gif', + animationSpeed: 250, + preloadNext: true, + enableKeyboard: true, + quitOnEnd: false, + quitOnImgClick: false, + quitOnDocClick: true, + onStart: false, + onEnd: false, + onLoadStart: false, + onLoadEnd: false + }, + options ), + + targets = $([]), + target = $(), + image = $(), + imageWidth = 0, + imageHeight = 0, + swipeDiff = 0, + inProgress = false, + + isTargetValid = function( element ) + { + return $( element ).prop( 'tagName' ).toLowerCase() == 'a' && ( new RegExp( '\.(' + options.allowedTypes + ')$', 'i' ) ).test( $( element ).attr( 'href' ) ); + }, + + setImage = function() + { + if( !image.length ) return false; + + var screenWidth = $( window ).width() * 0.8, + screenHeight = $( window ).height() * 0.9, + tmpImage = new Image(); + + tmpImage.src = image.attr( 'src' ); + tmpImage.onload = function() + { + imageWidth = tmpImage.width; + imageHeight = tmpImage.height; + + if( imageWidth > screenWidth || imageHeight > screenHeight ) + { + var ratio = imageWidth / imageHeight > screenWidth / screenHeight ? imageWidth / screenWidth : imageHeight / screenHeight; + imageWidth /= ratio; + imageHeight /= ratio; + } + + image.css( + { + 'width': imageWidth + 'px', + 'height': imageHeight + 'px', + 'top': ( $( window ).height() - imageHeight ) / 2 + 'px', + 'left': ( $( window ).width() - imageWidth ) / 2 + 'px' + }); + }; + }, + + loadImage = function( direction ) + { + if( inProgress ) return false; + + direction = typeof direction === 'undefined' ? false : direction == 'left' ? 1 : -1; + + if( image.length ) + { + if( direction !== false && ( targets.length < 2 || ( options.quitOnEnd === true && ( ( direction === -1 && targets.index( target ) == 0 ) || ( direction === 1 && targets.index( target ) == targets.length - 1 ) ) ) ) ) + { + quitLightbox(); + return false; + } + var params = { 'opacity': 0 }; + if( isCssTransitionSupport ) cssTransitionTranslateX( image, ( 100 * direction ) - swipeDiff + 'px', options.animationSpeed / 1000 ); + else params.left = parseInt( image.css( 'left' ) ) + 100 * direction + 'px'; + image.animate( params, options.animationSpeed, function(){ removeImage(); }); + swipeDiff = 0; + } + + inProgress = true; + if( options.onLoadStart !== false ) options.onLoadStart(); + + setTimeout( function() + { + image = $( '' ) + .attr( 'src', target.attr( 'href' ) ) + .load( function() + { + image.appendTo( 'body' ); + setImage(); + + var params = { 'opacity': 1 }; + + image.css( 'opacity', 0 ); + if( isCssTransitionSupport ) + { + cssTransitionTranslateX( image, -100 * direction + 'px', 0 ); + setTimeout( function(){ cssTransitionTranslateX( image, 0 + 'px', options.animationSpeed / 1000 ) }, 50 ); + } + else + { + var imagePosLeft = parseInt( image.css( 'left' ) ); + params.left = imagePosLeft + 'px'; + image.css( 'left', imagePosLeft - 100 * direction + 'px' ); + } + + image.animate( params, options.animationSpeed, function() + { + inProgress = false; + if( options.onLoadEnd !== false ) options.onLoadEnd(); + }); + if( options.preloadNext ) + { + var nextTarget = targets.eq( targets.index( target ) + 1 ); + if( !nextTarget.length ) nextTarget = targets.eq( 0 ); + $( '' ).attr( 'src', nextTarget.attr( 'href' ) ).load(); + } + }) + .error( function() + { + if( options.onLoadEnd !== false ) options.onLoadEnd(); + }) + + var swipeStart = 0, + swipeEnd = 0, + imagePosLeft = 0; + + image.on( hasPointers ? 'pointerup MSPointerUp' : 'click', function( e ) + { + e.preventDefault(); + if( options.quitOnImgClick ) + { + quitLightbox(); + return false; + } + if( wasTouched( e.originalEvent ) ) return true; + var posX = ( e.pageX || e.originalEvent.pageX ) - e.target.offsetLeft; + target = targets.eq( targets.index( target ) - ( imageWidth / 2 > posX ? 1 : -1 ) ); + if( !target.length ) target = targets.eq( imageWidth / 2 > posX ? targets.length : 0 ); + loadImage( imageWidth / 2 > posX ? 'left' : 'right' ); + }) + .on( 'touchstart pointerdown MSPointerDown', function( e ) + { + if( !wasTouched( e.originalEvent ) || options.quitOnImgClick ) return true; + if( isCssTransitionSupport ) imagePosLeft = parseInt( image.css( 'left' ) ); + swipeStart = e.originalEvent.pageX || e.originalEvent.touches[ 0 ].pageX; + }) + .on( 'touchmove pointermove MSPointerMove', function( e ) + { + if( !wasTouched( e.originalEvent ) || options.quitOnImgClick ) return true; + e.preventDefault(); + swipeEnd = e.originalEvent.pageX || e.originalEvent.touches[ 0 ].pageX; + swipeDiff = swipeStart - swipeEnd; + if( isCssTransitionSupport ) cssTransitionTranslateX( image, -swipeDiff + 'px', 0 ); + else image.css( 'left', imagePosLeft - swipeDiff + 'px' ); + }) + .on( 'touchend touchcancel pointerup MSPointerUp', function( e ) + { + if( !wasTouched( e.originalEvent ) || options.quitOnImgClick ) return true; + if( Math.abs( swipeDiff ) > 50 ) + { + target = targets.eq( targets.index( target ) - ( swipeDiff < 0 ? 1 : -1 ) ); + if( !target.length ) target = targets.eq( swipeDiff < 0 ? targets.length : 0 ); + loadImage( swipeDiff > 0 ? 'right' : 'left' ); + } + else + { + if( isCssTransitionSupport ) cssTransitionTranslateX( image, 0 + 'px', options.animationSpeed / 1000 ); + else image.animate({ 'left': imagePosLeft + 'px' }, options.animationSpeed / 2 ); + } + }); + + }, options.animationSpeed + 100 ); + }, + + removeImage = function() + { + if( !image.length ) return false; + image.remove(); + image = $(); + }, + + quitLightbox = function() + { + if( !image.length ) return false; + image.animate({ 'opacity': 0 }, options.animationSpeed, function() + { + removeImage(); + inProgress = false; + if( options.onEnd !== false ) options.onEnd(); + }); + }; + + $( window ).on( 'resize', setImage ); + + if( options.quitOnDocClick ) + { + $( document ).on( hasTouch ? 'touchend' : 'click', function( e ) + { + if( image.length && !$( e.target ).is( image ) ) quitLightbox(); + }) + } + + if( options.enableKeyboard ) + { + $( document ).on( 'keyup', function( e ) + { + if( !image.length ) return true; + e.preventDefault(); + if( e.keyCode == 27 ) quitLightbox(); + if( e.keyCode == 37 || e.keyCode == 39 ) + { + target = targets.eq( targets.index( target ) - ( e.keyCode == 37 ? 1 : -1 ) ); + if( !target.length ) target = targets.eq( e.keyCode == 37 ? targets.length : 0 ); + loadImage( e.keyCode == 37 ? 'left' : 'right' ); + } + }); + } + + $( document ).on( 'click', this.selector, function( e ) + { + if( !isTargetValid( this ) ) return true; + e.preventDefault(); + if( inProgress ) return false; + inProgress = false; + if( options.onStart !== false ) options.onStart(); + target = $( this ); + loadImage(); + }); + + this.each( function() + { + if( !isTargetValid( this ) ) return true; + targets = targets.add( $( this ) ); + }); + + this.switchImageLightbox = function( index ) + { + var tmpTarget = targets.eq( index ); + if( tmpTarget.length ) + { + var currentIndex = targets.index( target ); + target = tmpTarget; + loadImage( index < currentIndex ? 'left' : 'right' ); + } + return this; + }; + + this.quitImageLightbox = function() + { + quitLightbox(); + return this; + }; + + return this; + }; +})( jQuery, window, document ); diff --git a/sass/imagelightbox.scss b/sass/imagelightbox.scss new file mode 100644 index 0000000..fac8a78 --- /dev/null +++ b/sass/imagelightbox.scss @@ -0,0 +1,216 @@ +@import 'compass/css3'; + +@mixin touch-action($value) { + -ms-touch-action: $value; + touch-action: $value; +} + +html { + // killing 300ms touch delay in IE + @include touch-action(manipulation); +} + +#imagelightbox { + @include touch-action(none); + @include box-shadow(rgba(0, 0, 0, .75) 0 0 50px); + cursor: pointer; + position: fixed; + z-index: 10000; +} + +// Loading Indication +#imagelightbox-loading, +#imagelightbox-loading div { + @include border-radius(50%); +} + +#imagelightbox-loading { + $box-size: 20px; + @include box-shadow(rgba(0, 0, 0, .75) 0 0 $box-size*2); // 40 + + background: #444; // Fallback + background: rgba(0, 0, 0, .5); + height: $box-size; + left: 50%; + margin: -1.25em 0 0 -1.25em; // 20 + padding: 10px; + position: fixed; + top: 50%; + width: $box-size; + z-index: 10003; + + div { + -moz-animation: imagelightbox-loading .5s ease infinite; + -o-animation: imagelightbox-loading .5s ease infinite; + -webkit-animation: imagelightbox-loading .5s ease infinite; + animation: imagelightbox-loading .5s ease infinite; + + background-color: #fff; + height: 20px; + width: 20px; + } +} + +@-webkit-keyframes imagelightbox-loading { + from { opacity: .5; -webkit-transform: scale(.75); } + 50% { opacity: 1; -webkit-transform: scale(1 ); } + to { opacity: .5; -webkit-transform: scale(.75); } +} + +@-moz-keyframes imagelightbox-loading { + from { opacity: .5; -moz-transform: scale(.75); } + 50% { opacity: 1; -moz-transform: scale(1 ); } + to { opacity: .5; -moz-transform: scale(.75); } +} + +@-o-keyframes imagelightbox-loading { + from { opacity: .5; -o-transform: scale(.75); } + 50% { opacity: 1; -o-transform: scale(1 ); } + to { opacity: .5; -o-transform: scale(.75); } +} + +@keyframes imagelightbox-loading { + from { opacity: .5; transform: scale(.75); } + 50% { opacity: 1; transform: scale(1 ); } + to { opacity: .5; transform: scale(.75); } +} + +#imagelightbox-overlay { + background: #fff; + background: rgba(255, 255, 255, .9 ); + bottom: 0; + left: 0; + position: fixed; + right: 0; + top: 0; + z-index: 9998; +} + + +// Close button +#imagelightbox-close { + $size: 40px; + -moz-border-radius: 50%; + -moz-transition: color .3s ease; + -ms-transition: color .3s ease; + -o-transition: color .3s ease; + -webkit-border-radius: 50%; + -webkit-transition: color .3s ease; + + background-color: #666; + border-radius: 50%; + height: $size; + position: fixed; + right: $size; + text-align: left; + text-indent: -9999px; + top: $size; + transition: color .3s ease; + width: $size; + z-index: 10002; + + &:hover { + background-color: #111; + } + + &:before, + &:after { + background-color: #fff; + bottom: 20%; + content: ''; + left: 50%; + margin-left: -1px; + position: absolute; + top: 20%; + width: 2px; + } + + &:before { + @include rotate( 45deg ); + } + + &:after { + @include rotate( -45deg ); + } +} + +#imagelightbox-caption { + background-color: #666; + bottom: 0; + color: #fff; + left: 0; + padding: 10px; + position: fixed; + right: 0; + text-align: center; + z-index: 10001; +} + +// The nav bubbles +#imagelightbox-nav { + @include border-radius(20px); + @include translateX( -50% ); + + background-color: #444; + background-color: rgba(0, 0, 0, .5); + bottom: 60px; // 60 + left: 50%; + padding: 5px 2px 1px; + position: fixed; + z-index: 10001; + + a { + @include border-radius(50%); + border: 1px solid #fff; + display: inline-block; + height: 20px; + margin: 0 5px; + width: 20px; + } + + a.active { + background-color: #fff; + } +} + +#imagelightbox-loading, +#imagelightbox-overlay, +#imagelightbox-close, +#imagelightbox-caption, +#imagelightbox-nav { + -webkit-animation: fade-in .25s linear; + -moz-animation: fade-in .25s linear; + -o-animation: fade-in .25s linear; + animation: fade-in .25s linear; +} + +@-webkit-keyframes fade-in { + from { opacity: 0; } + to { opacity: 1; } +} + +@-moz-keyframes fade-in { + from { opacity: 0; } + to { opacity: 1; } +} + +@-o-keyframes fade-in { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes fade-in { + from { opacity: 0; } + to { opacity: 1; } +} + +@media only screen and (max-width: 660px) { + #imagelightbox-close { + right: 20px; + top: 20px; + } + + #imagelightbox-nav { + bottom: 20px; + } +} -- cgit v1.2.3