add fluidbox v1.4.1

This commit is contained in:
Peter Dave Hello
2014-11-09 09:58:32 +08:00
parent b81dc70f19
commit 028243b319
4 changed files with 470 additions and 1 deletions
+44
View File
@@ -0,0 +1,44 @@
.fluidbox {
outline: none;
}
.hidden {
display: none;
}
.fluidbox-overlay {
background-color: rgba(255,255,255,.85);
cursor: pointer;
cursor: -webkit-zoom-out;
cursor: -moz-zoom-out;
opacity: 0;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
transition: all .25s ease-in-out;
}
.fluidbox-wrap {
background-position: center center;
background-size: cover;
margin: 0 auto;
position: relative;
transition: all .25s ease-in-out;
}
.fluidbox-ghost {
background-size: 100% 100%;
background-position: center center;
position: absolute;
transition: all .25s ease-in-out;
}
.fluidbox-closed .fluidbox-ghost {
-webkit-transition-property: top, left, opacity, -webkit-transform;
-moz-transition-property: top, left, opacity, -moz-transform;
-o-transition-property: top, left, opacity, -o-transform;
transition-property: top, left, opacity, transform;
transition-delay: 0, 0, .25s, 0;
}
.fluidbox-closed .fluidbox-wrap img {
transition-property: opacity;
transition-delay: .25s;
transition-duration: 0s;
}
+424
View File
@@ -0,0 +1,424 @@
// Fluidbox
// Description: Replicating the seamless lightbox transition effect seen on Medium.com, with some improvements
// Version: 1.4.1
// Author: Terry Mun
// Author URI: http://terrymun.com
// -------------------------------------------------------- //
// Dependency: Paul Irish's jQuery debounced resize event //
// -------------------------------------------------------- //
(function($,sr){
// debouncing function from John Hann
// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
var debounce = function (func, threshold, execAsap) {
var timeout;
return function debounced () {
var obj = this, args = arguments;
function delayed () {
if (!execAsap)
func.apply(obj, args);
timeout = null;
};
if (timeout)
clearTimeout(timeout);
else if (execAsap)
func.apply(obj, args);
timeout = setTimeout(delayed, threshold || 100);
};
}
// smartresize
jQuery.fn[sr] = function(fn){ return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
})(jQuery,'smartresize');
// ---------------------------------------------------------------------------------------------------------------------- //
// Dependency: David Walsh (http://davidwalsh.name/css-animation-callback) //
// and //
// Jonathan Suh (https://jonsuh.com/blog/detect-the-end-of-css-animations-and-transitions-with-javascript/) //
// ---------------------------------------------------------------------------------------------------------------------- //
function whichTransitionEvent() {
var t,
el = document.createElement("fakeelement");
var transitions = {
"transition" : "transitionend",
"OTransition" : "oTransitionEnd",
"MozTransition" : "transitionend",
"WebkitTransition": "webkitTransitionEnd"
}
for (t in transitions){
if (el.style[t] !== undefined){
return transitions[t];
}
}
}
var customTransitionEnd = whichTransitionEvent();
// -----------------------------
// Fluidbox plugin starts here
// -----------------------------
(function ($) {
$.fn.fluidbox = function (opts) {
// Default settings
var settings = $.extend(true, {
viewportFill: 0.95,
debounceResize: true,
stackIndex: 1000,
stackIndexDelta: 10,
closeTrigger: [
{
selector: '.fluidbox-overlay',
event: 'click'
},
{
selector: 'document',
event: 'keyup',
keyCode: 27
}
]
}, opts);
// Ensure that the stackIndex does not become negative
if(settings.stackIndex < settings.stackIndexDelta) settings.stackIndexDelta = settings.stackIndex;
// Dynamically create overlay
$fbOverlay = $('<div />', {
'class': 'fluidbox-overlay',
css: {
'z-index': settings.stackIndex
}
});
// Declare variables
var $fb = this,
$w = $(window), // Shorthand for $(window)
vpRatio,
// Function:
// 1. funcCloseFb() - used to close any instance of opened Fluidbox
// 2. funcPositionFb() - used for dynamic positioning of any instance of opened Fluidbox
// 3. funcCalcAll() - used to run funcCalc() for every instance of targered Fluidbox thumbnail
// 4. funcCalc() - used to store dimensions of image, ghost element and wrapper element upon initialization or resize
// 5. fbClickhandler() - universal click handler for all Fluidbox items
funcCloseFb = function () {
$('.fluidbox-opened').trigger('click');
},
funcPositionFb = function ($activeFb, customEvent) {
// Get shorthand for more objects
var $img = $activeFb.find('img'),
$ghost = $activeFb.find('.fluidbox-ghost'),
$wrap = $activeFb.find('.fluidbox-wrap'),
$data = $activeFb.data(),
fHeight = 0,
fWidth = 0;
$img.data().imgRatio = $data.natWidth/ $data.natHeight;
var newHeight, missingRatioNormal, missingRatio;
// Check natural dimensions
if(vpRatio > $img.data().imgRatio) {
if($data.natHeight < $w.height()*settings.viewportFill) {
fHeight = $data.natHeight;
} else {
fHeight = $w.height()*settings.viewportFill;
}
$data.imgScale = fHeight/$img.height();
$data.imgScaleY = $data.imgScale;
newHeight = $img.height() * $data.imgScaleY;
missingRatioNormal = newHeight / $data.natHeight;
missingRatio = $data.natWidth * missingRatioNormal / $img.width();
$data.imgScaleX = missingRatio;
} else {
if($data.natWidth < $w.width()*settings.viewportFill) {
fWidth = $data.natWidth;
} else {
fWidth = $w.width()*settings.viewportFill;
}
$data.imgScale = fWidth/$img.width();
$data.imgScaleX = $data.imgScale;
var newWidth = $img.width() * $data.imgScaleX;
var missingRatioNormal = newWidth / $data.natWidth;
var missingRatio = $data.natHeight * missingRatioNormal / $img.height();
$data.imgScaleY = missingRatio;
}
// Calculation goes here
var offsetY = $w.scrollTop()-$img.offset().top+0.5*($img.data('imgHeight')*($img.data('imgScale')-1))+0.5*($w.height()-$img.data('imgHeight')*$img.data('imgScale')),
offsetX = 0.5*($img.data('imgWidth')*($img.data('imgScale')-1))+0.5*($w.width()-$img.data('imgWidth')*$img.data('imgScale'))-$img.offset().left,
scale = parseInt($data.imgScaleX * 1000) / 1000 + ',' + parseInt($data.imgScaleY * 1000) / 1000;
// Apply CSS transforms to ghost element
// For offsetX and Y, we round to one decimal place
// For scale, we round to three decimal places
$ghost.css({
'transform': 'translate('+parseInt(offsetX*10)/10+'px,'+parseInt(offsetY*10)/10+'px) scale('+ scale +')',
top: $img.offset().top - $wrap.offset().top,
left: $img.offset().left - $wrap.offset().left
}).one(customTransitionEnd, function() {
$activeFb.trigger(customEvent);
});
},
funcCalcAll = function() {
$fb.each(function () {
funcCalc($(this));
});
},
funcCalc = function ($fbItem) {
// Get viewport ratio
vpRatio = $w.width() / $w.height();
// Get image dimensions and aspect ratio
if($fbItem.hasClass('fluidbox')) {
var $img = $fbItem.find('img'),
$ghost = $fbItem.find('.fluidbox-ghost'),
$wrap = $fbItem.find('.fluidbox-wrap'),
data = $img.data();
function imageProp() {
// Store image dimensions in jQuery object
data.imgWidth = $img.width();
data.imgHeight = $img.height();
data.imgRatio = $img.width()/$img.height();
// Resize and position ghost element
$ghost.css({
width: $img.width(),
height: $img.height(),
top: $img.offset().top - $wrap.offset().top + parseInt($img.css('borderTopWidth')) + parseInt($img.css('paddingTop')),
left: $img.offset().left - $wrap.offset().left + parseInt($img.css('borderLeftWidth')) + parseInt($img.css('paddingLeft'))
});
// Calculate scale based on orientation
if(vpRatio > data.imgRatio) {
data.imgScale = $w.height()*settings.viewportFill/$img.height();
} else {
data.imgScale = $w.width()*settings.viewportFill/$img.width();
}
}
imageProp();
// Rerun everything on imageload, to overcome issue in Firefox
$img.load(imageProp);
}
},
fbClickHandler = function(e) {
// Check if the fluidbox element does have .fluidbox assigned to it
if($(this).hasClass('fluidbox')) {
// Variables
var $activeFb = $(this),
$img = $(this).find('img'),
$ghost = $(this).find('.fluidbox-ghost'),
$wrap = $(this).find('.fluidbox-wrap'),
timer = {};
if($(this).data('fluidbox-state') === 0 || !$(this).data('fluidbox-state')) {
// State: Closed
// Action: Open fluidbox
// Fire custom event: openstart
$(this).trigger('openstart');
// Wait for ghost image to be loaded successfully first, then do the rest
$('<img />', {
src: $img.attr('src')
}).load(function () {
// Preload ghost image
$('<img />', {
src: $activeFb.attr('href')
}).load(function() {
// Store natural width and heights
$activeFb
.data('natWidth', $(this)[0].naturalWidth)
.data('natHeight', $(this)[0].naturalHeight);
// What are we doing here:
// 1. Append overlay in fluidbox
// 2. Toggle fluidbox state with data attribute
// 3. Store original z-index with data attribute (so users can change z-index when they see fit in CSS file)
// 4. Class toggle
$activeFb
.append($fbOverlay)
.data('fluidbox-state', 1)
.removeClass('fluidbox-closed')
.addClass('fluidbox-opened');
// Force timer to completion
if(timer['close']) window.clearTimeout(timer['close']);
// Set timer for opening
timer['open'] = window.setTimeout(function() {
// Show overlay
$('.fluidbox-overlay').css({ opacity: 1 });
}, 10);
// Change wrapper z-index, so it is above everything else
// Decrease all siblings z-index by 1 just in case
$('.fluidbox-wrap').css({ zIndex: settings.stackIndex - settings.stackIndexDelta - 1 });
$wrap.css({ 'z-index': settings.stackIndex + settings.stackIndexDelta });
// Set thumbnail image source as background image first, preload later
$ghost.css({
'background-image': 'url('+$img.attr('src')+')',
opacity: 1
});
// Hide original image
$img.css({ opacity: 0 });
$ghost.css({ 'background-image': 'url('+$activeFb.attr('href')+')' });
// Position Fluidbox
funcPositionFb($activeFb, 'openend');
});
});
} else {
// State: Open
// Action: Close fluidbox
// Fire custom event: closestart
$activeFb.trigger('closestart');
// Switch state
$activeFb
.data('fluidbox-state', 0)
.removeClass('fluidbox-opened')
.addClass('fluidbox-closed');
// Set timer for closing
if(timer['open']) window.clearTimeout(timer['open']);
timer['close'] = window.setTimeout(function() {
$('.fluidbox-overlay').remove();
$wrap.css({ 'z-index': settings.stackIndex - settings.stackIndexDelta });
}, 10);
// Hide overlay
$('.fluidbox-overlay').css({ opacity: 0 });
// Reverse animation on wrapped elements, and restore stacking order
// You might want to change this value if your transition timing is longer
$ghost.css({
'transform': 'translate(0,0) scale(1)',
opacity: 0,
top: $img.offset().top - $wrap.offset().top + parseInt($img.css('borderTopWidth')) + parseInt($img.css('paddingTop')),
left: $img.offset().left - $wrap.offset().left + parseInt($img.css('borderLeftWidth')) + parseInt($img.css('paddingLeft'))
}).one(customTransitionEnd, function() {
$activeFb.trigger('closeend');
});
$img.css({ opacity: 1 });
}
e.preventDefault();
}
};
// When should we close Fluidbox?
if(settings.closeTrigger) {
// Go through array
$.each(settings.closeTrigger, function (i) {
var trigger = settings.closeTrigger[i];
// Attach events
if(trigger.selector != 'window') {
// If it is not 'window', we append click handler to $(document) object, allow it to bubble up
// However, if thes selector is 'document', we use a different .on() syntax
if(trigger.selector == 'document') {
if(trigger.keyCode) {
$(document).on(trigger.event, function (e) {
if(e.keyCode == trigger.keyCode) funcCloseFb();
});
} else {
$(document).on(trigger.event, funcCloseFb);
}
} else {
$(document).on(trigger.event, settings.closeTrigger[i].selector, funcCloseFb);
}
} else {
// If it is 'window', append click handler to $(window) object
$w.on(trigger.event, funcCloseFb);
}
});
}
// Go through each individual object
$fb.each(function (i) {
// Check if Fluidbox:
// 1. Is an anchor element ,<a>
// 2. Contains one and ONLY one child
// 3. The only child is an image element, <img>
// 4. If the element is hidden
if($(this).is('a') && $(this).children().length === 1 && $(this).children().is('img') && $(this).css('display') !== 'none' && $(this).parents().css('display') !=='none') {
// Define wrap
var $fbInnerWrap = $('<div />', {
'class': 'fluidbox-wrap',
css: {
'z-index': settings.stackIndex - settings.stackIndexDelta
}
});
// Add class
var $fbItem = $(this);
$fbItem
.addClass('fluidbox')
.wrapInner($fbInnerWrap)
.find('img')
.css({ opacity: 1 })
.after('<div class="fluidbox-ghost" />')
.each(function(){
var $img = $(this);
if ($img.width() > 0 && $img.height() > 0) {
// if image is already loaded (from cache)
funcCalc($fbItem);
$fbItem.click(fbClickHandler);
} else {
// wait for image to load
$img.load(function(){
funcCalc($fbItem);
$fbItem.click(fbClickHandler);
});
}
});
}
});
// Listen to window resize event
// Check if user wants to debounce the resize event (it is debounced by default)
var funcResize = function () {
// Recalculate dimensions
funcCalcAll();
// Reposition Fluidbox, but only if one is found to be open
var $activeFb = $('a.fluidbox.fluidbox-opened');
if($activeFb.length > 0) funcPositionFb($activeFb, 'resizeend');
}
if(settings.debounceResize) {
$(window).smartresize(funcResize);
} else {
$(window).resize(funcResize);
}
// Return to allow chaining
return $fb;
};
})(jQuery);
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "fluidbox",
"filename": "jquery.fluidbox.min.js",
"version": "1.4.0",
"version": "1.4.1",
"description": "Replicating Medium's beautiful, minimalist lightbox module",
"homepage": "https://github.com/terrymun/Fluidbox",
"authors": [