Fluidbox Demo
by Terry Mun
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.imagesloaded/3.0.4/jquery.imagesloaded.min.js"></script>
<main>
<header>
<h1>Fluidbox</h1>
<span class="byline">Replicating and improving the lightbox module seen on Medium with fluid transitions</span>
</header>
<p>Opening images seamlessly in a lightbox on your page without interruption. This demo was inspired by how Medium handles embedded images. Made by <a rel="author" href="http://terrymun.com" title="Visit me">Terry</a>. This project was originally initiated as a personal challenge to replicate Medum's lightbox module, but it soon developed into a full-fledged jQuery plugin. You can follow the links below to read how I've tackled the challenge, or view the jQuery plugin on GitHub:</p>
<div class="cards">
<a href="https://medium.com/coding-design/9c7fe9db92c7">
<svg version="1.2" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px" viewBox="0 0 200 200" xml:space="preserve">
<g>
<path d="M0.001,0v200h200V0H0.001z M148.913,72.752h-3.869c-1.437,0-3.095,1.879-3.095,3.206v48.081
c0,1.328,1.658,3.316,3.095,3.316h3.869v11.164h-34.708v-11.164h6.964V76.842h-0.332l-17.133,61.677H90.439L73.528,76.842h-0.332
v50.513h7.295v11.164H51.089v-11.164h3.758c1.548,0,3.206-1.988,3.206-3.316V75.958c0-1.327-1.658-3.206-3.206-3.206h-3.758V61.146
h36.697l12.048,44.877h0.332l12.16-44.877h36.587V72.752z"/>
</g>
</svg>
<span>Read the tutorial on Medium</span>
</a>
<a href="https://github.com/terrymun/Fluidbox">
<svg version="1.2" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 850.39 850.39" xml:space="preserve">
<g>
<path fill-rule="evenodd" fill="#191717"...
SCSS
html {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 16px;
}
body {
color: #333;
height: 100%;
line-height: 1.5em;
margin-bottom: 1.5em;
}
main {
margin: 0 auto;
width: 66.66667%;
}
header {
background-color: #333;
background-image: linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,.25) 100%);
box-sizing: border-box;
color: #eee;
margin-bottom: 1.5rem;
overflow: hidden;
padding: 3rem 25%;
pointer-events: none;
position: relative;
left: -25%;
width: 150%;
transition: all .25s ease-in-out;
&:before {
background-image:
linear-gradient(115deg, rgba(255,255,255,.025) 0%, rgba(255,255,255,.05) 50%, rgba(255,255,255,0) 50%),
linear-gradient(to left, #005952 0%, #005E20 100%);
content: "";
opacity: 0;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
transition: all .25s ease-in-out;
}
&:hover:before {
opacity: 1;
}
& > * {
position: relative;
}
& h1 {
font-weight: bold;
font-size: 3em;
line-height: 1em;
margin: 0 0 .5rem 0;
text-align: center;
}
& .byline {
color: rgba(255,255,255,.75);
display: block;
font-size: .8em;
letter-spacing: 1px;
text-align: center;
text-transform: uppercase;
}
}
p {
margin-bottom: 1.5rem;
}
.message {
background-color: #FDC68A;
border-left: .75rem solid #F26C4F;
padding: .75rem 1.5rem;
font-size: .85rem;
& a:hover {
border-color: rgba(0,0,0,.5);
color: rgba(0,0,0,.5);
}
&.note {
background-color: #7BCDC8;
border-color: #00A99D;
}
}
hr {
border: none;
border-top: .125rem solid #ddd;
margin-bottom: 1.375rem;
}
a {
border-bottom: 2px solid #333;
color: #333;
text-decoration: none;
transition: all .125s ease-in-out;
&:hover {
border-bottom-color: #4a7298;
color: #4a7298;
}
}
ul {
margin-top: -.75rem;
}
code {
background-color: rgba(0,0,0,.125);
border: 1px solid...
JavaScript
// Wait for DOM
$(function (){
// Global variables
var $fb = $('a[data-fluidbox]'),
vpRatio;
// Add class
$fb.addClass('fluidbox');
// Create fluidbox modal background
$('body').append('<div id="fluidbox-overlay" />');
// The following events will force FB to close
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 ghost element
// For offsetX and Y, we round to one decimal places
// For scale, we round to three decimal places
$ghost.css({
'transform': 'translate('+parseInt(offsetX*10)/10+'px,'+parseInt(offsetY*10)/10+'px) scale('+parseInt(scale*1000)/1000+')'
});
}
// Close Fluidbox when overlay is closed
$('#fluidbox-overlay').click(closeFb);
// Check if images are loaded first
$fb.imagesLoaded().done(function (){
// Create dynamic elements
$fb
.wrapInner('<div class="fluidbox-wrap" />')
.find('img')
.css({ opacity: 1 })
.after('<div class="fluidbox-ghost" />');
// Listen to resize event for calculations
$(window).resize(function (){
// Get viewport ratio
vpRatio = $(window).width() / $(window).height();
// Get dimensions and aspect ratios
$fb.each(function (){
var $img = $(this).find('img'),
$ghost =...