Then, we work with elements that are created dynamically by jQuery later on. I’ll style them now, and explain what they do:

#fluidbox-overlay is the element that we overlay over the entire page when Fluidbox is active, so we obscure the rest of the page’s content. You can choose to use an entire opaque overlay with any colour you want, but for aesthetic purposes I have chosen to overlay it with white with an opacity of 0.85.

is the element that we overlay over the entire page when Fluidbox is active, so we obscure the rest of the page’s content. You can choose to use an entire opaque overlay with any colour you want, but for aesthetic purposes I have chosen to overlay it with white with an opacity of 0.85. .fluidbox-wrap is a general wrapper element that is the glue, such that it will hold both the <img> and its ghost element (see next point) together.

is a general wrapper element that is the glue, such that it will hold both the <img> and its ghost element (see next point) together. .fluidbox-ghost is the element that is created by jQuery to mimic the <img> element within the anchor, for the purpose of image replacement with a higher resolution target image whose url is extracted from the anchor’s href attribute.

#fluidbox-overlay {

background-color: rgba(255,255,255,.85);

cursor: pointer;

cursor: -webkit-zoom-out;

cursor: -moz-zoom-out;

display: none;

position: fixed;

top: 0;

left: 0;

bottom: 0;

right: 0;

z-index: 500;

} .fluidbox-wrap {

background-position: center center;

background-size: cover;

margin: 0 auto;

position: relative;

z-index: 400;

transition: all .25s ease-in-out;

}

.fluidbox-opened .fluidbox-wrap {

z-index: 600;

} .fluidbox-ghost {

background-size: cover;

background-position: center center;

position: absolute;

transition: all .25s ease-in-out;

}

jQuery

The dependency needed for our jQuery function to work is the imagesloaded plugin. The plugin allows me to listen in on the loading of all the images used for Fluidbox — it makes sense because we have to wait for images to be loaded in order to access their dimesnions for calculations later.

As I feel most comfortable working with jQuery, Fluidbox has been written to utilize the framework. Remember that all of the following code has to be placed within the DOM ready event, i.e.:

$(function (){

// Everything

});

Step 1: General housekeeping and defining functions

Some general housekeeping function is needed: (1) declare some much-needed global variables, (2) appending the overlay to the body, (3) defining the closeFb function to close any opened Fluidbox and (4) defining the function that calculates positioning of Fluidbox.

// Global variables

var $fb = $('a[data-fluidbox');

vpRatio; // To store viewport aspect ratio // Add class to all Fluidboxes

$fb.addClass('fluidbox'); // Create fluidbox modal background

$('body').append('<div id="fluidbox-overlay"></div>'); // Functions:

// 1. to close any opened Fluidbox

// 2. to position Fluidbox dynamically

var closeFb = function (){

$('a[data-fluidbox].fluidbox-opened').trigger('click');

},

positionFb = function ($activeFb){

// Get elements

var $img = $activeFb.find('img'),

$ghost = $activeFb.find('.fluidbox-ghost');



// Calculate offset and scale

var offsetY = $(window).scrollTop()-$img.offset().top+0.5*($img.data('imgHeight')*($img.data('imgScale')-1))+0.5*($(window).height()-$img.data('imgHeight')*$img.data('imgScale')),

offsetX = 0.5*($img.data('imgWidth')*($img.data('imgScale')-1))+0.5*($(window).width()-$img.data('imgWidth')*$img.data('imgScale')) - $img.offset().left,

scale = $img.data('imgScale');



// Animate wrapped elements

// Parse integers:

// 1. Offsets can be integers

// 2. Scale is rounded to nearest 2 decimal places

$ghost.css({ 'transform': 'translate('+parseInt(offsetX*10)/10+'px,'+parseInt(offsetY*10)/10+'px) scale('+parseInt(scale*1000)/1000+')' });

} // The following events will force FB to close

// ... when the opqaue overlay is clicked upon

$('#fluidbox-bg').click(closeFb);

It is important to parse the calculated values of offsetX, offsetY and scale. Due to issues with floating point calculation in JavaScript, we might not get the expected value of 0 for some calculations even though it should be — instead, we get an epsilon value (e.g. 2.01e-14) which will invalidate our CSS styling. offsetX and offsetY can be rounded to the nearest one decimal place, For scale, however, we will round it to the nearest three decimal places.

You can also fire the closeFb() function for other event handlers. The reason why the calculations for dynamic positioning of Fluidbox is wrapped in a function is to allow the same calculations to be called when different events are detected — namely the window resize event (when viewport size and/or orientation will change, therefore determining how images should be scaled) and the click event.

How do we compute the correct values,

to translate and scale the ghost image?

The challenging part would be calculating the amount we need to translate the ghost element along the x and y axes such that it is centered. I have used a diagram below to better illustrate my calculations. I highly encourage you to view the high resolution animated gif (83kb, 2000*600px).