img-touch-canvas.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. /*
  2. =================================
  3. img-touch-canvas - v0.1
  4. http://github.com/rombdn/img-touch-canvas
  5. (c) 2013 Romain BEAUDON
  6. This code may be freely distributed under the MIT License
  7. =================================
  8. */
  9. (function() {
  10. var root = this; //global object
  11. var ImgTouchCanvas = function(options) {
  12. if( !options || !options.canvas || !options.path) {
  13. throw 'ImgZoom constructor: missing arguments canvas or path';
  14. }
  15. this.canvas = options.canvas;
  16. this.canvas.width = this.canvas.clientWidth;
  17. this.canvas.height = this.canvas.clientHeight;
  18. this.context = this.canvas.getContext('2d');
  19. this.desktop = options.desktop || false; //non touch events
  20. this.position = {
  21. x: 0,
  22. y: 0
  23. };
  24. this.scale = {
  25. x: 0.5,
  26. y: 0.5
  27. };
  28. this.imgTexture = new Image();
  29. this.imgTexture.src = options.path;
  30. this.lastZoomScale = null;
  31. this.lastX = null;
  32. this.lastY = null;
  33. this.mdown = false; //desktop drag
  34. this.init = false;
  35. this.checkRequestAnimationFrame();
  36. requestAnimationFrame(this.animate.bind(this));
  37. this.setEventListeners();
  38. };
  39. ImgTouchCanvas.prototype = {
  40. animate: function() {
  41. //set scale such as image cover all the canvas
  42. if(!this.init) {
  43. if(this.imgTexture.width) {
  44. var scaleRatio = null;
  45. if(this.canvas.clientWidth > this.canvas.clientHeight) {
  46. scaleRatio = this.canvas.clientWidth / this.imgTexture.width;
  47. }
  48. else {
  49. scaleRatio = this.canvas.clientHeight / this.imgTexture.height;
  50. }
  51. this.scale.x = scaleRatio;
  52. this.scale.y = scaleRatio;
  53. this.init = true;
  54. }
  55. }
  56. this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  57. this.context.drawImage(
  58. this.imgTexture,
  59. this.position.x, this.position.y,
  60. this.scale.x * this.imgTexture.width,
  61. this.scale.y * this.imgTexture.height);
  62. requestAnimationFrame(this.animate.bind(this));
  63. },
  64. gesturePinchZoom: function(event) {
  65. var zoom = false;
  66. if( event.targetTouches.length >= 2 ) {
  67. var p1 = event.targetTouches[0];
  68. var p2 = event.targetTouches[1];
  69. var zoomScale = Math.sqrt(Math.pow(p2.pageX - p1.pageX, 2) + Math.pow(p2.pageY - p1.pageY, 2)); //euclidian distance
  70. if( this.lastZoomScale ) {
  71. zoom = zoomScale - this.lastZoomScale;
  72. }
  73. this.lastZoomScale = zoomScale;
  74. }
  75. return zoom;
  76. },
  77. doZoom: function(zoom) {
  78. if(!zoom) return;
  79. //new scale
  80. var currentScale = this.scale.x;
  81. var newScale = this.scale.x + zoom/100;
  82. //some helpers
  83. var deltaScale = newScale - currentScale;
  84. var currentWidth = (this.imgTexture.width * this.scale.x);
  85. var currentHeight = (this.imgTexture.height * this.scale.y);
  86. var deltaWidth = this.imgTexture.width*deltaScale;
  87. var deltaHeight = this.imgTexture.height*deltaScale;
  88. //by default scale doesnt change position and only add/remove pixel to right and bottom
  89. //so we must move the image to the left to keep the image centered
  90. //ex: coefX and coefY = 0.5 when image is centered <=> move image to the left 0.5x pixels added to the right
  91. var canvasmiddleX = this.canvas.clientWidth / 2;
  92. var canvasmiddleY = this.canvas.clientHeight / 2;
  93. var xonmap = (-this.position.x) + canvasmiddleX;
  94. var yonmap = (-this.position.y) + canvasmiddleY;
  95. var coefX = -xonmap / (currentWidth);
  96. var coefY = -yonmap / (currentHeight);
  97. var newPosX = this.position.x + deltaWidth*coefX;
  98. var newPosY = this.position.y + deltaHeight*coefY;
  99. //edges cases
  100. var newWidth = currentWidth + deltaWidth;
  101. var newHeight = currentHeight + deltaHeight;
  102. if( newWidth < this.canvas.clientWidth ) return;
  103. if( newPosX > 0 ) { newPosX = 0; }
  104. if( newPosX + newWidth < this.canvas.clientWidth ) { newPosX = this.canvas.clientWidth - newWidth;}
  105. if( newHeight < this.canvas.clientHeight ) return;
  106. if( newPosY > 0 ) { newPosY = 0; }
  107. if( newPosY + newHeight < this.canvas.clientHeight ) { newPosY = this.canvas.clientHeight - newHeight; }
  108. //finally affectations
  109. this.scale.x = newScale;
  110. this.scale.y = newScale;
  111. this.position.x = newPosX;
  112. this.position.y = newPosY;
  113. },
  114. doMove: function(relativeX, relativeY) {
  115. if(this.lastX && this.lastY) {
  116. var deltaX = relativeX - this.lastX;
  117. var deltaY = relativeY - this.lastY;
  118. var currentWidth = (this.imgTexture.width * this.scale.x);
  119. var currentHeight = (this.imgTexture.height * this.scale.y);
  120. this.position.x += deltaX;
  121. this.position.y += deltaY;
  122. //edge cases
  123. if( this.position.x > 0 ) {
  124. this.position.x = 0;
  125. }
  126. else if( this.position.x + currentWidth < this.canvas.clientWidth ) {
  127. this.position.x = this.canvas.clientWidth - currentWidth;
  128. }
  129. if( this.position.y > 0 ) {
  130. this.position.y = 0;
  131. }
  132. else if( this.position.y + currentHeight < this.canvas.clientHeight ) {
  133. this.position.y = this.canvas.clientHeight - currentHeight;
  134. }
  135. }
  136. this.lastX = relativeX;
  137. this.lastY = relativeY;
  138. },
  139. setEventListeners: function() {
  140. // touch
  141. this.canvas.addEventListener('touchstart', function(e) {
  142. this.lastX = null;
  143. this.lastY = null;
  144. this.lastZoomScale = null;
  145. }.bind(this));
  146. this.canvas.addEventListener('touchmove', function(e) {
  147. e.preventDefault();
  148. if(e.targetTouches.length == 2) { //pinch
  149. this.doZoom(this.gesturePinchZoom(e));
  150. }
  151. else if(e.targetTouches.length == 1) {
  152. var relativeX = e.targetTouches[0].pageX - this.canvas.getBoundingClientRect().left;
  153. var relativeY = e.targetTouches[0].pageY - this.canvas.getBoundingClientRect().top;
  154. this.doMove(relativeX, relativeY);
  155. }
  156. }.bind(this));
  157. if(this.desktop) {
  158. // keyboard+mouse
  159. window.addEventListener('keyup', function(e) {
  160. if(e.keyCode == 187 || e.keyCode == 61) { //+
  161. this.doZoom(5);
  162. }
  163. else if(e.keyCode == 54) {//-
  164. this.doZoom(-5);
  165. }
  166. }.bind(this));
  167. window.addEventListener('mousedown', function(e) {
  168. this.mdown = true;
  169. this.lastX = null;
  170. this.lastY = null;
  171. }.bind(this));
  172. window.addEventListener('mouseup', function(e) {
  173. this.mdown = false;
  174. }.bind(this));
  175. window.addEventListener('mousemove', function(e) {
  176. var relativeX = e.pageX - this.canvas.getBoundingClientRect().left;
  177. var relativeY = e.pageY - this.canvas.getBoundingClientRect().top;
  178. if(e.target == this.canvas && this.mdown) {
  179. this.doMove(relativeX, relativeY);
  180. }
  181. if(relativeX <= 0 || relativeX >= this.canvas.clientWidth || relativeY <= 0 || relativeY >= this.canvas.clientHeight) {
  182. this.mdown = false;
  183. }
  184. }.bind(this));
  185. }
  186. },
  187. checkRequestAnimationFrame: function() {
  188. var lastTime = 0;
  189. var vendors = ['ms', 'moz', 'webkit', 'o'];
  190. for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
  191. window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
  192. window.cancelAnimationFrame =
  193. window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
  194. }
  195. if (!window.requestAnimationFrame) {
  196. window.requestAnimationFrame = function(callback, element) {
  197. var currTime = new Date().getTime();
  198. var timeToCall = Math.max(0, 16 - (currTime - lastTime));
  199. var id = window.setTimeout(function() { callback(currTime + timeToCall); },
  200. timeToCall);
  201. lastTime = currTime + timeToCall;
  202. return id;
  203. };
  204. }
  205. if (!window.cancelAnimationFrame) {
  206. window.cancelAnimationFrame = function(id) {
  207. clearTimeout(id);
  208. };
  209. }
  210. }
  211. };
  212. root.ImgTouchCanvas = ImgTouchCanvas;
  213. }).call(this);