zoom.min.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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. +function ($) { "use strict";
  8. /**
  9. * The zoom service
  10. */
  11. function ZoomService () {
  12. this._activeZoom =
  13. this._initialScrollPosition =
  14. this._initialTouchPosition =
  15. this._touchMoveListener = null
  16. this._$document = $(document)
  17. this._$window = $(window)
  18. this._$body = $(document.body)
  19. this._boundClick = $.proxy(this._clickHandler, this)
  20. }
  21. ZoomService.prototype.listen = function () {
  22. this._$body.on('click', '[data-action="zoom"]', $.proxy(this._zoom, this))
  23. }
  24. ZoomService.prototype._zoom = function (e) {
  25. var target = e.target
  26. if (!target || target.tagName != 'IMG') return
  27. if (this._$body.hasClass('zoom-overlay-open')) return
  28. if (e.metaKey || e.ctrlKey) {
  29. return window.open((e.target.getAttribute('data-original') || e.target.src), '_blank')
  30. }
  31. //if (target.width >= ($(window).width() - Zoom.OFFSET)) return
  32. this._activeZoomClose(true)
  33. this._activeZoom = new Zoom(target)
  34. this._activeZoom.zoomImage()
  35. // todo(fat): probably worth throttling this
  36. this._$window.on('scroll.zoom', $.proxy(this._scrollHandler, this))
  37. this._$document.on('keyup.zoom', $.proxy(this._keyHandler, this))
  38. this._$document.on('touchstart.zoom', $.proxy(this._touchStart, this))
  39. // we use a capturing phase here to prevent unintended js events
  40. // sadly no useCapture in jquery api (http://bugs.jquery.com/ticket/14953)
  41. if (document.addEventListener) {
  42. document.addEventListener('click', this._boundClick, true)
  43. } else {
  44. document.attachEvent('onclick', this._boundClick, true)
  45. }
  46. if ('bubbles' in e) {
  47. if (e.bubbles) e.stopPropagation()
  48. } else {
  49. // Internet Explorer before version 9
  50. e.cancelBubble = true
  51. }
  52. }
  53. ZoomService.prototype._activeZoomClose = function (forceDispose) {
  54. if (!this._activeZoom) return
  55. if (forceDispose) {
  56. this._activeZoom.dispose()
  57. } else {
  58. this._activeZoom.close()
  59. }
  60. this._$window.off('.zoom')
  61. this._$document.off('.zoom')
  62. document.removeEventListener('click', this._boundClick, true)
  63. this._activeZoom = null
  64. }
  65. ZoomService.prototype._scrollHandler = function (e) {
  66. if (this._initialScrollPosition === null) this._initialScrollPosition = $(window).scrollTop()
  67. var deltaY = this._initialScrollPosition - $(window).scrollTop()
  68. if (Math.abs(deltaY) >= 40) this._activeZoomClose()
  69. }
  70. ZoomService.prototype._keyHandler = function (e) {
  71. if (e.keyCode == 27) this._activeZoomClose()
  72. }
  73. ZoomService.prototype._clickHandler = function (e) {
  74. if (e.preventDefault) e.preventDefault()
  75. else event.returnValue = false
  76. if ('bubbles' in e) {
  77. if (e.bubbles) e.stopPropagation()
  78. } else {
  79. // Internet Explorer before version 9
  80. e.cancelBubble = true
  81. }
  82. this._activeZoomClose()
  83. }
  84. ZoomService.prototype._touchStart = function (e) {
  85. this._initialTouchPosition = e.touches[0].pageY
  86. $(e.target).on('touchmove.zoom', $.proxy(this._touchMove, this))
  87. }
  88. ZoomService.prototype._touchMove = function (e) {
  89. if (Math.abs(e.touches[0].pageY - this._initialTouchPosition) > 10) {
  90. this._activeZoomClose()
  91. $(e.target).off('touchmove.zoom')
  92. }
  93. }
  94. /**
  95. * The zoom object
  96. */
  97. function Zoom (img) {
  98. this._fullHeight =
  99. this._fullWidth =
  100. this._overlay =
  101. this._targetImageWrap = null
  102. this._targetImage = img
  103. this._$body = $(document.body)
  104. }
  105. Zoom.OFFSET = 80
  106. Zoom._MAX_WIDTH = 2560
  107. Zoom._MAX_HEIGHT = 4096
  108. Zoom.prototype.zoomImage = function () {
  109. var img = document.createElement('img')
  110. img.onload = $.proxy(function () {
  111. this._fullHeight = Number(img.height)
  112. this._fullWidth = Number(img.width)
  113. this._zoomOriginal()
  114. }, this)
  115. img.src = this._targetImage.src
  116. }
  117. Zoom.prototype._zoomOriginal = function () {
  118. this._targetImageWrap = document.createElement('div')
  119. this._targetImageWrap.className = 'zoom-img-wrap'
  120. this._targetImage.parentNode.insertBefore(this._targetImageWrap, this._targetImage)
  121. this._targetImageWrap.appendChild(this._targetImage)
  122. $(this._targetImage)
  123. .addClass('zoom-img')
  124. .attr('data-action', 'zoom-out')
  125. this._overlay = document.createElement('div')
  126. this._overlay.className = 'zoom-overlay'
  127. document.body.appendChild(this._overlay)
  128. this._calculateZoom()
  129. this._triggerAnimation()
  130. }
  131. Zoom.prototype._calculateZoom = function () {
  132. this._targetImage.offsetWidth // repaint before animating
  133. var originalFullImageWidth = this._fullWidth
  134. var originalFullImageHeight = this._fullHeight
  135. var scrollTop = $(window).scrollTop()
  136. var maxScaleFactor = originalFullImageWidth / this._targetImage.width
  137. var viewportHeight = ($(window).height() - Zoom.OFFSET)
  138. var viewportWidth = ($(window).width() - Zoom.OFFSET)
  139. var imageAspectRatio = originalFullImageWidth / originalFullImageHeight
  140. var viewportAspectRatio = viewportWidth / viewportHeight
  141. if (originalFullImageWidth < viewportWidth && originalFullImageHeight < viewportHeight) {
  142. this._imgScaleFactor = maxScaleFactor
  143. } else if (imageAspectRatio < viewportAspectRatio) {
  144. this._imgScaleFactor = (viewportHeight / originalFullImageHeight) * maxScaleFactor
  145. } else {
  146. this._imgScaleFactor = (viewportWidth / originalFullImageWidth) * maxScaleFactor
  147. }
  148. }
  149. Zoom.prototype._triggerAnimation = function () {
  150. this._targetImage.offsetWidth // repaint before animating
  151. var imageOffset = $(this._targetImage).offset()
  152. var scrollTop = $(window).scrollTop()
  153. var viewportY = scrollTop + ($(window).height() / 2)
  154. var viewportX = ($(window).width() / 2)
  155. var imageCenterY = imageOffset.top + (this._targetImage.height / 2)
  156. var imageCenterX = imageOffset.left + (this._targetImage.width / 2)
  157. this._translateY = viewportY - imageCenterY
  158. this._translateX = viewportX - imageCenterX
  159. var targetTransform = 'scale(' + this._imgScaleFactor + ')'
  160. var imageWrapTransform = 'translate(' + this._translateX + 'px, ' + this._translateY + 'px)'
  161. if ($.support.transition) {
  162. imageWrapTransform += ' translateZ(0)'
  163. }
  164. $(this._targetImage)
  165. .css({
  166. '-webkit-transform': targetTransform,
  167. '-ms-transform': targetTransform,
  168. 'transform': targetTransform
  169. })
  170. $(this._targetImageWrap)
  171. .css({
  172. '-webkit-transform': imageWrapTransform,
  173. '-ms-transform': imageWrapTransform,
  174. 'transform': imageWrapTransform
  175. })
  176. this._$body.addClass('zoom-overlay-open')
  177. }
  178. Zoom.prototype.close = function () {
  179. this._$body
  180. .removeClass('zoom-overlay-open')
  181. .addClass('zoom-overlay-transitioning')
  182. // we use setStyle here so that the correct vender prefix for transform is used
  183. $(this._targetImage)
  184. .css({
  185. '-webkit-transform': '',
  186. '-ms-transform': '',
  187. 'transform': ''
  188. })
  189. $(this._targetImageWrap)
  190. .css({
  191. '-webkit-transform': '',
  192. '-ms-transform': '',
  193. 'transform': ''
  194. })
  195. $(this._targetImage)
  196. .one("transitionend", $.proxy(this.dispose, this))
  197. }
  198. Zoom.prototype.dispose = function (e) {
  199. if (this._targetImageWrap && this._targetImageWrap.parentNode) {
  200. $(this._targetImage)
  201. .removeClass('zoom-img')
  202. .attr('data-action', 'zoom')
  203. this._targetImageWrap.parentNode.replaceChild(this._targetImage, this._targetImageWrap)
  204. this._overlay.parentNode.removeChild(this._overlay)
  205. this._$body.removeClass('zoom-overlay-transitioning')
  206. }
  207. }
  208. // wait for dom ready (incase script included before body)
  209. $(function () {
  210. new ZoomService().listen()
  211. })
  212. }(jQuery);