Skip to content
This repository was archived by the owner on Nov 19, 2019. It is now read-only.

Commit 0750c51

Browse files
committed
Initial commit
0 parents  commit 0750c51

File tree

6 files changed

+381
-0
lines changed

6 files changed

+381
-0
lines changed

css/zoom.css

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
img[data-action="zoom"] {
2+
cursor: pointer;
3+
cursor: -webkit-zoom-in;
4+
cursor: -moz-zoom-in;
5+
}
6+
7+
.zoom-img,
8+
.zoom-img-wrap {
9+
position: relative;
10+
z-index: 666;
11+
/*transition: transform 5s linear;*/
12+
}
13+
14+
.zoom-img-wrap {
15+
16+
}
17+
18+
img.zoom-img {
19+
cursor: pointer;
20+
cursor: -webkit-zoom-out;
21+
cursor: -moz-zoom-out;
22+
}
23+
24+
.zoom-overlay {
25+
z-index: 420;
26+
background: #fff;
27+
position: fixed;
28+
top: 0;
29+
left: 0;
30+
right: 0;
31+
bottom: 0;
32+
pointer-events: none;
33+
opacity: 0;
34+
transition: opacity 300ms;
35+
}
36+
37+
.zoom-overlay-open,
38+
.zoom-overlay-transitioning {
39+
cursor: default;
40+
}
41+
42+
.zoom-overlay-open .zoom-overlay {
43+
opacity: 1;
44+
}

img/palm.jpg

414 KB
Loading

img/trees.jpg

280 KB
Loading

index.html

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta name="viewport" content="width=device-width, initial-scale=1">
5+
6+
<style>
7+
/* SIMPLE DEMO STYLES */
8+
body {
9+
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
10+
font-size: 12px;
11+
line-height: 1.6;
12+
}
13+
.container {
14+
margin: 50px;
15+
max-width: 700px;
16+
}
17+
.container img {
18+
width: 100%;
19+
display: block;
20+
}
21+
.container .cropped {
22+
width: 80%;
23+
height: 50vh;
24+
margin: 0 10%; /*auto won't work here*/
25+
object-fit: cover;
26+
}
27+
@media (min-width: 750px) {
28+
body {
29+
font-size: 16px;
30+
line-height: 1.6;
31+
}
32+
.container {
33+
margin: 100px auto;
34+
}
35+
}
36+
</style>
37+
<link rel="stylesheet" type="text/css" href="css/zoom.css">
38+
</head>
39+
<body>
40+
<div class="container">
41+
<h1>Image Zoom</h1>
42+
<p>
43+
Trust fund seitan chia, wolf lomo letterpress Bushwick before they sold out. Carles kogi fixie, squid twee Tonx readymade cred typewriter scenester locavore kale chips vegan organic. Meggings pug wolf Shoreditch typewriter skateboard. McSweeney's iPhone chillwave, food truck direct trade disrupt flannel irony tousled Cosby sweater single-origin coffee. Organic disrupt bicycle rights, tattooed messenger bag flannel craft beer fashion axe bitters. Readymade sartorial craft beer, quinoa sustainable butcher Marfa Echo Park Terry Richardson gluten-free flannel retro cred mlkshk banjo. Salvia 90's art party Blue Bottle, PBR&B cardigan 8-bit.
44+
</p>
45+
<p>
46+
Meggings irony fashion axe, tattooed master cleanse Blue Bottle stumptown bitters authentic flannel freegan paleo letterpress ugh sriracha. Wolf PBR&B art party aesthetic meh cliche. Sartorial before they sold out deep v, aesthetic PBR&B craft beer post-ironic synth keytar pork belly skateboard pour-over. Tonx cray pug Etsy, gastropub ennui wolf ethnic Odd Future viral master cleanse skateboard banjo. Pitchfork scenester cornhole, whatever try-hard ethnic banjo +1 gastropub American Apparel vinyl skateboard Shoreditch seitan. Blue Bottle keffiyeh Austin Helvetica art party. Portland ethnic fixie, beard retro direct trade ugh scenester Tumblr readymade authentic plaid pickled hashtag biodiesel.
47+
</p>
48+
<img src="img/palm.jpg" data-action="zoom">
49+
<p>
50+
Thundercats freegan Truffaut, four loko twee Austin scenester lo-fi seitan High Life paleo quinoa cray. Schlitz butcher ethical Tumblr, pop-up DIY keytar ethnic iPhone PBR sriracha. Tonx direct trade bicycle rights gluten-free flexitarian asymmetrical. Whatever drinking vinegar PBR XOXO Bushwick gentrify. Cliche semiotics banjo retro squid Wes Anderson. Fashion axe dreamcatcher you probably haven't heard of them bicycle rights. Tote bag organic four loko ethical selfies gastropub, PBR fingerstache tattooed bicycle rights.
51+
</p>
52+
<p>
53+
Ugh Portland Austin, distillery tattooed typewriter polaroid pug Banksy Neutra keffiyeh. Shoreditch mixtape wolf PBR&B, tote bag dreamcatcher literally bespoke Odd Future selfies 90's master cleanse vegan. Flannel tofu deep v next level pickled, authentic Etsy Shoreditch literally swag photo booth iPhone pug semiotics banjo. Bicycle rights butcher Blue Bottle, actually DIY semiotics Banksy banjo mixtape Austin pork belly post-ironic. American Apparel gastropub hashtag, McSweeney's master cleanse occupy High Life bitters wayfarers next level bicycle rights. Wolf chia Terry Richardson, pop-up plaid kitsch ugh. Butcher +1 Carles, swag selfies Blue Bottle viral.
54+
</p>
55+
<p>
56+
Keffiyeh food truck organic letterpress leggings iPhone four loko hella pour-over occupy, Wes Anderson cray post-ironic. Neutra retro fixie gastropub +1, High Life semiotics. Vinyl distillery Etsy freegan flexitarian cliche jean shorts, Schlitz wayfarers skateboard tousled irony locavore XOXO meh. Ethnic Wes Anderson McSweeney's messenger bag, mixtape XOXO slow-carb cornhole aesthetic Marfa banjo Thundercats bitters. Raw denim banjo typewriter cray Tumblr, High Life single-origin coffee. 90's Tumblr cred, Terry Richardson occupy raw denim tofu fashion axe photo booth banh mi. Trust fund locavore Helvetica, fashion axe selvage authentic Shoreditch swag selfies stumptown +1.
57+
</p>
58+
<img src="img/trees.jpg" data-action="zoom" class="cropped">
59+
<p>
60+
Scenester chambray slow-carb, trust fund biodiesel ugh bicycle rights cornhole. Gentrify messenger bag Truffaut tousled roof party pork belly leggings, photo booth jean shorts. Austin readymade PBR plaid chambray. Squid Echo Park pour-over, wayfarers forage whatever locavore typewriter artisan deep v four loko. Locavore occupy Neutra Pitchfork McSweeney's, wayfarers fingerstache. Actually asymmetrical drinking vinegar yr brunch biodiesel. Before they sold out sustainable readymade craft beer Portland gastropub squid Austin, roof party Thundercats chambray narwhal Bushwick pug.
61+
</p>
62+
<p>
63+
Literally typewriter chillwave, bicycle rights Carles flannel wayfarers. Biodiesel farm-to-table actually, locavore keffiyeh hella shabby chic pour-over try-hard Bushwick. Sriracha American Apparel Brooklyn, synth cray stumptown blog Bushwick +1 VHS hashtag. Wolf umami Carles Marfa, 90's food truck Cosby sweater. Fanny pack try-hard keytar pop-up readymade, master cleanse four loko trust fund polaroid salvia. Photo booth kitsch forage chambray, Carles scenester slow-carb lomo cardigan dreamcatcher. Swag asymmetrical leggings, biodiesel Tonx shabby chic ethnic master cleanse freegan.
64+
</p>
65+
<p>
66+
Raw denim Banksy shabby chic, 8-bit salvia narwhal fashion axe. Ethical Williamsburg four loko, chia kale chips distillery Shoreditch messenger bag swag iPhone Pitchfork. Viral PBR&B single-origin coffee quinoa readymade, ethical chillwave drinking vinegar gluten-free Wes Anderson kitsch Tumblr synth actually bitters. Butcher McSweeney's forage mlkshk kogi fingerstache. Selvage scenester butcher Shoreditch, Carles beard plaid disrupt DIY. Pug readymade selvage retro, Austin salvia vinyl master cleanse flexitarian deep v bicycle rights plaid Terry Richardson mlkshk pour-over. Trust fund try-hard banh mi Brooklyn, 90's Etsy kogi YOLO salvia.
67+
</p>
68+
69+
</div>
70+
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
71+
<script src="js/zoom.js"></script>
72+
<script src="js/velocity.js"></script>
73+
</body>
74+
</html>

js/velocity.js

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/zoom.js

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
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

Comments
 (0)