Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- function CanvasZoom( canvas, tilesFolder, imageWidth, imageHeight )
- {
- this.getTileFile = function( zoom, column, row ) {
- return "" + this.tilesFolder + "/" + zoom + "/" + column + "_" + row + ".jpg";
- }
- // Constructor
- {
- var thisCanvasZoom = this;
- this.canvas = canvas;
- this.tilesFolder = tilesFolder;
- this.imageWidth = imageWidth;
- this.imageHeight = imageHeight;
- this.tileOverlap = 1; // assumed
- this.tileSize = 512; // assumed
- this.debug = false;
- this.zoomLevelMin = 0;
- this.zoomLevelMax = Math.ceil( Math.log( Math.max( this.imageWidth, this.imageHeight ))/Math.LN2 );
- this.zoomLevelFull = -1; // For painting a background image for all missing tiles. Needs improvement though.
- this.zoomLevel = -1;
- this.mouseX = this.mouseY = this.offsetX = this.offsetY = 0;
- // Make a space for all the images
- this.tileZoomArray = new Array(this.zoomLevelMax);
- var reducingWidth = this.imageWidth;
- var reducingHeight = this.imageHeight;
- var zoomLevelStart = -1;
- for( var iZoom = this.zoomLevelMax; iZoom >= this.zoomLevelMin; iZoom-- )
- {
- var columns = Math.ceil( reducingWidth / this.tileSize );
- var rows = Math.ceil( reducingHeight / this.tileSize );
- if( this.zoomLevelFull == -1 &&
- reducingWidth <= this.tileSize && reducingHeight <= this.tileSize )
- {
- // Largest full image inside single tile.
- this.zoomLevelFull = iZoom;
- }
- if( zoomLevelStart == -1 &&
- reducingWidth <= this.canvas.width && reducingHeight <= this.canvas.height )
- {
- // Largest full image inside single tile.
- zoomLevelStart = iZoom;
- }
- // Create array for tiles
- this.tileZoomArray[iZoom] = new Array(columns);
- for( var iColumn = 0; iColumn < columns; iColumn++ )
- this.tileZoomArray[iZoom][iColumn] = new Array(rows);
- // Set defaults
- this.tileZoomArray[iZoom]['width'] = reducingWidth;
- this.tileZoomArray[iZoom]['height'] = reducingHeight;
- for( var iColumn = 0; iColumn < columns; iColumn++ )
- {
- for( var iRow = 0; iRow < rows; iRow++ )
- {
- this.tileZoomArray[iZoom][iColumn][iRow] = new Array();
- this.tileZoomArray[iZoom][iColumn][iRow]['tile'] = null;
- this.tileZoomArray[iZoom][iColumn][iRow]['waiting'] = false;
- }
- }
- reducingWidth /= 2;
- reducingHeight /= 2;
- }
- this.zoomLevel = zoomLevelStart;
- //
- // Initial tile load
- //
- var imageList = new Array();
- var imageId = 0;
- var columns = this.tileZoomArray[this.zoomLevel].length;
- var rows = this.tileZoomArray[this.zoomLevel][0].length;
- for( var iColumn = 0; iColumn < columns; iColumn++ )
- {
- for( var iRow = 0; iRow < rows; iRow++ )
- {
- imageList.push( { "id" : imageId++, "file": this.getTileFile( this.zoomLevel, iColumn, iRow ) } );
- }
- }
- imageList.push( { "id" : imageId, "file": this.getTileFile( this.zoomLevelFull, 0, 0 ) } );
- this.imageLoader = new ImageLoader( {
- "images": imageList,
- "onAllLoaded":function() { thisCanvasZoom.setup(); }
- } )
- }
- this.setup = function() {
- var tileZoomLevel = this.tileZoomArray[this.zoomLevel];
- var columns = tileZoomLevel.length;
- var rows = tileZoomLevel[0].length;
- var imageId = 0;
- for( var iColumn = 0; iColumn < columns; iColumn++ )
- {
- for( var iRow = 0; iRow < rows; iRow++ )
- {
- tileZoomLevel[iColumn][iRow]['tile'] = this.imageLoader.getImageById( imageId++ );
- }
- }
- this.tileZoomArray[this.zoomLevelFull][0][0]['tile'] = this.imageLoader.getImageById( imageId );
- //
- // Centre image
- //
- /*
- this.offsetX = (this.canvas.width - tileZoomLevel['width']) / 2;
- this.offsetY = (this.canvas.height - tileZoomLevel['height']) / 2;
- We wan't our custom behaviour!
- */
- this.reposition();
- //
- // Add mouse listener events
- //
- this.canvas.addEventListener('mousemove', function (e) { thisCanvasZoom.mouseMove( getEvent(e) ); }, true);
- this.canvas.addEventListener('mousedown', function (e) { thisCanvasZoom.mouseDown( getEvent(e) ); }, true);
- this.canvas.addEventListener('mouseup', function (e) { thisCanvasZoom.mouseUp( getEvent(e) ); }, true);
- this.canvas.addEventListener('mouseout', function (e) { thisCanvasZoom.mouseOut( getEvent(e) ); }, true);
- this.canvas.addEventListener('mouseover', function (e) { thisCanvasZoom.mouseOver( getEvent(e) ); }, true);
- this.canvas.addEventListener('DOMMouseScroll', function (e) { thisCanvasZoom.mouseWheel( getEvent(e) ); }, true);
- this.canvas.addEventListener('mousewheel', function (e) { thisCanvasZoom.mouseWheel( getEvent(e) ); }, true);
- // Keep track even if mouse is outside of canvas while dragging image
- window.addEventListener('mouseup', function (e) { thisCanvasZoom.mouseUpWindow( getEvent(e) ); }, false);
- window.addEventListener('mousemove', function (e) { thisCanvasZoom.mouseMoveWindow( getEvent(e) ); }, false);
- this.ctx = this.canvas.getContext('2d');
- this.paint();
- };
- // Helper function
- var getEvent = function( event )
- {
- if( !event ) // IE
- event = window.event;
- return event;
- }
- this.mouseDown = function( event ) {
- this.mouseIsDown = true;
- this.mouseLeftWhileDown = false;
- this.mouseDownX = mousePosX(event);
- this.mouseDownY = mousePosY(event);
- this.mouseMoveX = this.mouseDownX;
- this.mouseMoveY = this.mouseDownY;
- }
- this.mouseUp = function( event ) {
- this.mouseIsDown = false;
- this.mouseLeftWhileDown = false;
- this.mouseX = mousePosX(event);
- this.mouseY = mousePosY(event);
- if( this.mouseX == this.mouseDownX &&
- this.mouseY == this.mouseDownY )
- {
- // Didn't drag so assume a click.
- this.zoomIn();
- }
- }
- this.reposInner = function(win, img, pos) {
- if (win >= img) {
- return 0; // Das Fenster ist größer als der Inhalt -> oben rechts anordnen
- }
- if(win-pos > img) { // pos ist negativ, wenn fenstergröße+pos > bildgröße
- return win-img; // unterkante des Fensters liegt außerhalb des Bildes -> unten links anordnen
- }
- if (pos > 0) { // positive Position wäre oben/links vom Bild entfernt
- return 0;
- }
- return pos;
- }
- this.reposition = function(x, y) {
- var leveldiff = this.zoomLevelMax-this.zoomLevel;
- leveldiff = Math.pow(2, leveldiff);
- var imgX = this.imageWidth / leveldiff;
- var imgY = this.imageHeight / leveldiff;
- var winX = this.canvas.width;
- var winY = this.canvas.height;
- this.offsetX = this.reposInner(winX,imgX,x, true);
- this.offsetY = this.reposInner(winY,imgY,y, false);
- }
- this.mouseMove = function(event) {
- this.mouseX = mousePosX(event);
- this.mouseY = mousePosY(event);
- if( this.mouseIsDown )
- {
- var newOffsetX = this.offsetX + (this.mouseX - this.mouseMoveX);
- var newOffsetY = this.offsetY + (this.mouseY - this.mouseMoveY);
- this.calculateNeededTiles( this.zoomLevel, newOffsetX, newOffsetY );
- // console.log ("x: "+newOffsetX+" y: "+newOffsetY);
- this.mouseMoveX = this.mouseX;
- this.mouseMoveY = this.mouseY;
- this.reposition(newOffsetX, newOffsetY);
- this.paint();
- }
- }
- var mousePosX = function( event ) {
- // Get the mouse position relative to the canvas element.
- var x = 0;
- if (event.layerX || event.layerX == 0) { // Firefox
- x = event.layerX - thisCanvasZoom.canvas.offsetLeft;
- } else if (event.offsetX || event.offsetX == 0) { // Opera
- x = event.offsetX;
- }
- return x;
- }
- var mousePosY = function( event ) {
- var y = 0;
- if (event.layerY || event.layerY == 0) { // Firefox
- y = event.layerY - thisCanvasZoom.canvas.offsetTop;
- } else if (event.offsetY || event.offsetY == 0) { // Opera
- y = event.offsetY;
- }
- return y;
- }
- this.mouseOut = function( event ) {
- if( this.mouseIsDown )
- {
- this.mouseLeftWhileDown = true;
- }
- }
- this.mouseOver = function( event ) {
- // (Should be called mouseEnter IMO...)
- this.mouseLeftWhileDown = false;
- }
- this.mouseWheel = function( event ) {
- var delta = 0;
- if (event.wheelDelta) { /* IE/Opera. */
- delta = event.wheelDelta/120;
- if (window.opera) /* Opera 9 */
- delta = -delta;
- } else if (event.detail) { /* Mozilla */
- delta = event.detail/3;
- }
- if (delta) {
- if (delta < 0)
- this.zoomIn();
- else
- this.zoomOut();
- }
- if (event.preventDefault)
- event.preventDefault();
- event.returnValue = false;
- }
- // If mouseUp occurs outside of canvas while moving, cancel movement.
- this.mouseUpWindow = function( event ) {
- if( this.mouseIsDown && this.mouseLeftWhileDown )
- {
- this.mouseUp( event );
- }
- }
- // keep track of mouse outside of canvas so movement continues.
- this.mouseMoveWindow = function(event) {
- if( this.mouseIsDown && this.mouseLeftWhileDown )
- {
- this.mouseMove(event);
- }
- }
- // Zoom in a single level
- this.zoomIn = function () {
- this.zoom( this.zoomLevel + 1 );
- this.paint();
- }
- // Zoom out a single level
- this.zoomOut = function () {
- this.zoom( this.zoomLevel - 1 );
- this.paint();
- }
- //Zoom in at the centre of the canvas
- this.zoomInCentre = function () {
- this.mouseX = this.canvas.width / 2;
- this.mouseY = this.canvas.height / 2;
- this.zoomIn();
- }
- //Zoom out at the centre of the canvas
- this.zoomOutCentre = function () {
- this.mouseX = this.canvas.width / 2;
- this.mouseY = this.canvas.height / 2;
- this.zoomOut();
- }
- // Change the zoom level and update.
- this.zoom = function(zoomLevel) {
- if( zoomLevel >= this.zoomLevelMin && zoomLevel <= this.zoomLevelMax )
- {
- var newZoom = zoomLevel,
- currentZoom = this.zoomLevel;
- //
- // Calculate new offset
- //
- var zoomX = this.mouseX;
- var zoomY = this.mouseY;
- var currentImageX = zoomX - this.offsetX,
- currentImageY = zoomY - this.offsetY;
- var scale = this.tileZoomArray[newZoom]['width'] / this.tileZoomArray[currentZoom]['width'];
- var newImageX = currentImageX * scale,
- newImageY = currentImageY * scale;
- var newOffsetX = this.offsetX - (newImageX - currentImageX),
- newOffsetY = this.offsetY - (newImageY - currentImageY);
- this.calculateNeededTiles( newZoom, newOffsetX, newOffsetY );
- this.zoomLevel = newZoom;
- if (newOffsetX > 0) {
- newOffsetX = 0;
- }
- this.reposition(newOffsetX, newOffsetY);
- }
- }
- // Work out which of the tiles we need to download
- this.calculateNeededTiles = function( zoom, offsetX, offsetY ) {
- //
- // Calculate needed tiles
- //
- var tileZoomLevelArray = this.tileZoomArray[zoom];
- var tileSize = this.tileSize;
- var extend = tileSize / 2;
- var canvasLeft = -offsetX,
- canvasTop = -offsetY;
- var canvasRight = canvasLeft + this.canvas.width,
- canvasBottom = canvasTop + this.canvas.height;
- var tileLeft = 0, tileRight = 0, tileTop = 0, tileBottom = 0;
- var tile = null;
- var zoomWidth = tileZoomLevelArray['width'],
- zoomHeight = tileZoomLevelArray['height'];
- var columns = tileZoomLevelArray.length,
- rows = tileZoomLevelArray[0].length;
- var tileList = new Array();
- for( var iColumn = 0; iColumn < columns; iColumn++ )
- {
- for( var iRow = 0; iRow < rows; iRow++ )
- {
- tile = tileZoomLevelArray[iColumn][iRow];
- if( tile['tile'] == null && tile['waiting'] == false )
- {
- tileLeft = iColumn * tileSize;
- tileRight = tileLeft + Math.min( tileSize, zoomWidth - tileLeft );
- tileTop = iRow * tileSize;
- tileBottom = tileTop + Math.min( tileSize, zoomHeight - tileTop );
- if( !( tileLeft > canvasRight || tileRight < canvasLeft || tileTop > canvasBottom || tileBottom < canvasTop ) )
- {
- // request tile!
- tile['waiting'] = true;
- tileList.push( { "name" : zoom + "_" + iColumn + "_" + iRow, "file" : this.getTileFile( zoom, iColumn, iRow ) } );
- }
- }
- }
- }
- this.getTiles( tileList );
- }
- // Load the tiles we need with ImageLoader
- this.getTiles = function( tileList ) {
- if( tileList.length > 0 )
- {
- this.imageLoader = new ImageLoader( {
- "images": tileList,
- "onImageLoaded":function( name, tile ) { thisCanvasZoom.tileLoaded( name, tile ); }
- } )
- }
- }
- // Tile loaded, save it.
- this.tileLoaded = function ( name, tile ) {
- var tileDetails = name.split("_");
- if( tileDetails.length == 3 )
- {
- var tileInfo = this.tileZoomArray[tileDetails[0]][tileDetails[1]][tileDetails[2]];
- tileInfo['tile'] = tile;
- tileInfo['waiting'] = false;
- this.paint();
- }
- }
- this.paint = function () {
- var canvasWidth = this.canvas.width,
- canvasHeight = this.canvas.height;
- var ctx = this.ctx;
- var debug = this.debug;
- //
- // Clear
- //
- ctx.fillStyle = ctx.strokeStyle = "#fff";
- ctx.clearRect( 0, 0, canvasWidth, canvasHeight );
- //
- // Show images
- //
- var tileZoomLevelArray = this.tileZoomArray[this.zoomLevel];
- var columns = tileZoomLevelArray.length,
- rows = tileZoomLevelArray[0].length;
- var offsetX = this.offsetX,
- offsetY = this.offsetY;
- var canvasLeft = -offsetX,
- canvasTop = -offsetY;
- var canvasRight = canvasLeft + canvasWidth,
- canvasBottom = canvasTop + canvasHeight;
- var tileLeft = 0, tileRight = 0, tileTop = 0, tileBottom = 0;
- var tileCount = 0;
- var tile = null;
- var tileSize = this.tileSize;
- var zoomWidth = tileZoomLevelArray['width'];
- var zoomHeight = tileZoomLevelArray['height'];
- // 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)
- var fullTile = this.tileZoomArray[this.zoomLevelFull][0][0]['tile'];
- // 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!)
- for( var iColumn = 0; iColumn < columns; iColumn++ )
- {
- for( var iRow = 0; iRow < rows; iRow++ )
- {
- tileLeft = iColumn * tileSize;
- tileRight = tileLeft + Math.min( tileSize, zoomWidth - tileLeft );
- tileTop = iRow * tileSize;
- tileBottom = tileTop + Math.min( tileSize, zoomHeight - tileTop );
- if( !( tileLeft > canvasRight || tileRight < canvasLeft || tileTop > canvasBottom || tileBottom < canvasTop ) )
- {
- tile = tileZoomLevelArray[iColumn][iRow]['tile'];
- tileLeft += offsetX;
- tileRight += offsetX;
- tileTop += offsetY;
- tileBottom += offsetY;
- if( tile != null )
- {
- // Draw tile
- ctx.drawImage( tile, tileLeft, tileTop );
- if( debug )
- {
- ctx.strokeRect( tileLeft, tileTop, tileSize, tileSize );
- tileCount++;
- }
- }
- else
- {
- //
- // Tile still loading
- //
- if( true )
- {
- ctx.save();
- ctx.beginPath();
- ctx.moveTo( tileLeft, tileTop );
- ctx.lineTo( tileRight, tileTop );
- ctx.lineTo( tileRight, tileBottom );
- ctx.lineTo( tileLeft, tileBottom );
- ctx.closePath();
- ctx.clip();
- // TODO: Fill with a lower zoom image. (or possible use combination of higher zooms??)
- // but scaling images in canvas still VERY SLOW.
- // THIS NOTABLY SLOWS DOWN PANNING WHEN IMAGES ARE NOT YET LOADED ON SOME BROWSERS.
- ctx.drawImage( fullTile, offsetX, offsetY, zoomWidth, zoomHeight );
- ctx.restore();
- }
- else
- {
- ctx.fillStyle = "#999";
- ctx.fillRect( tileLeft, tileTop, tileRight - tileLeft, tileBottom - tileTop );
- }
- }
- }
- }
- }
- //
- // Canvas area
- //
- ctx.strokeStyle = "#000";
- ctx.strokeRect( 0, 0, canvasWidth, canvasHeight );
- if( debug )
- {
- //
- // DEBUG!
- //
- ctx.fillStyle = "#0f0";
- ctx.font = "normal 12px Arial";
- // Text
- ctx.fillText( this.mouseX + "," + this.mouseY + " | " + offsetX + "," + offsetY + " | " + tileCount, 0, 20 );
- // Grid
- ctx.strokeStyle = "#f00";
- for( var y = 0; y < canvasHeight; y += tileSize )
- for( var x = 0; x < canvasWidth; x += tileSize )
- ctx.strokeRect( x, y, tileSize, tileSize );
- }
- };
- }
Add Comment
Please, Sign In to add comment