Last active
January 22, 2019 19:31
-
-
Save jlieb10/6ec66baa892cb2cdde85507d52d4781f to your computer and use it in GitHub Desktop.
Image Enlarger
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///////////////////////////////////////////// | |
// Image Enlarger Module | |
// | |
// loc: src/js/modules/image_enlarger.js | |
// | |
// Module that finds images in the DOM and | |
// duplicates them dynamically (on mouseover) | |
// with their larger versions which are | |
// displayed dynamically (on click) to | |
// drastically reduce load time on | |
// HD image-heavy articles | |
//////////////////////////////////////////// | |
class ImageEnlarger { | |
constructor() { | |
// track state | |
this.active = false; | |
this.currentScroll = null; | |
// get nodes from the DOM & using Array prototype to convert node collections to arrays | |
this.images = [].slice.call(document.querySelectorAll('.inline-large, .inline-xlarge')); | |
this.article = document.querySelector('.post-article'); | |
this.navs = [].slice.call(document.querySelectorAll('.navbar-brands-container, .reading-bar')); | |
} | |
init() { | |
this.setListeners(); | |
this.processPoster(); | |
} | |
// Test article to see if it has a poster to enlarge and then to set proper listeners & class on the poster | |
processPoster() { | |
let test = document.querySelector('.fc-header .poster-wrapper :nth-child(1)'); | |
if (test) { | |
test = test.tagName.match(/(IMG|NOSCRIPT)/); | |
} | |
if (test && test.length > 0) { | |
let imageWrapper = document.createElement('div'); | |
let overlay = this.createOverlay(); | |
let poster = document.querySelector('.fc-header .poster-wrapper'); | |
let posterImg = poster.querySelector('img'); | |
imageWrapper.setAttribute('class', 'image-wrapper'); | |
poster.classList.add('inline-large'); | |
imageWrapper.appendChild(posterImg); | |
imageWrapper.appendChild(overlay); | |
if (poster.querySelector('figcaption')) { | |
poster.insertBefore(imageWrapper, poster.querySelector('figcaption')); | |
} else { | |
poster.appendChild(imageWrapper); | |
} | |
poster.addEventListener('click', function() { | |
this.clickHandle(posterImg); | |
}.bind(this)); | |
} | |
} | |
// Set hover listeners on all inline-large images within post | |
setListeners() { | |
this.images.forEach(function(figure) { | |
figure.on('mouseover', function(e) { | |
// return if already triggered before | |
if (e.currentTarget.dataset.triggered) return; | |
e.currentTarget.dataset.triggered = true; | |
this.hoverHandle(figure); | |
}.bind(this)); | |
}.bind(this)); | |
} | |
// On hover, replace the small image with its full-size version | |
hoverHandle(figure) { | |
let rgx = /\/imagecache\/inline-(large|xlarge)/i; | |
let wrapper = figure.querySelector('.image-wrapper') || figure; | |
let img = wrapper.querySelector('img'); | |
let cloneSrc = img.getAttribute('src').replace(rgx, ''); | |
let overlay = this.createOverlay(); | |
let newImage = document.createElement('img'); | |
newImage.setAttribute('src', cloneSrc); | |
newImage.onload = function() { | |
wrapper.insertBefore(newImage, img); | |
wrapper.removeChild(img); | |
wrapper.appendChild(overlay); | |
figure.addEventListener('click', function() { | |
this.clickHandle(newImage); | |
}.bind(this)); | |
}.bind(this); | |
} | |
// Direct click behavior depending on state of image (enlarged or not) | |
clickHandle(clone) { | |
if (this.active) { | |
this.rmHandle(clone); | |
return; | |
} | |
this.active = true; | |
this.currentScroll = function() { | |
this.scrollHandle(clone); | |
}.bind(this); | |
document.addEventListener('scroll', this.currentScroll); | |
this.getTranslationPoints(clone); | |
} | |
// Direct scroll behavior depending on state of image (enlarged or not) | |
scrollHandle(clone) { | |
if (this.active) { | |
this.rmHandle(clone); | |
return; | |
} | |
return; | |
} | |
// On scroll or click, if the image is enlarged, get rid of enlargement | |
rmHandle(clone) { | |
let navBar = document.querySelector('.nav-bar-wrapper'); | |
let figure = clone.parentNode.parentNode; | |
document.removeEventListener('scroll', this.currentScroll); | |
clone.style.transform = 'scale(1) translate(0, 0)'; | |
figure.classList.remove('enlarged'); | |
navBar.style.position = 'relative'; | |
this.article.style.overflow = 'hidden'; | |
this.active = false; | |
} | |
// Find where the image is relative to viewport and set its translation points to center it when enlarged | |
getTranslationPoints(clone) { | |
let figure = clone.parentNode.parentNode; | |
let scrollTop = document.body.scrollTop; | |
let cloneOffset = clone.getBoundingClientRect(); | |
let viewportCenterX = (window.innerWidth / 2); | |
let viewportCenterY = scrollTop + (window.innerHeight / 2); | |
let imageCenterX = cloneOffset.left + (clone.width / 2); | |
let imageCenterY = (cloneOffset.top + scrollTop) + (clone.height / 2); | |
let xTranslate = (viewportCenterX - imageCenterX); | |
let yTranslate = (viewportCenterY - imageCenterY); | |
let scaleFactor = this.getScaleFactor(clone); | |
let cloneTransform = 'translate(' + xTranslate + 'px, ' + yTranslate + 'px) scale(' + scaleFactor + ')'; | |
clone.style.transform = cloneTransform; | |
this.article.style.overflow = 'visible'; | |
this.animateNavs(); | |
figure.classList.add('enlarged'); | |
} | |
// Find and compare image's natural aspect ratio with window's and set its scale factor accordingly | |
// If the image is smaller than the window, enlarge image to its maximum size | |
getScaleFactor(clone) { | |
let imgRatio = (clone.width / clone.height); | |
let windowRatio = (window.innerWidth / window.innerHeight); | |
let scaleFactor = window.innerHeight / clone.height; | |
if ((clone.naturalHeight < window.innerHeight) && (clone.naturalWidth < window.innerWidth)) { | |
scaleFactor = clone.naturalWidth / clone.width; | |
return scaleFactor; | |
} | |
if (imgRatio > windowRatio) { | |
scaleFactor = window.innerWidth / clone.width; | |
return scaleFactor; | |
} | |
return scaleFactor; | |
} | |
// Simple function to create overlay on either poster or | |
createOverlay() { | |
let overlay = document.createElement('div'); | |
overlay.setAttribute('class', 'overlay'); | |
return overlay; | |
} | |
// Animate navs out of view when image is enlarged, depending on if the user has scrolled beyond the top ad or not | |
animateNavs() { | |
let navBar = document.querySelector('.nav-bar-wrapper'); | |
let adHeight = navBar.querySelector('.ad-wrapper').offsetHeight; | |
let currentY = window.scrollY; | |
navBar.style.position = 'static'; | |
if (adHeight < currentY) { | |
this.navs.forEach(function(nav) { | |
nav.style.top = '-60px'; | |
}); | |
} | |
} | |
// First function called in controller, initialize if screen is at least 1024px wide | |
on() { | |
if (window.innerWidth > 1023) { | |
window.addEventListener('DOMContentLoaded', this.init.bind(this)); | |
} | |
} | |
} | |
export default new ImageEnlarger(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment