Created
October 6, 2014 09:44
-
-
Save gordonnl/0816b1fd9ea3396e3e4e to your computer and use it in GitHub Desktop.
Drag and zoom controls with touch support.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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