Skip to content

Instantly share code, notes, and snippets.

@dallinskinner
Created May 20, 2017 19:24
Show Gist options
  • Select an option

  • Save dallinskinner/3a741a69c28c5b0b1f71f1a8cfd0c04b to your computer and use it in GitHub Desktop.

Select an option

Save dallinskinner/3a741a69c28c5b0b1f71f1a8cfd0c04b to your computer and use it in GitHub Desktop.
Zoomable
const u = require('umbrellajs').u;
u.prototype.absTop = function() {
return this.first().getBoundingClientRect().top + window.pageYOffset;
}
u.prototype.absBottom = function() {
return this.first().getBoundingClientRect().bottom + window.pageYOffset;
}
u.prototype.prev = function() {
var previous = this.first().previousElementSibling;
if (previous) {
return u(previous);
}
return null;
}
u.prototype.next = function() {
var next = this.first().nextElementSibling;
if (next) {
return u(next);
}
return null;
}
var checkScrollThreshold;
var unzoomOnResize;
module.exports = function(){
var $body = u('body');
var $shiftables = u('.zoomable, .shiftable');
$body.on('page-load', function(){
$shiftables = u('.zoomable, .shiftable');
});
$body.on('click', '.zoomable', function(){
var $this = u(this);
var padding = 20;
if ($this.hasClass('zoomed')) {
unzoom($this, $shiftables);
} else {
zoom($this, padding, $shiftables);
}
});
$body.on('transitionend', '.zoomed', function(e){
if (u(e.srcElement).hasClass('zoomable')) {
activateScrollThreshold(u(this), $shiftables);
}
});
$body.on('transitionend', '.info-zoom-block', function(e){
if (u(e.srcElement).hasClass('zoomable')) {
u(this).removeClass('info-zoom-block');
}
});
}
function zoom($img, padding, $shiftables) {
var sDelta = getScaleDelta($img, padding);
if (sDelta == 1) {
return;
}
var yDelta = getYDelta($img, sDelta, padding);
var xDelta = getXDelta($img);
$img.first().style.transform = 'translateY('+yDelta+'px) translateX('+xDelta+'px) scale('+sDelta+')';
$img.addClass('zoomed');
$img.addClass('info-zoom-block');
shiftOthers($img, yDelta, sDelta, padding, $shiftables);
}
function unzoom($img, $shiftables) {
$img.first().style.removeProperty('transform');
$img.removeClass('zoomed');
window.removeEventListener('scroll', checkScrollThreshold);
window.removeEventListener('resize', unzoomOnResize);
unshiftOthers($shiftables);
}
function getYDelta($img, sDelta, padding) {
var size = $img.size();
var imgYMiddle = size.height / 2 + size.top;
var windowYMiddle = window.innerHeight / 2;
var middleDelta = windowYMiddle - imgYMiddle;
// for images larger than the window size, we want to align them to the top.
// if they aren't larger than the window, align to middle.
// the following finds the amount of shift from the middle position if it needs to align to top
var scaledHeight = size.height * sDelta;
var topDelta = (scaledHeight / 2 - window.innerHeight / 2) + padding;
return middleDelta + Math.max(topDelta, 0);
}
function getXDelta($img) {
var size = $img.size();
var imgXMiddle = size.width / 2 + size.left;
var windowXMiddle = window.innerWidth / 2;
return windowXMiddle - imgXMiddle;
}
function getScaleDelta($img, padding) {
var imgWidth = $img.size().width;
var windowWidth = window.innerWidth - (2*padding);
return windowWidth / imgWidth;
}
function shiftOthers($img, yDelta, sDelta, padding, $shiftables) {
var size = $img.size();
// get extra shift if image height is smaller than window
var newImgHeight = size.height * sDelta;
var viewableWindowHeight = window.innerHeight - padding*2;
var extraShift = 0;
if (newImgHeight < viewableWindowHeight) {
extraShift = (viewableWindowHeight - newImgHeight)/2;
}
var $prev = previousLowestShiftable($img, $shiftables);
if ($prev) {
var topShift = $prev.size().bottom - extraShift;
}
// var $next = nextShiftable($img, $shiftables);
var $next = nextHighestShiftable($img, $shiftables);
if ($next) {
var bottomShift = Math.max(newImgHeight + (padding*2) - $next.size().top + extraShift, 0);
}
var isAfter = false;
$shiftables.nodes.forEach(function(element){
if (u(element).hasClass('zoomed')) {
isAfter = true;
return
}
if (isAfter) {
element.style.transform = 'translateY('+bottomShift+'px)';
} else {
element.style.transform = 'translateY(-'+topShift+'px)';
}
});
}
function unshiftOthers($shiftables) {
$shiftables.nodes.forEach(function(element){
element.style.removeProperty('transform');
});
}
function nextShiftable($img, $shiftables) {
var nextIndex = $shiftables.nodes.indexOf($img.first()) + 1;
if (nextIndex < $shiftables.nodes.length) {
return u($shiftables.nodes[nextIndex]);
}
return null;
}
function nextHighestShiftable($img, $shiftables) {
var zoomedIndex = $shiftables.nodes.indexOf($img.first());
var highestNode = null;
var highestTop = null;
for (var i = 1; i <= 4 ; i++) {
var nextIndex = zoomedIndex + i;
if (nextIndex < $shiftables.nodes.length) {
var node = $shiftables.nodes[nextIndex];
var nodeTop = node.getBoundingClientRect().top;
if (highestTop == null || nodeTop < highestTop) {
highestNode = $shiftables.nodes[nextIndex];
highestTop = nodeTop;
}
}
}
if (highestNode) {
return u(highestNode);
}
return null;
}
function previousLowestShiftable($img, $shiftables) {
var zoomedIndex = $shiftables.nodes.indexOf($img.first());
var lowestNode = null;
var lowestBottom = null;
for (var i = 1; i <= 4 ; i++) {
var previousIndex = zoomedIndex - i;
if (previousIndex >= 0) {
var node = $shiftables.nodes[previousIndex];
var nodeBottom = node.getBoundingClientRect().bottom;
if (lowestBottom == null || nodeBottom > lowestBottom) {
lowestNode = $shiftables.nodes[previousIndex];
lowestBottom = nodeBottom;
}
}
}
if (lowestNode) {
return u(lowestNode);
}
return null;
}
function previousShiftable($img, $shiftables) {
var prevIndex = $shiftables.nodes.indexOf($img.first()) - 1;
if (prevIndex >= 0) {
return u($shiftables.nodes[prevIndex]);
}
return null;
}
function activateScrollThreshold($img, $shiftables) {
var scrollBuffer = 50;
var imgBottom = $img.absBottom();
var bottomThreshold = scrollBuffer + imgBottom;
var topThreshold = window.pageYOffset - scrollBuffer;
checkScrollThreshold = function(){
var windowTop = window.pageYOffset;
var windowBottom = windowTop + window.innerHeight;
if (windowBottom > bottomThreshold || windowTop < topThreshold) {
unzoom($img, $shiftables);
}
}
unzoomOnResize = function(){
unzoom($img, $shiftables);
}
window.addEventListener('scroll', checkScrollThreshold);
window.addEventListener('resize', unzoomOnResize);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment