Skip to content

Instantly share code, notes, and snippets.

@gordonnl
Created October 6, 2014 09:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gordonnl/0816b1fd9ea3396e3e4e to your computer and use it in GitHub Desktop.
Save gordonnl/0816b1fd9ea3396e3e4e to your computer and use it in GitHub Desktop.
Drag and zoom controls with touch support.
// Requires jquery and detectizr (checks classes of 'touch', 'firefox' and 'ie' on the html element)
var dragZoom = (function() {
// The container - normally styled to be the size of the page with overflow hidden.
var _cont = $('.container');
// The child of container, who is going to scale and move.
var _content = $('.content');
// Edit this to set max scale.
var maxScale = 4;
// Minimum scale is automatically set to the size of the container, as to not show gaps.
var isTouch = $('html').hasClass('touch');
var isFirefox = $('html').hasClass('firefox');
var isIe = $('html').hasClass('ie');
var width = _content.width();
var height = _content.height();
var ratio = width / height;
var contTop = _cont.offset().top;
// These are attached in the resize function
var contWidth;
var contHeight;
var contRatio;
// The values that we will apply to the content
var matrix = {
s: 1,
x: 0,
y: 0
}
// Values a step behind to check what has changed
var checkMatrix = {
s: -1,
x: -1,
y: -1
}
// Mouse coords
var cur = {
x: 0,
y: 0
}
// Mouse diff coords - amount of movement to apply
var move = {
x: 0,
y: 0
}
var inertia = false;
var pinching = false;
var pinchScale;
var stopDraw = false;
// Turns matrix object into string
var printMatrix = function() {
//Give ie 2D matrix (3D matrixes are faster)
if (isIe) {
var str = 'matrix('
+ matrix.s +', '
+ '0, '
+ '0, '
+ matrix.s +', '
+ matrix.x +', '
+ matrix.y +')';
return str;
}
var str = 'matrix3d('
+ matrix.s +', 0, 0, 0, '
+ '0, '+matrix.s+', 0, 0, '
+ '0, 0, 1, 0, '
+ matrix.x+', '+matrix.y+', 0, 1'
+ ')';
return str;
};
var applyMatrix = function() {
_content[0].style.webkitTransform = printMatrix();
_content[0].style['-ms-transform'] = printMatrix();
_content[0].style.transform = printMatrix();
}
// Check to see if scale is too small for the container.
var checkScale = function() {
if (width * matrix.s < contWidth || height * matrix.s < contHeight) {
if ( ratio <= contRatio ) {
matrix.s = contWidth / width;
matrix.x = 0;
} else {
matrix.s = contHeight / height;
matrix.y = 0;
}
}
};
// Check border limits
var checkTranslation = function() {
var right = matrix.x + width * matrix.s;
var bottom = matrix.y + height * matrix.s;
matrix.x = matrix.x >= 0 ? 0 : right < contWidth ? contWidth - width * matrix.s : matrix.x;
matrix.y = matrix.y >= 0 ? 0 : bottom < contHeight ? contHeight - height * matrix.s : matrix.y;
};
// Every frame - where stuff happens
var draw = function() {
if (stopDraw) return;
requestAnimationFrame(draw);
if (inertia) {
matrix.x += move.x *= 0.85;
matrix.y += move.y *= 0.85;
if (Math.abs(move.x) < 0.001 && Math.abs(move.y) < 0.001 ) inertia = false;
}
// if nothing has moved, don't do anything
if (checkMatrix.x == matrix.x && checkMatrix.y == matrix.y && checkMatrix.s == matrix.s) return;
// Check limits before applying matix
checkScale();
checkTranslation();
// Apply movement
applyMatrix();
// Update old matrix for next check.
checkMatrix.x = matrix.x;
checkMatrix.y = matrix.y;
checkMatrix.s = matrix.s;
};
var drag = function(e) {
e = isTouch ? e.originalEvent.touches[0] : e;
move.x = e.clientX - cur.x;
move.y = e.clientY - cur.y;
matrix.x += move.x;
matrix.y += move.y;
cur.x = e.clientX;
cur.y = e.clientY;
};
var endDrag = function(e) {
inertia = true;
$(document).off('mousemove', drag );
$(document).off('touchmove', drag );
$(document).off('mouseup', endDrag );
$(document).off('touchend', endDrag );
};
var initDrag = function(e) {
e.preventDefault();
e = isTouch ? e.originalEvent.touches[0] : e;
inertia = false;
cur.x = e.clientX;
cur.y = e.clientY;
$(document).on('mousemove', drag );
$(document).on('touchmove', drag );
$(document).on('mouseup', endDrag );
$(document).on('touchend', endDrag );
};
var = zoom = function(e, zoom, value) {
if (zoom) {
var scale = value;
var origin = {
x: (-matrix.x + contWidth/2) / (width * matrix.s),
y: (-matrix.y + contHeight/2) / (height * matrix.s)
}
} else {
e.preventDefault();
e = isFirefox ? e.originalEvent : e;
if (isTouch) {
var scale = e.originalEvent.scale - pinchScale;
pinchScale = e.originalEvent.scale;
var origin = {
x: (e.originalEvent.pageX - matrix.x) / (width * matrix.s),
y: (e.originalEvent.pageY - matrix.y - contTop) / (height * matrix.s)
}
} else {
var scale = isFirefox ? -parseFloat(e.detail)/100 : e.originalEvent.wheelDelta/1000;
var origin = {
x: (e.clientX - matrix.x) / (width * matrix.s),
y: (e.clientY - matrix.y - contTop) / (height * matrix.s)
}
}
}
// Apply maximum scale limit
var newScale = matrix.s + scale;
if (newScale > maxScale) {
scale = maxScale - matrix.s;
} else if ( width * newScale <= contWidth || height * newScale <= contHeight ) {
if ( ratio <= contRatio ) {
scale = (contWidth / width) - matrix.s;
} else {
scale = (contHeight / height) - matrix.s;
}
}
matrix.s += scale;
matrix.x -= width * scale * origin.x;
matrix.y -= height * scale * origin.y;
};
var initPinch = function(e) {
pinchScale = e.originalEvent.scale;
pinching = true;
};
var endPinch = function(e) {
pinching = false;
};
var resize = function() {
// Recalculate container size and then do a check
contWidth = _cont.width();
contHeight = _cont.height();
contRatio = contWidth / contHeight;
checkScale();
checkTranslation();
};
var init = function() {
// Attach listeners
_content.on('mousedown', initDrag);
_content.on('touchstart', initDrag);
_content.on('mousewheel', zoom);
_content.on('DOMMouseScroll', zoom);
_content.on('gesturechange', zoom);
_content.on('gesturestart', initPinch );
_content.on('gestureend', endPinch);
// If resize triggered from elsewhere, remove this
$(window).on('resize', resize);
resize();
stopDraw = false;
draw();
// Set size to fit container
if ( ratio <= contRatio ) {
matrix.s = contWidth / width;
matrix.x = 0;
} else {
matrix.s = contHeight / height;
matrix.y = 0;
}
};
var exit = function() {
// Stops the loop
stopDraw = true;
};
return {
init: init,
exit: exit,
resize: resize,
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment