1/** 2 * zoom.js - It's the best way to zoom an image 3 * @version v0.0.2 4 * @link https://github.com/fat/zoom.js 5 * @license MIT 6 */ 7 8+function ($) { "use strict"; 9 10 /** 11 * The zoom service 12 */ 13 function ZoomService () { 14 this._activeZoom = 15 this._initialScrollPosition = 16 this._initialTouchPosition = 17 this._touchMoveListener = null 18 19 this._$document = $(document) 20 this._$window = $(window) 21 this._$body = $(document.body) 22 23 this._boundClick = $.proxy(this._clickHandler, this) 24 } 25 26 ZoomService.prototype.listen = function () { 27 this._$body.on('click', '[data-action="zoom"]', $.proxy(this._zoom, this)) 28 } 29 30 ZoomService.prototype._zoom = function (e) { 31 var target = e.target 32 33 if (!target || target.tagName != 'IMG') return 34 35 if (this._$body.hasClass('zoom-overlay-open')) return 36 37 if (e.metaKey || e.ctrlKey) { 38 return window.open((e.target.getAttribute('data-original') || e.target.src), '_blank') 39 } 40 41 if (target.width >= ($(window).width() - Zoom.OFFSET)) return 42 43 this._activeZoomClose(true) 44 45 this._activeZoom = new Zoom(target) 46 this._activeZoom.zoomImage() 47 48 // todo(fat): probably worth throttling this 49 this._$window.on('scroll.zoom', $.proxy(this._scrollHandler, this)) 50 51 this._$document.on('keyup.zoom', $.proxy(this._keyHandler, this)) 52 this._$document.on('touchstart.zoom', $.proxy(this._touchStart, this)) 53 54 // we use a capturing phase here to prevent unintended js events 55 // sadly no useCapture in jquery api (http://bugs.jquery.com/ticket/14953) 56 if (document.addEventListener) { 57 document.addEventListener('click', this._boundClick, true) 58 } else { 59 document.attachEvent('onclick', this._boundClick, true) 60 } 61 62 if ('bubbles' in e) { 63 if (e.bubbles) e.stopPropagation() 64 } else { 65 // Internet Explorer before version 9 66 e.cancelBubble = true 67 } 68 } 69 70 ZoomService.prototype._activeZoomClose = function (forceDispose) { 71 if (!this._activeZoom) return 72 73 if (forceDispose) { 74 this._activeZoom.dispose() 75 } else { 76 this._activeZoom.close() 77 } 78 79 this._$window.off('.zoom') 80 this._$document.off('.zoom') 81 82 document.removeEventListener('click', this._boundClick, true) 83 84 this._activeZoom = null 85 } 86 87 ZoomService.prototype._scrollHandler = function (e) { 88 if (this._initialScrollPosition === null) this._initialScrollPosition = $(window).scrollTop() 89 var deltaY = this._initialScrollPosition - $(window).scrollTop() 90 if (Math.abs(deltaY) >= 40) this._activeZoomClose() 91 } 92 93 ZoomService.prototype._keyHandler = function (e) { 94 if (e.keyCode == 27) this._activeZoomClose() 95 } 96 97 ZoomService.prototype._clickHandler = function (e) { 98 if (e.preventDefault) e.preventDefault() 99 else event.returnValue = false 100 101 if ('bubbles' in e) { 102 if (e.bubbles) e.stopPropagation() 103 } else { 104 // Internet Explorer before version 9 105 e.cancelBubble = true 106 } 107 108 this._activeZoomClose() 109 } 110 111 ZoomService.prototype._touchStart = function (e) { 112 this._initialTouchPosition = e.touches[0].pageY 113 $(e.target).on('touchmove.zoom', $.proxy(this._touchMove, this)) 114 } 115 116 ZoomService.prototype._touchMove = function (e) { 117 if (Math.abs(e.touches[0].pageY - this._initialTouchPosition) > 10) { 118 this._activeZoomClose() 119 $(e.target).off('touchmove.zoom') 120 } 121 } 122 123 124 /** 125 * The zoom object 126 */ 127 function Zoom (img) { 128 this._fullHeight = 129 this._fullWidth = 130 this._overlay = 131 this._targetImageWrap = null 132 133 this._targetImage = img 134 135 this._$body = $(document.body) 136 } 137 138 Zoom.OFFSET = 80 139 Zoom._MAX_WIDTH = 2560 140 Zoom._MAX_HEIGHT = 4096 141 142 Zoom.prototype.zoomImage = function () { 143 var img = document.createElement('img') 144 img.onload = $.proxy(function () { 145 this._fullHeight = Number(img.height) 146 this._fullWidth = Number(img.width) 147 this._zoomOriginal() 148 }, this) 149 img.src = this._targetImage.src 150 } 151 152 Zoom.prototype._zoomOriginal = function () { 153 this._targetImageWrap = document.createElement('div') 154 this._targetImageWrap.className = 'zoom-img-wrap' 155 156 this._targetImage.parentNode.insertBefore(this._targetImageWrap, this._targetImage) 157 this._targetImageWrap.appendChild(this._targetImage) 158 159 $(this._targetImage) 160 .addClass('zoom-img') 161 .attr('data-action', 'zoom-out') 162 163 this._overlay = document.createElement('div') 164 this._overlay.className = 'zoom-overlay' 165 166 document.body.appendChild(this._overlay) 167 168 this._calculateZoom() 169 this._triggerAnimation() 170 } 171 172 Zoom.prototype._calculateZoom = function () { 173 this._targetImage.offsetWidth // repaint before animating 174 175 var originalFullImageWidth = this._fullWidth 176 var originalFullImageHeight = this._fullHeight 177 178 var scrollTop = $(window).scrollTop() 179 180 var maxScaleFactor = originalFullImageWidth / this._targetImage.width 181
182 var viewportHeight = ($(window).height() - Zoom.OFFSET) 183 var viewportWidth = ($(window).width() - Zoom.OFFSET) 184 185 var imageAspectRatio = originalFullImageWidth / originalFullImageHeight 186 var viewportAspectRatio = viewportWidth / viewportHeight 187 188 if (originalFullImageWidth < viewportWidth && originalFullImageHeight < viewportHeight) { 189 this._imgScaleFactor = maxScaleFactor 190 191 } else if (imageAspectRatio < viewportAspectRatio) { 192 this._imgScaleFactor = (viewportHeight / originalFullImageHeight) * maxScaleFactor 193 194 } else { 195 this._imgScaleFactor = (viewportWidth / originalFullImageWidth) * maxScaleFactor 196 } 197 } 198 199 Zoom.prototype._triggerAnimation = function () { 200 this._targetImage.offsetWidth // repaint before animating 201 202 var imageOffset = $(this._targetImage).offset() 203 var scrollTop = $(window).scrollTop() 204 205 var viewportY = scrollTop + ($(window).height() / 2) 206 var viewportX = ($(window).width() / 2) 207 208 var imageCenterY = imageOffset.top + (this._targetImage.height / 2) 209 var imageCenterX = imageOffset.left + (this._targetImage.width / 2) 210 211 this._translateY = viewportY - imageCenterY 212 this._translateX = viewportX - imageCenterX 213 214 var targetTransform = 'scale(' + this._imgScaleFactor + ')' 215 var imageWrapTransform = 'translate(' + this._translateX + 'px, ' + this._translateY + 'px)' 216 217 if ($.support.transition) { 218 imageWrapTransform += ' translateZ(0)' 219 } 220 221 $(this._targetImage) 222 .css({ 223 '-webkit-transform': targetTransform, 224 '-ms-transform': targetTransform, 225 'transform': targetTransform 226 }) 227 228 $(this._targetImageWrap) 229 .css({ 230 '-webkit-transform': imageWrapTransform, 231 '-ms-transform': imageWrapTransform, 232 'transform': imageWrapTransform 233 }) 234 235 this._$body.addClass('zoom-overlay-open') 236 } 237 238 Zoom.prototype.close = function () { 239 this._$body 240 .removeClass('zoom-overlay-open') 241 .addClass('zoom-overlay-transitioning') 242 243 // we use setStyle here so that the correct vender prefix for transform is used 244 $(this._targetImage) 245 .css({ 246 '-webkit-transform': '', 247 '-ms-transform': '', 248 'transform': '' 249 }) 250 251 $(this._targetImageWrap) 252 .css({ 253 '-webkit-transform': '', 254 '-ms-transform': '', 255 'transform': '' 256 }) 257 258 if (!$.support.transition) { 259 return this.dispose() 260 } 261 262 $(this._targetImage) 263 .one($.support.transition.end, $.proxy(this.dispose, this)) 264 .emulateTransitionEnd(300) 265 } 266 267 Zoom.prototype.dispose = function () { 268 if (this._targetImageWrap && this._targetImageWrap.parentNode) { 269 $(this._targetImage) 270 .removeClass('zoom-img') 271 .attr('data-action', 'zoom') 272 273 this._targetImageWrap.parentNode.replaceChild(this._targetImage, this._targetImageWrap) 274 this._overlay.parentNode.removeChild(this._overlay) 275 276 this._$body.removeClass('zoom-overlay-transitioning') 277 } 278 } 279 280 // wait for dom ready (incase script included before body) 281 $(function () { 282 new ZoomService().listen() 283 }) 284 285}(jQuery)
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.