Skip to content

Commit ed5fee8

Browse files
committed
support touch events and refine mouse event handling
support requestAnimationFrame for rendering during image manipulation use css transformations to manipulate image
1 parent 6772ff0 commit ed5fee8

File tree

14 files changed

+360
-74
lines changed

14 files changed

+360
-74
lines changed

Gruntfile.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ module.exports = function(grunt) {
3939
options: {
4040
urls: (function() {
4141
var res = [];
42-
['2.0.1', '1.10.0', '1.9.1', '1.8.3' ].forEach( function(jqversion) {
43-
['1.10.3', '1.9.2', '1.8.24'].forEach( function(uiversion) {
42+
['2.1.0', '2.0.3', '1.11.0', '1.10.2', '1.9.1', '1.8.3' ].forEach( function(jqversion) {
43+
['1.10.4', '1.9.2', '1.8.24'].forEach( function(uiversion) {
4444
this.push('http://localhost:<%= connect.server.options.port %>/test/imgViewer.html?jquery=' + jqversion + '&jquery-ui=' + uiversion);
4545
}, this);
4646
}, res);

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
imgViewer
22
=========
33

4-
imgViewer is a jQuery plugin that adds to an image the ability to zoom in and out with the mousewheel
5-
and pan around by click and drag. A unique feature of this plugin is it's ability to work on images
6-
which have widths or heights specified as a percentage of their container. Try out the [demo](http://waynegm.github.io/imgViewer/)
4+
imgViewer is a jQuery plugin that allows an image to be zoomed and panned. Zooming and panning works on desktop browsers using the mousewheel to zoom in and out left mouse button click and drag to pan around. On touch enabled devices pinch gestures can be used to zoom in and out and tap and drag to pan around. A unique feature of this plugin is it's ability to work on images which have widths or heights specified as a percentage of their container. Try out the [demo](http://waynegm.github.io/imgViewer/)
75

86
## Dependencies
97
The plugin is known to work with the configuration described below:
108

119
* [jQuery](http://jquery.com/) (>=1.8)
1210
* [jQuery UI](http://jqueryui.com/) (>=1.8)
1311
* [Widget Factory](http://api.jqueryui.com/jQuery.widget/)
14-
* [jQuery Mousewheel](http://brandonaaron.net/code/mousewheel/docs) (>=3.0)
12+
* [toe.js](https://github.com/visiongeist/toe.js) (>=3.0)
13+
* [Zoetrope](https://github.com/benplum/Zoetrope) (>=3.0)
1514

1615
## Usage
1716

@@ -23,7 +22,8 @@ Include either the development version or minified production version of the JS
2322
...
2423
<script src="jquery.js"></script>
2524
<script src="jquery-ui.js"></script>
26-
<script src="jquery.mousewheel.js"></script>
25+
<script src="jquery.fs.zoetrope.min.js"></script>
26+
<script src="toe.min.js"></script>
2727
<script src="imgViewer.min.js"></script>
2828
...
2929
</head>
@@ -175,4 +175,10 @@ may need refinement to increase it's flexibility and usefulness.
175175
### 0.6
176176
Major refactoring of the code to make it work in IE8. Instead of manipulating a background image a new image element
177177
with the same src as the original image is positioned over it. Added the panTo, getView, isVisible, imgtoView and viewToImg public methods.
178-
Added unit tests to cover most of the code.
178+
Added unit tests to cover most of the code.
179+
### 0.7
180+
Added support for pinch and drag touch gestures for mobile device support (adds requirement for toe.js).
181+
Added dependency on the requestAnimationFrame polyfill provided by Zoetrope for more responsive image scaling anddragging.
182+
Changed to using css transform to scale and translate image for better performance on mobile platforms.
183+
Minimum IE supported is now IE 9 - stick with version 0.6 if you need IE 8 support.
184+
Updated Grunfile.js to include tests against latest version (2.1.0) of jQuery.

demo.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
<script type="text/javascript" src="libs/jquery/jquery.js"></script>
77
<script type="text/javascript" src="libs/jquery/jquery-ui.js"></script>
8-
<script type="text/javascript" src="libs/jquery.mousewheel.min.js"></script>
8+
<script type="text/javascript" src="libs/jquery.fs.zoetrope.min.js"></script>
9+
<script type="text/javascript" src="libs/toe.min.js"></script>
910
<script type="text/javascript" src="src/imgViewer.js"></script>
1011
</head>
1112
<body>

dist/.directory

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[Dolphin]
2+
Timestamp=2014,5,17,10,4,8
3+
Version=3
4+
ViewMode=1

dist/imgViewer.js

Lines changed: 156 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,62 @@
1-
/*! jQuery imgViewer - v0.6.0 - 2013-06-06
1+
/*! jQuery imgViewer - v0.7.0 - 2014-05-17
22
* https://github.com/waynegm/imgViewer
3-
* Copyright (c) 2013 Wayne Mogg; Licensed MIT */
3+
* Copyright (c) 2014 Wayne Mogg; Licensed MIT */
4+
/*
5+
* Add a tap and drag gesture to toe.js
6+
*/
7+
;(function ($, touch, window, undefined) {
8+
9+
var namespace = 'drag',
10+
cfg = {
11+
distance: 40 // minimum
12+
},
13+
started;
14+
15+
touch.track(namespace, {
16+
touchstart: function (event, state, start) {
17+
started = false;
18+
state[namespace] = {
19+
finger: start.point.length,
20+
start: start,
21+
deltaX: 0,
22+
deltaY: 0
23+
};
24+
},
25+
touchmove: function (event, state, move) {
26+
var opt = $.extend(cfg, event.data);
27+
28+
// if another finger was used then increment the amount of fingers used
29+
state[namespace].finger = move.point.length > state[namespace].finger ? move.point.length : state[namespace].finger;
30+
31+
var distance = touch.calc.getDistance(state.start.point[0], move.point[0]);
32+
if (Math.abs(1 - distance) > opt.distance) {
33+
if (!started) {
34+
$(event.target).trigger($.Event('dragstart', state[namespace]));
35+
started = true;
36+
}
37+
state[namespace].deltaX = (move.point[0].x - state.start.point[0].x);
38+
state[namespace].deltaY = (move.point[0].y - state.start.point[0].y);
39+
$(event.target).trigger($.Event('drag', state[namespace]));
40+
}
41+
},
42+
touchend: function (event, state, end) {
43+
if (started) {
44+
started = false;
45+
46+
var distance = touch.calc.getDistance(state.start.point[0], end.point[0]);
47+
if (distance > cfg.distance) {
48+
state[namespace].deltaX = (end.point[0].x - state.start.point[0].x);
49+
state[namespace].deltaY = (end.point[0].y - state.start.point[0].y);
50+
$(event.target).trigger($.Event('dragend', state[namespace]));
51+
}
52+
}
53+
}
54+
});
55+
}(jQuery, jQuery.toe, this));
56+
57+
/*
58+
* imgViewer plugin starts here
59+
*/
460
;(function($) {
561
$.widget("wgm.imgViewer", {
662
options: {
@@ -76,7 +132,8 @@
76132
top: 0+"px",
77133
left: 0+"px",
78134
width: width+"px",
79-
height: height+"px"
135+
height: height+"px",
136+
"-webkit-tap-highlight-color": "transparent"
80137
});
81138
// the initial view is centered at the orignal image
82139
self.vCenter = {
@@ -88,29 +145,109 @@
88145
if (this.complete) { $(this).load(); }
89146
});
90147
/*
91-
* Mousewheel event handler for image zooming
92-
*/
93-
$zimg.mousewheel(function(event, delta) {
94-
event.preventDefault();
95-
self.options.zoom -= delta * self.options.zoomStep;
96-
self.update();
148+
* Render loop code during dragging and scaling using requestAnimationFrame
149+
*/
150+
self.render = false;
151+
function startRenderLoop() {
152+
if (!self.render) {
153+
self.render = true;
154+
doRender();
155+
}
156+
}
157+
158+
function stopRenderLoop() {
159+
self.render = false;
160+
}
161+
162+
function doRender() {
163+
if (self.render) {
164+
window.requestAnimationFrame(doRender);
165+
self.update();
166+
}
167+
}
168+
/*
169+
* Touch event handlers
170+
*/
171+
$zimg.on('touchstart touchmove touchend', function(ev) {
172+
ev.preventDefault();
173+
});
174+
175+
$zimg.on( "tap" , function(ev) {
176+
if (!self.dragging) {
177+
ev.preventDefault();
178+
self._trigger("onClick", ev, self);
179+
}
180+
});
181+
$zimg.on( "transformstart" , function(ev) {
182+
ev.preventDefault();
183+
self.pinchzoom = self.options.zoom;
184+
startRenderLoop();
185+
});
186+
$zimg.on("transform", function(ev) {
187+
ev.preventDefault();
188+
self.options.zoom = self.pinchzoom * ev.scale;
189+
});
190+
$zimg.on("transformend", function(ev) {
191+
ev.preventDefault();
192+
self.options.zoom = self.pinchzoom * ev.scale;
193+
stopRenderLoop();
194+
});
195+
196+
$zimg.on( "dragstart" , function(ev) {
197+
ev.preventDefault();
198+
self.dragging = true;
199+
self.dragXorg = self.vCenter.x;
200+
self.dragYorg = self.vCenter.y;
201+
startRenderLoop();
202+
});
203+
$zimg.on( "drag", function(ev) {
204+
ev.preventDefault();
205+
self.vCenter.x = self.dragXorg - ev.deltaX/self.options.zoom;
206+
self.vCenter.y = self.dragYorg - ev.deltaY/self.options.zoom;
207+
});
208+
209+
$zimg.on( "dragend", function(ev) {
210+
ev.preventDefault();
211+
self.dragging = false;
212+
self.vCenter.x = self.dragXorg - ev.deltaX/self.options.zoom;
213+
self.vCenter.y = self.dragYorg - ev.deltaY/self.options.zoom;
214+
stopRenderLoop();
97215
});
216+
98217
/*
99-
* Mouse drag handler for image panning
218+
* Mouse event handlers
100219
*/
220+
function MouseWheelHandler(ev) {
221+
ev.preventDefault();
222+
var e = ev.originalEvent;
223+
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
224+
self.options.zoom -= delta * self.options.zoomStep;
225+
self.update();
226+
}
227+
$zimg.on("mousewheel", MouseWheelHandler);
228+
$zimg.on("DOMMouseScroll", MouseWheelHandler);
229+
$zimg.on("onmousewheel", MouseWheelHandler);
230+
231+
$zimg.click(function(e) {
232+
if (!self.dragging) {
233+
self._trigger("onClick", e, self);
234+
}
235+
});
236+
101237
$zimg.mousedown( function(e) {
102238
e.preventDefault();
239+
startRenderLoop();
103240
var last = e;
104241
$zimg.mousemove( function(e) {
105242
e.preventDefault();
106243
self.dragging = true;
107244
self.vCenter.x = self.vCenter.x - (e.pageX - last.pageX)/self.options.zoom;
108245
self.vCenter.y = self.vCenter.y - (e.pageY - last.pageY)/self.options.zoom;
109246
last = e;
110-
self.update();
111247
});
112248
function endDrag(e) {
113249
e.preventDefault();
250+
stopRenderLoop();
114251
setTimeout(function() { self.dragging = false; }, 0);
115252
$zimg.unbind("mousemove");
116253
$zimg.unbind("mouseup");
@@ -119,14 +256,7 @@
119256
$(document).one("mouseup", endDrag);
120257
$zimg.one("mouseup", endDrag);
121258
});
122-
/*
123-
* Mouse click handler - supply an action by defining the onClick option
124-
*/
125-
$zimg.click(function(e) {
126-
if (!self.dragging) {
127-
self._trigger("onClick", e, self);
128-
}
129-
});
259+
130260
/*
131261
* Window resize handler
132262
*/
@@ -148,7 +278,6 @@
148278
destroy: function() {
149279
var $zimg = $(this.zimg);
150280
$zimg.unbind("click");
151-
$zimg.unmousewheel();
152281
$(window).unbind("resize");
153282
$zimg.remove();
154283
$(this.view).remove();
@@ -331,6 +460,7 @@
331460
y: half_height
332461
};
333462
this.options.zoom = 1;
463+
zoom = 1;
334464
} else {
335465
zTop = Math.round(half_height - this.vCenter.y * zoom);
336466
zLeft = Math.round(half_width - this.vCenter.x * zoom);
@@ -363,11 +493,13 @@
363493
height: height+"px"
364494
});
365495
$(this.zimg).css({
366-
left: zLeft+"px",
367-
top: zTop+"px",
368-
width: zWidth+"px",
369-
height: zHeight+"px"
496+
width: width+"px",
497+
height: height+"px"
370498
});
499+
500+
var xt = -(this.vCenter.x - half_width)*zoom;
501+
var yt = -(this.vCenter.y - half_height)*zoom;
502+
$(this.zimg).css({transform: "translate(" + xt + "px," + yt + "px) scale(" + zoom + "," + zoom + ")" });
371503
/*
372504
* define the onUpdate option to do something after the image is redisplayed
373505
* probably shouldn't pass out the this object - need to think of something better

0 commit comments

Comments
 (0)