Created
May 20, 2017 19:24
-
-
Save dallinskinner/3a741a69c28c5b0b1f71f1a8cfd0c04b to your computer and use it in GitHub Desktop.
Zoomable
This file contains hidden or 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
| 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