You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

326 lines
11 KiB
Plaintext

11 months ago
"use strict";
/*
Copyright [2014] [Diagramo]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/** Creates a minimap for the big canvas
*
* @constructor
* @this {Minimap}
* @param {HTMLObject} bigCanvas (usually the canvas object)
* @param {HTMLObject} minimapContainer The minimap DOM Object (usually the container DIV)
* @author Zack Newsham <zack_newsham@yahoo.co.uk>
* @author Alex Gheorghiu <alex@scriptoid.com>
*
* @see See /documents/specs/minimap.jpg for a visual representation of the minimap architecture.
* @see See minimap.css to fully understand the CSS positioning
*/
function Minimap(bigCanvas, minimapContainer){
/**Keeps track it minimap is selected or not*/
this.selected = false;
/**The big canvas DOM object (canvas). You can get it also by document.getElementById('map')*/
this.bigCanvas = bigCanvas;
/**The minimap DOM object (div). You can get it also by document.getElementById('minimap')*/
this.minimapContainer = minimapContainer;
/*Stores reference to div selection rectangle (as a div)*/
this.selection = document.createElement("div");
this.selection.id = "selection";
//create a canvas to paint the minimap on
/**Reference to small DOM canvas (minimap)*/
this.smallCanvas = document.createElement("canvas");
this.smallCanvas.style.width = "100%";
this.smallCanvas.style.height = "100%";
//the size has to be specified so we get a minimap that has the same proportions as the map
this.minimapContainer.style.height = Minimap.prefferedHeight + "px"; //initially it's zero
this.minimapContainer.style.width = Minimap.prefferedWidth + "px";
this.minimapContainer.appendChild(this.smallCanvas);
this.minimapContainer.appendChild(this.selection);
//If big canvas is scrolled --effect--> update minimap position
this.bigCanvas.parentNode.onscroll = function (scrollObject){
return function(event){
scrollObject.updateMinimapPosition();
}
}(this);
//Minimap move mouse --effect--> update map position
this.minimapContainer.onmousemove = function(aMinimapObject){
return function(event){
aMinimapObject.onScrollMinimap(event);
return false;
}
}(this);
//Selection mouse down --effect--> select minimap
this.selection.onmousedown = function(aMinimapObject){
return function(event){
/*prevent Firefox to allow canvas dragg effect. By default FF allows you
* to drag the canvas out of it's place, similar to drag an image*/
event.preventDefault();
aMinimapObject.selected = true;
}
}(this);
//Canvas mouse down --effect--> center selection
this.smallCanvas.onmousedown = function(aMinimapObject){
return function(event){
aMinimapObject.selected = true;
aMinimapObject.onScrollMinimap(event);
}
}(this);
//because we have a fixed width this ratio will give us the (minimap width / bigmap width) percent
/**The ratio between the big map and the small one*/
this.ratio = 0;
this.initMinimap();
}
/**Preffered/default width*/
Minimap.prefferedWidth = 115;
/**Preffered/default height*/
Minimap.prefferedHeight = 250;
Minimap.prototype = {
constructor : Minimap,
/**
*Update the minimap (canvas) with a scalled down version of the big map (canvas)
*@author Zack
*TODO: because of this the whole paiting is very slow. We need to optimize it
*Ideea: make update not real time....but with a delay...1 s for example so
*instead of hundreds of micro repaint we will have only a few
**/
updateMinimap:function(){
//this part should be moved somewhere more relevant, only here for testing
var canvas = this.bigCanvas;
//recreate a new image from encoded data
//TODO: remove following lines....not used anymore?
//var ctx = canvas.getContext("2d");
//var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var thisCtx = this.smallCanvas.getContext("2d");
thisCtx.beginPath();
thisCtx.clearRect(0,0,this.smallCanvas.width,this.smallCanvas.height)
thisCtx.closePath();
thisCtx.stroke();
thisCtx.save();
/*@see http://STACKoverflow.com/questions/3448347/how-to-scale-an-imagedata-in-html-canvas*/
thisCtx.scale(this.ratio/100 , this.ratio/100 );
thisCtx.drawImage(canvas, 0,0);
thisCtx.restore();
},
/**
*Reset/init ratio and minimap's height
*@author Zack, Alex
**/
initMinimap:function(){
/*
*We need to recompute the width and height of the minimap.
*Initially minimap has a Minimap.prefferedWidth x Minimap.prefferedHeight size
*Now we will compute the vertical ration and horizontal one
*and pick the smaller one as general ratio and based on that we will
*recompute the width and height of the minimap
**/
var horizontalRatio = Minimap.prefferedWidth * 100 / ($(this.bigCanvas).width()); //horizontal ratio
var verticalRatio = Minimap.prefferedHeight * 100 / ($(this.bigCanvas).height()); //vertical ratio
//pick smaller ratio
if(horizontalRatio < verticalRatio){
this.ratio = horizontalRatio;
}
else{
this.ratio = verticalRatio;
}
//recompute width and height
var width = $(this.bigCanvas).width() * this.ratio / 100;
var height = $(this.bigCanvas).height() * this.ratio / 100;
//update minimap container sizes
this.minimapContainer.style.width = width +"px";
this.minimapContainer.style.height = height + "px";
//small canvas will fill all it's parent space (it's a gas :) )
this.smallCanvas.width = $(this.minimapContainer).width();
this.smallCanvas.height = $(this.minimapContainer).height();
//compute selection size
var selectionWidth = this.ratio * ($(this.bigCanvas.parentNode).width() - scrollBarWidth) / 100;
var selectionHeight = this.ratio * ($(this.bigCanvas.parentNode).height() - scrollBarWidth) / 100;
if(selectionWidth > $(this.minimapContainer).width()){ //if selection bigger than the container trim it
selectionWidth = $(this.minimapContainer).width();
}
if(selectionHeight > $(this.minimapContainer).height()){ //if selection bigger than the container trim it
selectionHeight = $(this.minimapContainer).height();
}
//update selection
this.selection.style.width = selectionWidth + "px";
this.selection.style.height = selectionHeight + "px";
},
/**Called by Minimap, usually it just moves/shift the big canvas into the right
*position
**/
updateMapPosition:function(){
var x = parseInt(this.selection.style.left.replace("px",""));//+border
var y = parseInt(this.selection.style.top.replace("px",""));//+border
this.bigCanvas.parentNode.scrollLeft = x / this.ratio * 100;
this.bigCanvas.parentNode.scrollTop = y / this.ratio * 100;
},
/**
*Called whenever we scroll the big map/canvas. It will update the minimap
*@author Zack
*/
updateMinimapPosition:function(){
//get big map's offset
var x = parseInt(this.bigCanvas.parentNode.scrollLeft);
var y = parseInt(this.bigCanvas.parentNode.scrollTop);
//compute minimap's offset
x = x * this.ratio / 100 ;
y = y * this.ratio / 100 ;
//apply the offset
this.selection.style.left = x + "px";
this.selection.style.top = y + "px";
},
/**Called when we move over the minimap and the 'minimap' was previously selected
*@param {Event} event - the event triggered
*@author Zack
**/
onScrollMinimap:function(event){
if(this.selected == true){ //we will 'action' only if the select are is selected
//try to reposition the selection
var mousePos = this.getInternalXY(event);
var containerWidth = this.minimapContainer.style.width.replace("px","");
var containerHeight = this.minimapContainer.style.height.replace("px","");
var width = this.selection.style.width.replace("px","");
var height = this.selection.style.height.replace("px","");
//if we are scrolling outside the area, put us back in
if(mousePos[0] - width/2 < 0){
mousePos[0] = width/2;
}
if(mousePos[1] - height/2 < 0){
mousePos[1] = height/2;
}
if(mousePos[0] + width/2 > containerWidth){
mousePos[0] = containerWidth - width/2;
}
if(mousePos[1] + height/2 > containerHeight){
mousePos[1] = containerHeight - height/2;
}
//update our minimap
if(mousePos[0] != undefined){
this.selection.style.left = mousePos[0] - width/2 + "px";
this.selection.style.top = mousePos[1] - height/2 + "px";
}
//update the actual area
this.updateMapPosition();
}
else{
this.selected = false;
}
},
/**
*Computes the boundary of minimap relative to the whole page
*@author Zack
*@author (comments) Alex
*@see <a href="http://www.quirksmode.org/js/findpos.html">http://www.quirksmode.org/js/findpos.html</a>
**/
getBounds:function(){
var thisMinX = 0;
var thisMinY = 0;
var obj = this.minimapContainer;
/*Go recursively up in the hierarchy (parent) and find the minx and min y*/
do{
thisMinX += obj.offsetLeft;
thisMinY += obj.offsetTop;
/*offsetParent - Returns a reference to the object that is the current
*element's offset positioning context*/
}while(obj = obj.offsetParent);
/*Add minimap's width and height*/
var thisMaxX = thisMinX + parseInt(this.minimapContainer.style.width.replace("px",""));
var thisMaxY = thisMinY + parseInt(this.minimapContainer.style.height.replace("px",""));
return [thisMinX, thisMinY, thisMaxX, thisMaxY];
},
/**Get the (x, y) position relative to
*current DOM object (minimap div in our case)
*@param {Event} event - the event triggered
*@return {Array} of [x, y] relative position inside DOM object
*@author Zack
*@author (comments) Alex
**/
getInternalXY:function(event){
var position = [];
var thisBounds = this.getBounds();
if(event.pageX >= thisBounds[0] && event.pageX <= thisBounds[2] //if event inside [Ox bounds
&& event.pageY >= thisBounds[1] && event.pageY <= thisBounds[3]) //if event inside [Oy bounds
{
position = [event.pageX - thisBounds[0], event.pageY - thisBounds[1]];
}
return position;
}
}