1
+ + function ( $ ) { "use strict" ;
2
+
3
+ /**
4
+ * The zoom service
5
+ */
6
+ function ZoomService ( ) {
7
+ this . _activeZoom =
8
+ this . _initialScrollPosition =
9
+ this . _initialTouchPosition =
10
+ this . _touchMoveListener = null
11
+
12
+ this . _$document = $ ( document )
13
+ this . _$window = $ ( window )
14
+ this . _$body = $ ( document . body )
15
+
16
+ this . _boundClick = $ . proxy ( this . _clickHandler , this )
17
+ }
18
+
19
+ ZoomService . prototype . listen = function ( ) {
20
+ this . _$body . on ( 'click' , '[data-action="zoom"]' , $ . proxy ( this . _zoom , this ) )
21
+ }
22
+
23
+ ZoomService . prototype . _zoom = function ( e ) {
24
+ var target = e . target
25
+
26
+ if ( ! target || target . tagName != 'IMG' ) return
27
+
28
+ if ( this . _$body . hasClass ( 'zoom-overlay-open' ) ) return
29
+
30
+ if ( e . metaKey || e . ctrlKey ) return window . open ( e . target . src , '_blank' )
31
+
32
+ if ( target . width >= ( window . innerWidth - Zoom . OFFSET ) ) return
33
+
34
+ this . _activeZoomClose ( true )
35
+
36
+ this . _activeZoom = new Zoom ( target )
37
+ this . _activeZoom . zoomImage ( )
38
+
39
+ // todo(fat): probably worth throttling this
40
+ this . _$window . on ( 'scroll.zoom' , $ . proxy ( this . _scrollHandler , this ) )
41
+
42
+ this . _$document . on ( 'keyup.zoom' , $ . proxy ( this . _keyHandler , this ) )
43
+ this . _$document . on ( 'touchstart.zoom' , $ . proxy ( this . _touchStart , this ) )
44
+
45
+ // we use a capturing phase here to prevent unintended js events
46
+ // sadly no useCapture in jquery api (http://bugs.jquery.com/ticket/14953)
47
+ document . addEventListener ( 'click' , this . _boundClick , true )
48
+
49
+ e . stopPropagation ( )
50
+ }
51
+
52
+ ZoomService . prototype . _activeZoomClose = function ( forceDispose ) {
53
+ if ( ! this . _activeZoom ) return
54
+
55
+ if ( forceDispose ) {
56
+ this . _activeZoom . dispose ( )
57
+ } else {
58
+ this . _activeZoom . close ( )
59
+ }
60
+
61
+ this . _$window . off ( '.zoom' )
62
+ this . _$document . off ( '.zoom' )
63
+
64
+ document . removeEventListener ( 'click' , this . _boundClick , true )
65
+
66
+ this . _activeZoom = null
67
+ }
68
+
69
+ ZoomService . prototype . _scrollHandler = function ( e ) {
70
+ if ( this . _initialScrollPosition === null ) this . _initialScrollPosition = window . scrollY
71
+ var deltaY = this . _initialScrollPosition - window . scrollY
72
+ if ( Math . abs ( deltaY ) >= 40 ) this . _activeZoomClose ( )
73
+ }
74
+
75
+ ZoomService . prototype . _keyHandler = function ( e ) {
76
+ if ( e . keyCode == 27 ) this . _activeZoomClose ( )
77
+ }
78
+
79
+ ZoomService . prototype . _clickHandler = function ( e ) {
80
+ e . stopPropagation ( )
81
+ e . preventDefault ( )
82
+ this . _activeZoomClose ( )
83
+ }
84
+
85
+ ZoomService . prototype . _touchStart = function ( e ) {
86
+ this . _initialTouchPosition = e . touches [ 0 ] . pageY
87
+ $ ( e . target ) . on ( 'touchmove.zoom' , $ . proxy ( this . _touchMove , this ) )
88
+ }
89
+
90
+ ZoomService . prototype . _touchMove = function ( e ) {
91
+ if ( Math . abs ( e . touches [ 0 ] . pageY - this . _initialTouchPosition ) > 10 ) {
92
+ this . _activeZoomClose ( )
93
+ $ ( e . target ) . off ( 'touchmove.zoom' )
94
+ }
95
+ }
96
+
97
+ /**
98
+ * The zoom object
99
+ */
100
+ function Zoom ( img ) {
101
+ this . _fullHeight =
102
+ this . _fullWidth =
103
+ this . _overlay =
104
+ this . _targetImageWrap = null
105
+
106
+ this . _targetImage = img
107
+
108
+ this . _$body = $ ( document . body )
109
+
110
+ this . _transitionDuration = 300
111
+ }
112
+
113
+ Zoom . OFFSET = 80 //margins
114
+
115
+ Zoom . prototype . zoomImage = function ( ) {
116
+ var img = document . createElement ( 'img' )
117
+ img . onload = $ . proxy ( function ( ) {
118
+ this . _fullHeight = Number ( img . height )
119
+ this . _fullWidth = Number ( img . width )
120
+ this . _zoomOriginal ( )
121
+ } , this )
122
+ img . src = this . _targetImage . src
123
+ }
124
+
125
+ Zoom . prototype . _zoomOriginal = function ( ) {
126
+ this . _targetImageWrap = document . createElement ( 'div' )
127
+ this . _targetImageWrap . className = 'zoom-img-wrap'
128
+
129
+ this . _targetImage . parentNode . insertBefore ( this . _targetImageWrap , this . _targetImage )
130
+ this . _targetImageWrap . appendChild ( this . _targetImage )
131
+
132
+ $ ( this . _targetImage )
133
+ . addClass ( 'zoom-img' )
134
+ . attr ( 'data-action' , 'zoom-out' )
135
+
136
+ this . _overlay = document . createElement ( 'div' )
137
+ this . _overlay . className = 'zoom-overlay'
138
+
139
+ document . body . appendChild ( this . _overlay )
140
+
141
+ this . _calculateZoom ( )
142
+ this . _triggerAnimation ( )
143
+ }
144
+
145
+ Zoom . prototype . _calculateZoom = function ( ) {
146
+ this . _targetImage . offsetWidth // repaint before animating
147
+
148
+ var originalFullImageWidth = this . _fullWidth
149
+ var originalFullImageHeight = this . _fullHeight
150
+
151
+ var scrollTop = window . scrollY
152
+
153
+ var viewportHeight = ( window . innerHeight - Zoom . OFFSET )
154
+ var viewportWidth = ( window . innerWidth - Zoom . OFFSET )
155
+
156
+ var viewportAspectRatio = viewportWidth / viewportHeight
157
+
158
+ var imageAspectRatio = originalFullImageWidth / originalFullImageHeight
159
+ var imageTargetAspectRatio = this . _targetImage . width / this . _targetImage . height
160
+
161
+ this . _trueHeight = this . _targetImage . height
162
+ this . _trueWidth = this . _targetImage . width
163
+
164
+ if ( imageAspectRatio < imageTargetAspectRatio ) {
165
+ this . _trueHeight = ( this . _fullHeight * this . _targetImage . width ) / this . _fullWidth
166
+
167
+ } else {
168
+ this . _trueWidth = ( this . _fullWidth * this . _targetImage . height ) / this . _fullHeight
169
+ }
170
+
171
+ var maxScaleFactor = originalFullImageWidth / this . _trueWidth
172
+
173
+ if ( originalFullImageWidth < viewportWidth && originalFullImageHeight < viewportHeight ) {
174
+ this . _imgScaleFactor = maxScaleFactor
175
+
176
+ } else if ( imageAspectRatio < viewportAspectRatio ) {
177
+ this . _imgScaleFactor = ( viewportHeight / originalFullImageHeight ) * maxScaleFactor
178
+
179
+ } else {
180
+ this . _imgScaleFactor = ( viewportWidth / originalFullImageWidth ) * maxScaleFactor
181
+ }
182
+ }
183
+
184
+ Zoom . prototype . _triggerAnimation = function ( ) {
185
+ this . _targetImage . offsetWidth // repaint before animating
186
+
187
+ var imageOffset = $ ( this . _targetImage ) . offset ( )
188
+ var scrollTop = $ ( window ) . scrollTop ( )
189
+
190
+ var viewportY = scrollTop + ( window . innerHeight / 2 )
191
+ var viewportX = ( window . innerWidth / 2 )
192
+
193
+ var imageCenterY = imageOffset . top + ( this . _trueHeight / 2 )
194
+ var imageCenterX = imageOffset . left + ( this . _trueWidth / 2 )
195
+
196
+ this . _translateY = viewportY - imageCenterY
197
+ this . _translateX = viewportX - imageCenterX
198
+
199
+ $ ( this . _targetImage ) . velocity ( {
200
+ scale : this . _imgScaleFactor ,
201
+ height : this . _trueHeight ,
202
+ width : this . _trueWidth ,
203
+ } , this . _transitionDuration ) ;
204
+
205
+ $ ( this . _targetImageWrap ) . velocity ( {
206
+ translateX : this . _translateX ,
207
+ translateY : this . _translateY ,
208
+ translateZ : 0 ,
209
+ } , this . _transitionDuration ) ;
210
+
211
+
212
+ this . _$body . addClass ( 'zoom-overlay-open' )
213
+
214
+ }
215
+
216
+ Zoom . prototype . close = function ( ) {
217
+ this . _$body
218
+ . removeClass ( 'zoom-overlay-open' )
219
+ . addClass ( 'zoom-overlay-transitioning' )
220
+
221
+ $ ( this . _targetImage ) . velocity ( 'reverse' , { duration : this . _transitionDuration } ) ;
222
+
223
+ var myself = this
224
+
225
+ $ ( this . _targetImageWrap ) . velocity (
226
+ {
227
+ translateX : 0 ,
228
+ translateY : 0 ,
229
+ } ,
230
+ {
231
+ duration : this . _transitionDuration ,
232
+ complete : function ( elements ) {
233
+ console . log ( "Done animating the scale property." ) ;
234
+ myself . dispose ( ) // should probably use proxy here
235
+ }
236
+ } ) ;
237
+
238
+ }
239
+
240
+ Zoom . prototype . dispose = function ( ) {
241
+ if ( this . _targetImageWrap && this . _targetImageWrap . parentNode ) {
242
+ $ ( this . _targetImage )
243
+ . removeClass ( 'zoom-img' )
244
+ . attr ( 'data-action' , 'zoom' )
245
+ . removeAttr ( 'style' ) // ugh this does nothing
246
+
247
+ this . _targetImageWrap . parentNode . replaceChild ( this . _targetImage , this . _targetImageWrap )
248
+ this . _overlay . parentNode . removeChild ( this . _overlay )
249
+
250
+ this . _$body . removeClass ( 'zoom-overlay-transitioning' )
251
+ }
252
+ }
253
+
254
+ // wait for dom ready (incase script included before body)
255
+ $ ( function ( ) {
256
+ new ZoomService ( ) . listen ( )
257
+ } )
258
+
259
+ } ( jQuery )
0 commit comments