diff --git a/README.md b/README.md index a38146e..1636b37 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,4 @@ zoom.js should (in theory) work in all relevant browsers (ie9+). If not, create ### Who Written by @fat, made better by you. + diff --git a/css/zoom.css b/css/zoom.css old mode 100644 new mode 100755 index 679bccc..b894253 --- a/css/zoom.css +++ b/css/zoom.css @@ -1,41 +1,42 @@ -img[data-action="zoom"] { - cursor: pointer; - cursor: -webkit-zoom-in; - cursor: -moz-zoom-in; -} -.zoom-img, -.zoom-img-wrap { - position: relative; - z-index: 666; - -webkit-transition: all 300ms; - -o-transition: all 300ms; - transition: all 300ms; -} -img.zoom-img { - cursor: pointer; - cursor: -webkit-zoom-out; - cursor: -moz-zoom-out; -} -.zoom-overlay { - z-index: 420; - background: #fff; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - pointer-events: none; - filter: "alpha(opacity=0)"; - opacity: 0; - -webkit-transition: opacity 300ms; - -o-transition: opacity 300ms; - transition: opacity 300ms; -} -.zoom-overlay-open .zoom-overlay { - filter: "alpha(opacity=100)"; - opacity: 1; -} -.zoom-overlay-open, -.zoom-overlay-transitioning { - cursor: default; -} +img[data-action="zoom"] { + cursor: pointer; + cursor: -webkit-zoom-in; + cursor: -moz-zoom-in; +} +.zoom-img, +.zoom-img-wrap { + position: relative; + z-index: 666; + -webkit-transition: all 300ms; + -o-transition: all 300ms; + transition: all 300ms; +} +img.zoom-img { + cursor: pointer; + cursor: -webkit-zoom-out; + cursor: -moz-zoom-out; + max-width:unset; +} +.zoom-overlay { + z-index: 420; + background: #fff; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; + filter: "alpha(opacity=0)"; + opacity: 0; + -webkit-transition: opacity 300ms; + -o-transition: opacity 300ms; + transition: opacity 300ms; +} +.zoom-overlay-open .zoom-overlay { + filter: "alpha(opacity=100)"; + opacity: 1; +} +.zoom-overlay-open, +.zoom-overlay-transitioning { + cursor: default; +} \ No newline at end of file diff --git a/js/zoom.js b/js/zoom.js index 1348757..cf23f6c 100755 --- a/js/zoom.js +++ b/js/zoom.js @@ -1,278 +1,293 @@ -+function ($) { "use strict"; - - /** - * The zoom service - */ - function ZoomService () { - this._activeZoom = - this._initialScrollPosition = - this._initialTouchPosition = - this._touchMoveListener = null - - this._$document = $(document) - this._$window = $(window) - this._$body = $(document.body) - - this._boundClick = $.proxy(this._clickHandler, this) - } - - ZoomService.prototype.listen = function () { - this._$body.on('click', '[data-action="zoom"]', $.proxy(this._zoom, this)) - } - - ZoomService.prototype._zoom = function (e) { - var target = e.target - - if (!target || target.tagName != 'IMG') return - - if (this._$body.hasClass('zoom-overlay-open')) return - - if (e.metaKey || e.ctrlKey) { - return window.open((e.target.getAttribute('data-original') || e.target.src), '_blank') - } - - if (target.width >= ($(window).width() - Zoom.OFFSET)) return - - this._activeZoomClose(true) - - this._activeZoom = new Zoom(target) - this._activeZoom.zoomImage() - - // todo(fat): probably worth throttling this - this._$window.on('scroll.zoom', $.proxy(this._scrollHandler, this)) - - this._$document.on('keyup.zoom', $.proxy(this._keyHandler, this)) - this._$document.on('touchstart.zoom', $.proxy(this._touchStart, this)) - - // we use a capturing phase here to prevent unintended js events - // sadly no useCapture in jquery api (http://bugs.jquery.com/ticket/14953) - if (document.addEventListener) { - document.addEventListener('click', this._boundClick, true) - } else { - document.attachEvent('onclick', this._boundClick, true) - } - - if ('bubbles' in e) { - if (e.bubbles) e.stopPropagation() - } else { - // Internet Explorer before version 9 - e.cancelBubble = true - } - } - - ZoomService.prototype._activeZoomClose = function (forceDispose) { - if (!this._activeZoom) return - - if (forceDispose) { - this._activeZoom.dispose() - } else { - this._activeZoom.close() - } - - this._$window.off('.zoom') - this._$document.off('.zoom') - - document.removeEventListener('click', this._boundClick, true) - - this._activeZoom = null - } - - ZoomService.prototype._scrollHandler = function (e) { - if (this._initialScrollPosition === null) this._initialScrollPosition = $(window).scrollTop() - var deltaY = this._initialScrollPosition - $(window).scrollTop() - if (Math.abs(deltaY) >= 40) this._activeZoomClose() - } - - ZoomService.prototype._keyHandler = function (e) { - if (e.keyCode == 27) this._activeZoomClose() - } - - ZoomService.prototype._clickHandler = function (e) { - if (e.preventDefault) e.preventDefault() - else event.returnValue = false - - if ('bubbles' in e) { - if (e.bubbles) e.stopPropagation() - } else { - // Internet Explorer before version 9 - e.cancelBubble = true - } - - this._activeZoomClose() - } - - ZoomService.prototype._touchStart = function (e) { - this._initialTouchPosition = e.touches[0].pageY - $(e.target).on('touchmove.zoom', $.proxy(this._touchMove, this)) - } - - ZoomService.prototype._touchMove = function (e) { - if (Math.abs(e.touches[0].pageY - this._initialTouchPosition) > 10) { - this._activeZoomClose() - $(e.target).off('touchmove.zoom') - } - } - - - /** - * The zoom object - */ - function Zoom (img) { - this._fullHeight = - this._fullWidth = - this._overlay = - this._targetImageWrap = null - - this._targetImage = img - - this._$body = $(document.body) - } - - Zoom.OFFSET = 80 - Zoom._MAX_WIDTH = 2560 - Zoom._MAX_HEIGHT = 4096 - - Zoom.prototype.zoomImage = function () { - var img = document.createElement('img') - img.onload = $.proxy(function () { - this._fullHeight = Number(img.height) - this._fullWidth = Number(img.width) - this._zoomOriginal() - }, this) - img.src = this._targetImage.src - } - - Zoom.prototype._zoomOriginal = function () { - this._targetImageWrap = document.createElement('div') - this._targetImageWrap.className = 'zoom-img-wrap' - - this._targetImage.parentNode.insertBefore(this._targetImageWrap, this._targetImage) - this._targetImageWrap.appendChild(this._targetImage) - - $(this._targetImage) - .addClass('zoom-img') - .attr('data-action', 'zoom-out') - - this._overlay = document.createElement('div') - this._overlay.className = 'zoom-overlay' - - document.body.appendChild(this._overlay) - - this._calculateZoom() - this._triggerAnimation() - } - - Zoom.prototype._calculateZoom = function () { - this._targetImage.offsetWidth // repaint before animating - - var originalFullImageWidth = this._fullWidth - var originalFullImageHeight = this._fullHeight - - var scrollTop = $(window).scrollTop() - - var maxScaleFactor = originalFullImageWidth / this._targetImage.width - - var viewportHeight = ($(window).height() - Zoom.OFFSET) - var viewportWidth = ($(window).width() - Zoom.OFFSET) - - var imageAspectRatio = originalFullImageWidth / originalFullImageHeight - var viewportAspectRatio = viewportWidth / viewportHeight - - if (originalFullImageWidth < viewportWidth && originalFullImageHeight < viewportHeight) { - this._imgScaleFactor = maxScaleFactor - - } else if (imageAspectRatio < viewportAspectRatio) { - this._imgScaleFactor = (viewportHeight / originalFullImageHeight) * maxScaleFactor - - } else { - this._imgScaleFactor = (viewportWidth / originalFullImageWidth) * maxScaleFactor - } - } - - Zoom.prototype._triggerAnimation = function () { - this._targetImage.offsetWidth // repaint before animating - - var imageOffset = $(this._targetImage).offset() - var scrollTop = $(window).scrollTop() - - var viewportY = scrollTop + ($(window).height() / 2) - var viewportX = ($(window).width() / 2) - - var imageCenterY = imageOffset.top + (this._targetImage.height / 2) - var imageCenterX = imageOffset.left + (this._targetImage.width / 2) - - this._translateY = viewportY - imageCenterY - this._translateX = viewportX - imageCenterX - - var targetTransform = 'scale(' + this._imgScaleFactor + ')' - var imageWrapTransform = 'translate(' + this._translateX + 'px, ' + this._translateY + 'px)' - - if ($.support.transition) { - imageWrapTransform += ' translateZ(0)' - } - - $(this._targetImage) - .css({ - '-webkit-transform': targetTransform, - '-ms-transform': targetTransform, - 'transform': targetTransform - }) - - $(this._targetImageWrap) - .css({ - '-webkit-transform': imageWrapTransform, - '-ms-transform': imageWrapTransform, - 'transform': imageWrapTransform - }) - - this._$body.addClass('zoom-overlay-open') - } - - Zoom.prototype.close = function () { - this._$body - .removeClass('zoom-overlay-open') - .addClass('zoom-overlay-transitioning') - - // we use setStyle here so that the correct vender prefix for transform is used - $(this._targetImage) - .css({ - '-webkit-transform': '', - '-ms-transform': '', - 'transform': '' - }) - - $(this._targetImageWrap) - .css({ - '-webkit-transform': '', - '-ms-transform': '', - 'transform': '' - }) - - if (!$.support.transition) { - return this.dispose() - } - - $(this._targetImage) - .one($.support.transition.end, $.proxy(this.dispose, this)) - .emulateTransitionEnd(300) - } - - Zoom.prototype.dispose = function () { - if (this._targetImageWrap && this._targetImageWrap.parentNode) { - $(this._targetImage) - .removeClass('zoom-img') - .attr('data-action', 'zoom') - - this._targetImageWrap.parentNode.replaceChild(this._targetImage, this._targetImageWrap) - this._overlay.parentNode.removeChild(this._overlay) - - this._$body.removeClass('zoom-overlay-transitioning') - } - } - - // wait for dom ready (incase script included before body) - $(function () { - new ZoomService().listen() - }) - -}(jQuery) ++function ($) { + "use strict"; + + /** + * The zoom service + */ + function ZoomService() { + this._activeZoom = + this._initialScrollPosition = + this._initialTouchPosition = + this._touchMoveListener = null + + this._$document = $(document) + this._$window = $(window) + this._$body = $(document.body) + + this._boundClick = $.proxy(this._clickHandler, this) + } + + ZoomService.prototype.listen = function () { + this._$body.on('click', '[data-action="zoom"]', $.proxy(this._zoom, this)) + } + + ZoomService.prototype._zoom = function (e) { + var target = e.target + + if (!target || target.tagName != 'IMG') return + + if (this._$body.hasClass('zoom-overlay-open')) return + + if (e.metaKey || e.ctrlKey) { + return window.open((e.target.getAttribute('data-original') || e.target.src), '_blank') + } + + if (target.width >= ($(window).width() - Zoom.OFFSET)) return + + this._activeZoomClose(true) + + this._activeZoom = new Zoom(target) + this._activeZoom.zoomImage() + + // todo(fat): probably worth throttling this + //this._$window.on('scroll.zoom', $.proxy(this._scrollHandler, this)) + + this._$document.on('keyup.zoom', $.proxy(this._keyHandler, this)) + this._$document.on('touchstart.zoom', $.proxy(this._touchStart, this)) + + // we use a capturing phase here to prevent unintended js events + // sadly no useCapture in jquery api (http://bugs.jquery.com/ticket/14953) + if (document.addEventListener) { + document.addEventListener('click', this._boundClick, true) + } else { + document.attachEvent('onclick', this._boundClick, true) + } + + if ('bubbles' in e) { + if (e.bubbles) e.stopPropagation() + } else { + // Internet Explorer before version 9 + e.cancelBubble = true + } + } + + ZoomService.prototype._activeZoomClose = function (forceDispose) { + if (!this._activeZoom) return + + if (forceDispose) { + this._activeZoom.dispose() + } else { + this._activeZoom.close() + } + + this._$window.off('.zoom') + this._$document.off('.zoom') + + document.removeEventListener('click', this._boundClick, true) + + this._activeZoom = null + } + + ZoomService.prototype._scrollHandler = function (e) { + if (this._initialScrollPosition === null) this._initialScrollPosition = $(window).scrollTop() + var deltaY = this._initialScrollPosition - $(window).scrollTop() + if (Math.abs(deltaY) >= 40) this._activeZoomClose() + } + + ZoomService.prototype._keyHandler = function (e) { + if (e.keyCode == 27) this._activeZoomClose() + } + + ZoomService.prototype._clickHandler = function (e) { + if (e.preventDefault) e.preventDefault() + else event.returnValue = false + + if ('bubbles' in e) { + if (e.bubbles) e.stopPropagation() + } else { + // Internet Explorer before version 9 + e.cancelBubble = true + } + + this._activeZoomClose() + } + + ZoomService.prototype._touchStart = function (e) { + this._initialTouchPosition = e.touches[0].pageY + $(e.target).on('touchmove.zoom', $.proxy(this._touchMove, this)) + } + + ZoomService.prototype._touchMove = function (e) { + if (Math.abs(e.touches[0].pageY - this._initialTouchPosition) > 10) { + this._activeZoomClose() + $(e.target).off('touchmove.zoom') + } + } + + + /** + * The zoom object + */ + function Zoom(img) { + this._fullHeight = + this._fullWidth = + this._overlay = + this._targetImageWrap = null + + this._targetImage = img + + this._$body = $(document.body) + } + + Zoom.OFFSET = 80 + Zoom._MAX_WIDTH = 2560 + Zoom._MAX_HEIGHT = 4096 + + Zoom.prototype.zoomImage = function () { + var img = document.createElement('img') + img.onload = $.proxy(function () { + this._fullHeight = Number(img.height) + this._fullWidth = Number(img.width) + this._zoomOriginal() + }, this) + img.src = this._targetImage.src + } + + Zoom.prototype._zoomOriginal = function () { + this._targetImageWrap = document.createElement('div') + this._targetImageWrap.className = 'zoom-img-wrap' + + this._targetImage.parentNode.insertBefore(this._targetImageWrap, this._targetImage) + this._targetImageWrap.appendChild(this._targetImage) + + $(this._targetImage) + .addClass('zoom-img') + .attr('data-action', 'zoom-out') + + this._overlay = document.createElement('div') + this._overlay.className = 'zoom-overlay' + + document.body.appendChild(this._overlay) + + this._calculateZoom() + this._triggerAnimation() + } + + Zoom.prototype._calculateZoom = function () { + this._targetImage.offsetWidth // repaint before animating + + var originalFullImageWidth = this._fullWidth + var originalFullImageHeight = this._fullHeight + + var scrollTop = $(window).scrollTop() + + var maxScaleFactor = originalFullImageWidth / this._targetImage.width + + var viewportHeight = ($(window).height() - Zoom.OFFSET) + var viewportWidth = ($(window).width() - Zoom.OFFSET) + + var imageAspectRatio = originalFullImageWidth / originalFullImageHeight + var viewportAspectRatio = viewportWidth / viewportHeight + + if (this._fullWidth > 1366 || this._fullHeight > 968) { + this._imgScaleFactor = maxScaleFactor + } else { + + if (originalFullImageWidth < viewportWidth && originalFullImageHeight < viewportHeight) { + this._imgScaleFactor = maxScaleFactor + + } else if (imageAspectRatio < viewportAspectRatio) { + this._imgScaleFactor = (viewportHeight / originalFullImageHeight) * maxScaleFactor + + } else { + this._imgScaleFactor = (viewportWidth / originalFullImageWidth) * maxScaleFactor + } + } + } + + Zoom.prototype._triggerAnimation = function () { + this._targetImage.offsetWidth // repaint before animating + + var imageOffset = $(this._targetImage).offset() + var scrollTop = $(window).scrollTop() + + var viewportY = scrollTop + ($(window).height() / 2) + var viewportX = ($(window).width() / 2) + + var imageCenterY = imageOffset.top + (this._targetImage.height / 2) + var imageCenterX = imageOffset.left + (this._targetImage.width / 2) + + this._translateY = viewportY - imageCenterY + this._translateX = viewportX - imageCenterX + + var targetTransform = 'scale(' + this._imgScaleFactor + ')' + var imageWrapTransform = 'translate(' + this._translateX + 'px, ' + this._translateY + 'px)' + + if ($.support.transition) { + imageWrapTransform += ' translateZ(0)' + } + if (this._fullHeight > 968 || this._fullWidth > 1366) { + $(this._targetImage) + .css({ + '-webkit-transform': targetTransform, + '-ms-transform': targetTransform, + 'transform': targetTransform, + 'top': -this._translateY + }) + } else { + $(this._targetImage) + .css({ + '-webkit-transform': targetTransform, + '-ms-transform': targetTransform, + 'transform': targetTransform + }) + } + + $(this._targetImageWrap) + .css({ + '-webkit-transform': imageWrapTransform, + '-ms-transform': imageWrapTransform, + 'transform': imageWrapTransform + }) + + this._$body.addClass('zoom-overlay-open') + } + + Zoom.prototype.close = function () { + this._$body + .removeClass('zoom-overlay-open') + .addClass('zoom-overlay-transitioning') + + // we use setStyle here so that the correct vender prefix for transform is used + $(this._targetImage) + .css({ + '-webkit-transform': '', + '-ms-transform': '', + 'transform': '' + }) + + $(this._targetImageWrap) + .css({ + '-webkit-transform': '', + '-ms-transform': '', + 'transform': '' + }) + + if (!$.support.transition) { + return this.dispose() + } + + $(this._targetImage) + .one($.support.transition.end, $.proxy(this.dispose, this)) + .emulateTransitionEnd(300) + } + + Zoom.prototype.dispose = function () { + if (this._targetImageWrap && this._targetImageWrap.parentNode) { + $(this._targetImage) + .removeClass('zoom-img') + .attr('data-action', 'zoom') + + this._targetImageWrap.parentNode.replaceChild(this._targetImage, this._targetImageWrap) + this._overlay.parentNode.removeChild(this._overlay) + + this._$body.removeClass('zoom-overlay-transitioning') + } + } + + // wait for dom ready (incase script included before body) + $(function () { + new ZoomService().listen() + }) + +}(jQuery) \ No newline at end of file