Guest User

Untitled

a guest
Jan 22nd, 2018
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.42 KB | None | 0 0
  1. function CanvasZoom( canvas, tilesFolder, imageWidth, imageHeight )
  2. {
  3. this.getTileFile = function( zoom, column, row ) {
  4. return "" + this.tilesFolder + "/" + zoom + "/" + column + "_" + row + ".jpg";
  5. }
  6.  
  7. // Constructor
  8. {
  9. var thisCanvasZoom = this;
  10.  
  11. this.canvas = canvas;
  12. this.tilesFolder = tilesFolder;
  13. this.imageWidth = imageWidth;
  14. this.imageHeight = imageHeight;
  15.  
  16. this.tileOverlap = 1; // assumed
  17. this.tileSize = 512; // assumed
  18.  
  19. this.debug = false;
  20.  
  21. this.zoomLevelMin = 0;
  22. this.zoomLevelMax = Math.ceil( Math.log( Math.max( this.imageWidth, this.imageHeight ))/Math.LN2 );
  23. this.zoomLevelFull = -1; // For painting a background image for all missing tiles. Needs improvement though.
  24. this.zoomLevel = -1;
  25.  
  26. this.mouseX = this.mouseY = this.offsetX = this.offsetY = 0;
  27.  
  28. // Make a space for all the images
  29. this.tileZoomArray = new Array(this.zoomLevelMax);
  30.  
  31. var reducingWidth = this.imageWidth;
  32. var reducingHeight = this.imageHeight;
  33. var zoomLevelStart = -1;
  34. for( var iZoom = this.zoomLevelMax; iZoom >= this.zoomLevelMin; iZoom-- )
  35. {
  36. var columns = Math.ceil( reducingWidth / this.tileSize );
  37. var rows = Math.ceil( reducingHeight / this.tileSize );
  38.  
  39. if( this.zoomLevelFull == -1 &&
  40. reducingWidth <= this.tileSize && reducingHeight <= this.tileSize )
  41. {
  42. // Largest full image inside single tile.
  43. this.zoomLevelFull = iZoom;
  44. }
  45.  
  46. if( zoomLevelStart == -1 &&
  47. reducingWidth <= this.canvas.width && reducingHeight <= this.canvas.height )
  48. {
  49. // Largest full image inside single tile.
  50. zoomLevelStart = iZoom;
  51. }
  52.  
  53. // Create array for tiles
  54. this.tileZoomArray[iZoom] = new Array(columns);
  55. for( var iColumn = 0; iColumn < columns; iColumn++ )
  56. this.tileZoomArray[iZoom][iColumn] = new Array(rows);
  57.  
  58. // Set defaults
  59. this.tileZoomArray[iZoom]['width'] = reducingWidth;
  60. this.tileZoomArray[iZoom]['height'] = reducingHeight;
  61. for( var iColumn = 0; iColumn < columns; iColumn++ )
  62. {
  63. for( var iRow = 0; iRow < rows; iRow++ )
  64. {
  65. this.tileZoomArray[iZoom][iColumn][iRow] = new Array();
  66.  
  67. this.tileZoomArray[iZoom][iColumn][iRow]['tile'] = null;
  68. this.tileZoomArray[iZoom][iColumn][iRow]['waiting'] = false;
  69. }
  70. }
  71.  
  72. reducingWidth /= 2;
  73. reducingHeight /= 2;
  74. }
  75.  
  76. this.zoomLevel = zoomLevelStart;
  77.  
  78. //
  79. // Initial tile load
  80. //
  81. var imageList = new Array();
  82. var imageId = 0;
  83.  
  84. var columns = this.tileZoomArray[this.zoomLevel].length;
  85. var rows = this.tileZoomArray[this.zoomLevel][0].length;
  86.  
  87. for( var iColumn = 0; iColumn < columns; iColumn++ )
  88. {
  89. for( var iRow = 0; iRow < rows; iRow++ )
  90. {
  91. imageList.push( { "id" : imageId++, "file": this.getTileFile( this.zoomLevel, iColumn, iRow ) } );
  92. }
  93. }
  94.  
  95. imageList.push( { "id" : imageId, "file": this.getTileFile( this.zoomLevelFull, 0, 0 ) } );
  96.  
  97. this.imageLoader = new ImageLoader( {
  98. "images": imageList,
  99. "onAllLoaded":function() { thisCanvasZoom.setup(); }
  100. } )
  101. }
  102.  
  103. this.setup = function() {
  104.  
  105. var tileZoomLevel = this.tileZoomArray[this.zoomLevel];
  106.  
  107. var columns = tileZoomLevel.length;
  108. var rows = tileZoomLevel[0].length;
  109.  
  110. var imageId = 0;
  111. for( var iColumn = 0; iColumn < columns; iColumn++ )
  112. {
  113. for( var iRow = 0; iRow < rows; iRow++ )
  114. {
  115. tileZoomLevel[iColumn][iRow]['tile'] = this.imageLoader.getImageById( imageId++ );
  116. }
  117. }
  118.  
  119. this.tileZoomArray[this.zoomLevelFull][0][0]['tile'] = this.imageLoader.getImageById( imageId );
  120.  
  121. //
  122. // Centre image
  123. //
  124. /*
  125. this.offsetX = (this.canvas.width - tileZoomLevel['width']) / 2;
  126. this.offsetY = (this.canvas.height - tileZoomLevel['height']) / 2;
  127. We wan't our custom behaviour!
  128. */
  129. this.reposition();
  130.  
  131. //
  132. // Add mouse listener events
  133. //
  134. this.canvas.addEventListener('mousemove', function (e) { thisCanvasZoom.mouseMove( getEvent(e) ); }, true);
  135. this.canvas.addEventListener('mousedown', function (e) { thisCanvasZoom.mouseDown( getEvent(e) ); }, true);
  136. this.canvas.addEventListener('mouseup', function (e) { thisCanvasZoom.mouseUp( getEvent(e) ); }, true);
  137.  
  138. this.canvas.addEventListener('mouseout', function (e) { thisCanvasZoom.mouseOut( getEvent(e) ); }, true);
  139. this.canvas.addEventListener('mouseover', function (e) { thisCanvasZoom.mouseOver( getEvent(e) ); }, true);
  140. this.canvas.addEventListener('DOMMouseScroll', function (e) { thisCanvasZoom.mouseWheel( getEvent(e) ); }, true);
  141. this.canvas.addEventListener('mousewheel', function (e) { thisCanvasZoom.mouseWheel( getEvent(e) ); }, true);
  142.  
  143. // Keep track even if mouse is outside of canvas while dragging image
  144. window.addEventListener('mouseup', function (e) { thisCanvasZoom.mouseUpWindow( getEvent(e) ); }, false);
  145. window.addEventListener('mousemove', function (e) { thisCanvasZoom.mouseMoveWindow( getEvent(e) ); }, false);
  146.  
  147. this.ctx = this.canvas.getContext('2d');
  148.  
  149. this.paint();
  150. };
  151.  
  152. // Helper function
  153. var getEvent = function( event )
  154. {
  155. if( !event ) // IE
  156. event = window.event;
  157.  
  158. return event;
  159. }
  160.  
  161. this.mouseDown = function( event ) {
  162. this.mouseIsDown = true;
  163. this.mouseLeftWhileDown = false;
  164.  
  165. this.mouseDownX = mousePosX(event);
  166. this.mouseDownY = mousePosY(event);
  167.  
  168. this.mouseMoveX = this.mouseDownX;
  169. this.mouseMoveY = this.mouseDownY;
  170. }
  171.  
  172. this.mouseUp = function( event ) {
  173. this.mouseIsDown = false;
  174. this.mouseLeftWhileDown = false;
  175.  
  176. this.mouseX = mousePosX(event);
  177. this.mouseY = mousePosY(event);
  178.  
  179. if( this.mouseX == this.mouseDownX &&
  180. this.mouseY == this.mouseDownY )
  181. {
  182. // Didn't drag so assume a click.
  183. this.zoomIn();
  184. }
  185. }
  186.  
  187. this.reposInner = function(win, img, pos) {
  188. if (win >= img) {
  189. return 0; // Das Fenster ist größer als der Inhalt -> oben rechts anordnen
  190. }
  191. if(win-pos > img) { // pos ist negativ, wenn fenstergröße+pos > bildgröße
  192. return win-img; // unterkante des Fensters liegt außerhalb des Bildes -> unten links anordnen
  193. }
  194. if (pos > 0) { // positive Position wäre oben/links vom Bild entfernt
  195. return 0;
  196. }
  197. return pos;
  198. }
  199.  
  200. this.reposition = function(x, y) {
  201.  
  202. var leveldiff = this.zoomLevelMax-this.zoomLevel;
  203. leveldiff = Math.pow(2, leveldiff);
  204. var imgX = this.imageWidth / leveldiff;
  205. var imgY = this.imageHeight / leveldiff;
  206. var winX = this.canvas.width;
  207. var winY = this.canvas.height;
  208.  
  209.  
  210.  
  211.  
  212. this.offsetX = this.reposInner(winX,imgX,x, true);
  213. this.offsetY = this.reposInner(winY,imgY,y, false);
  214. }
  215.  
  216. this.mouseMove = function(event) {
  217. this.mouseX = mousePosX(event);
  218. this.mouseY = mousePosY(event);
  219.  
  220. if( this.mouseIsDown )
  221. {
  222. var newOffsetX = this.offsetX + (this.mouseX - this.mouseMoveX);
  223. var newOffsetY = this.offsetY + (this.mouseY - this.mouseMoveY);
  224.  
  225. this.calculateNeededTiles( this.zoomLevel, newOffsetX, newOffsetY );
  226.  
  227. // console.log ("x: "+newOffsetX+" y: "+newOffsetY);
  228.  
  229.  
  230. this.mouseMoveX = this.mouseX;
  231. this.mouseMoveY = this.mouseY;
  232.  
  233. this.reposition(newOffsetX, newOffsetY);
  234.  
  235.  
  236. this.paint();
  237. }
  238. }
  239.  
  240. var mousePosX = function( event ) {
  241. // Get the mouse position relative to the canvas element.
  242. var x = 0;
  243.  
  244. if (event.layerX || event.layerX == 0) { // Firefox
  245. x = event.layerX - thisCanvasZoom.canvas.offsetLeft;
  246. } else if (event.offsetX || event.offsetX == 0) { // Opera
  247. x = event.offsetX;
  248. }
  249.  
  250. return x;
  251. }
  252.  
  253. var mousePosY = function( event ) {
  254. var y = 0;
  255.  
  256. if (event.layerY || event.layerY == 0) { // Firefox
  257. y = event.layerY - thisCanvasZoom.canvas.offsetTop;
  258. } else if (event.offsetY || event.offsetY == 0) { // Opera
  259. y = event.offsetY;
  260. }
  261.  
  262. return y;
  263. }
  264.  
  265. this.mouseOut = function( event ) {
  266. if( this.mouseIsDown )
  267. {
  268. this.mouseLeftWhileDown = true;
  269. }
  270. }
  271.  
  272. this.mouseOver = function( event ) {
  273. // (Should be called mouseEnter IMO...)
  274. this.mouseLeftWhileDown = false;
  275. }
  276.  
  277. this.mouseWheel = function( event ) {
  278. var delta = 0;
  279.  
  280. if (event.wheelDelta) { /* IE/Opera. */
  281. delta = event.wheelDelta/120;
  282.  
  283. if (window.opera) /* Opera 9 */
  284. delta = -delta;
  285.  
  286. } else if (event.detail) { /* Mozilla */
  287. delta = event.detail/3;
  288. }
  289.  
  290. if (delta) {
  291. if (delta < 0)
  292. this.zoomIn();
  293. else
  294. this.zoomOut();
  295. }
  296.  
  297. if (event.preventDefault)
  298. event.preventDefault();
  299.  
  300. event.returnValue = false;
  301. }
  302.  
  303. // If mouseUp occurs outside of canvas while moving, cancel movement.
  304. this.mouseUpWindow = function( event ) {
  305. if( this.mouseIsDown && this.mouseLeftWhileDown )
  306. {
  307. this.mouseUp( event );
  308. }
  309. }
  310.  
  311. // keep track of mouse outside of canvas so movement continues.
  312. this.mouseMoveWindow = function(event) {
  313. if( this.mouseIsDown && this.mouseLeftWhileDown )
  314. {
  315. this.mouseMove(event);
  316. }
  317. }
  318.  
  319. // Zoom in a single level
  320. this.zoomIn = function () {
  321. this.zoom( this.zoomLevel + 1 );
  322. this.paint();
  323. }
  324.  
  325. // Zoom out a single level
  326. this.zoomOut = function () {
  327. this.zoom( this.zoomLevel - 1 );
  328. this.paint();
  329. }
  330.  
  331. //Zoom in at the centre of the canvas
  332. this.zoomInCentre = function () {
  333. this.mouseX = this.canvas.width / 2;
  334. this.mouseY = this.canvas.height / 2;
  335.  
  336. this.zoomIn();
  337. }
  338.  
  339. //Zoom out at the centre of the canvas
  340. this.zoomOutCentre = function () {
  341. this.mouseX = this.canvas.width / 2;
  342. this.mouseY = this.canvas.height / 2;
  343.  
  344. this.zoomOut();
  345. }
  346.  
  347. // Change the zoom level and update.
  348. this.zoom = function(zoomLevel) {
  349. if( zoomLevel >= this.zoomLevelMin && zoomLevel <= this.zoomLevelMax )
  350. {
  351. var newZoom = zoomLevel,
  352. currentZoom = this.zoomLevel;
  353.  
  354. //
  355. // Calculate new offset
  356. //
  357. var zoomX = this.mouseX;
  358. var zoomY = this.mouseY;
  359.  
  360. var currentImageX = zoomX - this.offsetX,
  361. currentImageY = zoomY - this.offsetY;
  362.  
  363. var scale = this.tileZoomArray[newZoom]['width'] / this.tileZoomArray[currentZoom]['width'];
  364.  
  365. var newImageX = currentImageX * scale,
  366. newImageY = currentImageY * scale;
  367.  
  368. var newOffsetX = this.offsetX - (newImageX - currentImageX),
  369. newOffsetY = this.offsetY - (newImageY - currentImageY);
  370.  
  371.  
  372. this.calculateNeededTiles( newZoom, newOffsetX, newOffsetY );
  373.  
  374.  
  375. this.zoomLevel = newZoom;
  376. if (newOffsetX > 0) {
  377. newOffsetX = 0;
  378. }
  379. this.reposition(newOffsetX, newOffsetY);
  380. }
  381. }
  382.  
  383. // Work out which of the tiles we need to download
  384. this.calculateNeededTiles = function( zoom, offsetX, offsetY ) {
  385. //
  386. // Calculate needed tiles
  387. //
  388. var tileZoomLevelArray = this.tileZoomArray[zoom];
  389.  
  390. var tileSize = this.tileSize;
  391. var extend = tileSize / 2;
  392.  
  393. var canvasLeft = -offsetX,
  394. canvasTop = -offsetY;
  395. var canvasRight = canvasLeft + this.canvas.width,
  396. canvasBottom = canvasTop + this.canvas.height;
  397.  
  398. var tileLeft = 0, tileRight = 0, tileTop = 0, tileBottom = 0;
  399. var tile = null;
  400.  
  401. var zoomWidth = tileZoomLevelArray['width'],
  402. zoomHeight = tileZoomLevelArray['height'];
  403.  
  404. var columns = tileZoomLevelArray.length,
  405. rows = tileZoomLevelArray[0].length;
  406.  
  407. var tileList = new Array();
  408. for( var iColumn = 0; iColumn < columns; iColumn++ )
  409. {
  410. for( var iRow = 0; iRow < rows; iRow++ )
  411. {
  412. tile = tileZoomLevelArray[iColumn][iRow];
  413.  
  414. if( tile['tile'] == null && tile['waiting'] == false )
  415. {
  416. tileLeft = iColumn * tileSize;
  417. tileRight = tileLeft + Math.min( tileSize, zoomWidth - tileLeft );
  418. tileTop = iRow * tileSize;
  419. tileBottom = tileTop + Math.min( tileSize, zoomHeight - tileTop );
  420.  
  421. if( !( tileLeft > canvasRight || tileRight < canvasLeft || tileTop > canvasBottom || tileBottom < canvasTop ) )
  422. {
  423. // request tile!
  424. tile['waiting'] = true;
  425. tileList.push( { "name" : zoom + "_" + iColumn + "_" + iRow, "file" : this.getTileFile( zoom, iColumn, iRow ) } );
  426. }
  427. }
  428. }
  429. }
  430.  
  431. this.getTiles( tileList );
  432. }
  433.  
  434. // Load the tiles we need with ImageLoader
  435. this.getTiles = function( tileList ) {
  436. if( tileList.length > 0 )
  437. {
  438. this.imageLoader = new ImageLoader( {
  439. "images": tileList,
  440. "onImageLoaded":function( name, tile ) { thisCanvasZoom.tileLoaded( name, tile ); }
  441. } )
  442. }
  443. }
  444.  
  445. // Tile loaded, save it.
  446. this.tileLoaded = function ( name, tile ) {
  447. var tileDetails = name.split("_");
  448.  
  449. if( tileDetails.length == 3 )
  450. {
  451. var tileInfo = this.tileZoomArray[tileDetails[0]][tileDetails[1]][tileDetails[2]];
  452. tileInfo['tile'] = tile;
  453. tileInfo['waiting'] = false;
  454.  
  455. this.paint();
  456. }
  457. }
  458.  
  459. this.paint = function () {
  460.  
  461. var canvasWidth = this.canvas.width,
  462. canvasHeight = this.canvas.height;
  463.  
  464. var ctx = this.ctx;
  465. var debug = this.debug;
  466.  
  467. //
  468. // Clear
  469. //
  470. ctx.fillStyle = ctx.strokeStyle = "#fff";
  471. ctx.clearRect( 0, 0, canvasWidth, canvasHeight );
  472.  
  473. //
  474. // Show images
  475. //
  476. var tileZoomLevelArray = this.tileZoomArray[this.zoomLevel];
  477.  
  478. var columns = tileZoomLevelArray.length,
  479. rows = tileZoomLevelArray[0].length;
  480.  
  481. var offsetX = this.offsetX,
  482. offsetY = this.offsetY;
  483.  
  484. var canvasLeft = -offsetX,
  485. canvasTop = -offsetY;
  486. var canvasRight = canvasLeft + canvasWidth,
  487. canvasBottom = canvasTop + canvasHeight;
  488.  
  489. var tileLeft = 0, tileRight = 0, tileTop = 0, tileBottom = 0;
  490. var tileCount = 0;
  491. var tile = null;
  492. var tileSize = this.tileSize;
  493.  
  494. var zoomWidth = tileZoomLevelArray['width'];
  495. var zoomHeight = tileZoomLevelArray['height'];
  496.  
  497. // TODO: This pastes a low resolution copy on the background (It's a bit of a hack and quite slow. A better solution is to find a nearer zoom)
  498. var fullTile = this.tileZoomArray[this.zoomLevelFull][0][0]['tile'];
  499.  
  500. // TODO: Improve this by working out the start / end column and row using the image position instead of looping through them all (still pretty fast though!)
  501. for( var iColumn = 0; iColumn < columns; iColumn++ )
  502. {
  503. for( var iRow = 0; iRow < rows; iRow++ )
  504. {
  505. tileLeft = iColumn * tileSize;
  506. tileRight = tileLeft + Math.min( tileSize, zoomWidth - tileLeft );
  507. tileTop = iRow * tileSize;
  508. tileBottom = tileTop + Math.min( tileSize, zoomHeight - tileTop );
  509.  
  510. if( !( tileLeft > canvasRight || tileRight < canvasLeft || tileTop > canvasBottom || tileBottom < canvasTop ) )
  511. {
  512. tile = tileZoomLevelArray[iColumn][iRow]['tile'];
  513.  
  514. tileLeft += offsetX;
  515. tileRight += offsetX;
  516. tileTop += offsetY;
  517. tileBottom += offsetY;
  518.  
  519. if( tile != null )
  520. {
  521. // Draw tile
  522. ctx.drawImage( tile, tileLeft, tileTop );
  523. if( debug )
  524. {
  525. ctx.strokeRect( tileLeft, tileTop, tileSize, tileSize );
  526. tileCount++;
  527. }
  528. }
  529. else
  530. {
  531. //
  532. // Tile still loading
  533. //
  534. if( true )
  535. {
  536. ctx.save();
  537. ctx.beginPath();
  538.  
  539. ctx.moveTo( tileLeft, tileTop );
  540. ctx.lineTo( tileRight, tileTop );
  541. ctx.lineTo( tileRight, tileBottom );
  542. ctx.lineTo( tileLeft, tileBottom );
  543. ctx.closePath();
  544.  
  545. ctx.clip();
  546.  
  547. // TODO: Fill with a lower zoom image. (or possible use combination of higher zooms??)
  548. // but scaling images in canvas still VERY SLOW.
  549. // THIS NOTABLY SLOWS DOWN PANNING WHEN IMAGES ARE NOT YET LOADED ON SOME BROWSERS.
  550. ctx.drawImage( fullTile, offsetX, offsetY, zoomWidth, zoomHeight );
  551.  
  552. ctx.restore();
  553. }
  554. else
  555. {
  556. ctx.fillStyle = "#999";
  557. ctx.fillRect( tileLeft, tileTop, tileRight - tileLeft, tileBottom - tileTop );
  558. }
  559. }
  560. }
  561. }
  562. }
  563.  
  564. //
  565. // Canvas area
  566. //
  567. ctx.strokeStyle = "#000";
  568. ctx.strokeRect( 0, 0, canvasWidth, canvasHeight );
  569.  
  570. if( debug )
  571. {
  572. //
  573. // DEBUG!
  574. //
  575. ctx.fillStyle = "#0f0";
  576. ctx.font = "normal 12px Arial";
  577.  
  578. // Text
  579. ctx.fillText( this.mouseX + "," + this.mouseY + " | " + offsetX + "," + offsetY + " | " + tileCount, 0, 20 );
  580.  
  581. // Grid
  582. ctx.strokeStyle = "#f00";
  583. for( var y = 0; y < canvasHeight; y += tileSize )
  584. for( var x = 0; x < canvasWidth; x += tileSize )
  585. ctx.strokeRect( x, y, tileSize, tileSize );
  586. }
  587. };
  588. }
Add Comment
Please, Sign In to add comment