类似GoogleMap地图网站的简单实现(1)
实现分析
地图分割、拼凑(地图网站的简单实现--图片分割小工具)
(图片来自都市圈地图)
地图网站主要展现的就是地图图片,当查看或拖动地图的时候,看到的是一张完整的当前视窗地图图片,但实际上是一张张小图片拼凑而成。地图是拼凑而成那就涉及到图片是如何分割,分割的方法很多,我说一下其中一种:地图本来就是一张很大的图片,把大图当作放在二维坐标系中(单位像素),大图左上角为圆点,沿着坐标线一小块一小块的切下来,这里以256像素X256像素的正方形为一小块,切下来之后按照坐标点除以小图边长命名(比如某小块是从坐标(256,512)处开始切图,则文件名为1,2.jpg),这样就可以知道小图的位置了。网站上看到多种倍数的地图,也就是把大图缩写相应的倍数再按同样的规格切小图;不同类型的图片(比如航拍图,2D图)也就同样的道理了,至于怎么存放就按自己的思路存放,怎么存就怎么取出来。
Web中地图展现
网页中展现地图是通过Div层实现,一个层是作为窗口,层的大小就是看到的地图大小(简称窗口层);一个层是用来拖动时随鼠标移动的层(简称移动层),前面说道大图是当作放在二维坐标系中,那么这个层就是充当这个坐标系,你需要显示哪张小图,就把小图拼在它切出来的坐标上,要知道显示哪张小图这就得通过当前定位的坐标(center)(定位的坐标就作为窗口层的中心位置)和窗口层的宽度(width)以及高度(height)计算出要拼上去的小图。
计算推理:
横向坐标范围:center.x-width/2 至 center.x+width/2
纵向坐标范围:center.y-height/2 至 center.y+height/2
小图是一块256像素的正方形,因此
小图横向下标范围:Math.floor((center.x-width/2)/256) 至 Math.floor((center.x+width/2)/256)
小图纵向下标范围:Math.floor((center.y-height/2)/256) 至 Math.floor((center.y+height/2)256)
通过以上下标范围就可以找到相应的图片,把图片显示在相应的坐标位置上,小图是拼凑得了,还要把镜头(窗口层)移动到center这个位置,窗口层是固定的不能移动,移动是相对的那就移动一下移动层吧
移动层的Left=width/2-center.x
移动层的top=height/2-center.y
这样简单的地图显示就可以实现了,看一下还有一个遮盖层,其实这层是透明的,不影响地图的显示,其作用是避免直接操作到地图图片,以及避免IE下出现图片工具条
还是来看看代码和效果吧
为了好测试,把代码都写在页面上了。 查看演示 (兼容IE和Firefox )
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
<style type="text/css">
body{
margin:0px;padding:0;
}
.test{background-color:black;}
.test a{float:left;text-decoration: underline;color: white;background-color:black;display:block;width:200px;text-align:center}
.test a:hover{float:left;text-decoration: underline;color: black;background-color:white;display:block;width:200px}
</style>
<script type="text/javascript">
// http://ghtyan.cnblogs.com 代码供大家学习
/*============================prototype.js中的部分函数================================*/
Object.extend = function(destination, source) {
for (property in source) destination[property] = source[property];
return destination;
}
Function.prototype.bind = function(object) {
var __method = this;
return function() {
return __method.apply(object, arguments);
}
}
Function.prototype.bindAsEventListener = function(object) {
var __method = this;
return function(event) {
return __method.call(object, event || window.event);
}
}
function $() {
if (arguments.length == 1) return get$(arguments[0]);
function get$(el){
if (typeof el == 'string') el = document.getElementById(el);
return el;
}
}
/*============================基础类================================*/
function CPoint(x,y){ //坐标点
this.x=x;
this.y=y;
}
function CSize(width,height){ //矩形区域
this.width=width;
this.height=height;
}
function CBounds(p1,p2){ //矩形坐标范围,参数左上点和右下点组成
this.minX=p1.x;
this.minY=p1.y;
this.maxX=p2.x;
this.maxY=p2.y;
this.getSize=function(){
return new CSize(this.maxX-this.minX,this.maxY-this.minY);
}
}
window.CEvent={ //自定义事件处理
addListener:function(obj,target,act){
if(obj.attachEvent) obj.attachEvent("on"+target,act);
if(obj.addEventListener) obj.addEventListener(target,act,false);
},
removeListener:function(obj,target,act){
if(obj.detachEvent) obj.detachEvent("on"+target,act);
if(obj.removeEventListener) obj.removeEventListener(target,act,false);
}
}
/*============================地图类================================*/
function CMap(div){
this.holder=div; //地图的载体,即窗口层
this.config={
imgsize:256 //小图边长
};
this._center; //实时中心点
this.mvl; //用于移动的层,即移动层
this.mouseopt={ //鼠标在地图上操作选项
down:false, //是否按下
move:false, //是否按下并移动过
dx:0,dy:0, //按下时移动层的left和top
ex:0,ey:0 //按下时的事件坐标
};
this.mapimage=new Array();//当前显示的地图集合
this.cacheimage=new Array();//图片对象缓存,避免重复创建和删除IMG对象
}
CMap.prototype = {
_getimage:function(){//获取一个IMG对象
var img =this.cacheimage.shift();
if(img==null) {img=document.createElement("IMG");}
return img;
},
_loadmap:function(){//加载地图
var bounds=this.getBounds();
var x1=Math.floor(bounds.minX/this.config.imgsize);
var x2=Math.ceil(bounds.maxX/this.config.imgsize);
var y1=Math.floor(bounds.minY/this.config.imgsize);
var y2=Math.ceil(bounds.maxY/this.config.imgsize);
this.mvl.style.left=-bounds.minX+"px";
this.mvl.style.top=-bounds.minY+"px";
for(var y=y1;y<y2;y++)
{
for(var x=x1;x<x2;x++)
{
var img = this._getimage();
this.mapimage.push(img);
img.style.position="absolute";
img.style.backgroundColor="#AEAEAE";
img.style.left=x*this.config.imgsize+"px";
img.style.top=y*this.config.imgsize+"px";
img.style.width=this.config.imgsize+"px";
img.style.height=this.config.imgsize+"px";
img.alt=x+","+y;
this.mvl.appendChild(img);
}
}
},
_resize:function(evt){ //地图resize事件,内部函数
this.onresize();
this._clearmap();
this._loadmap();
},
_mapmousedown:function(evt){
//记录鼠标操作信息
this.mouseopt.down=true;
this.mouseopt.ex=evt.clientX;
this.mouseopt.ey=evt.clientY;
this.mouseopt.dx=this.mvl.offsetLeft;
this.mouseopt.dy=this.mvl.offsetTop;
//为了使鼠标移动到浏览器外部,事件依然有效,IE & firefox
if(this.mvl.setCapture)
this.mvl.setCapture();
else if(window.captureEvents)
window.captureEvents(Event.MOUSEMOVE|Event.MOUSEUP);
},
_mapmove:function(evt){
if(this.mouseopt.down==true)
{
//拖动时记录鼠标拖动过以及设定移动层的位置
this.mouseopt.move=true;
this.mvl.style.left=this.mouseopt.dx+(evt.clientX-this.mouseopt.ex)+"px";
this.mvl.style.top=this.mouseopt.dy+(evt.clientY-this.mouseopt.ey)+"px";
}
},
_mapmouseup:function(evt){
//取消事件捕捉
if(this.mvl.releaseCapture)
this.mvl.releaseCapture();
else if(window.releaseEvents)
window.releaseEvents(Event.MOUSEMOVE|Event.MOUSEUP);
if(this.mouseopt.down==true&&this.mouseopt.move==true)
{
//重新设定实时中心位置
this._clearmap();
this._center.x=this.holder.offsetWidth/2-this.mvl.offsetLeft;
this._center.y=this.holder.offsetHeight/2-this.mvl.offsetTop;
this._loadmap();
}
this.mouseopt.down=false;
this.mouseopt.move=false;
},
_clearmap:function(){//清除当前地图图片,放入图片缓存
var tm;
while((tm=this.mapimage.pop())!=null){
this.mvl.removeChild(tm);
this.cacheimage.push(tm);
}
}
}
CMap.prototype.init=function(){
//创建移动层
this.holder.style.overflow="hidden";
this.mvl=document.createElement("DIV");
this.mvl.style.cssText="position:absolute;left:0;top:0;z-index:1";
this.holder.appendChild(this.mvl);
//创建遮盖层
this.cover=document.createElement("DIV");
this.cover.innerHTML=" ";
this.cover.style.cssText="position:relative;left:0;top:0;z-index:2;width:2000px;height:2000px;background-color:gray;filter:alpha(opacity=0);opacity:0;";
this.holder.appendChild(this.cover);
this._loadmap();
//事件绑定
CEvent.addListener(window,"resize",this._resize.bindAsEventListener(this));
CEvent.addListener(this.holder,"mousedown",this._mapmousedown.bindAsEventListener(this));
CEvent.addListener(this.holder,"mousemove",this._mapmove.bindAsEventListener(this));
CEvent.addListener(this.holder,"mouseup",this._mapmouseup.bindAsEventListener(this));
}
CMap.prototype.reload=function(){ //外部强制重新加载地图
this._loadmap();
}
CMap.prototype.onresize=function(evt){ //地图resize事件,供外部定义函数
}
CMap.prototype.setCenter=function(p){
this._center = p;
}
CMap.prototype.getCenter=function(){
return this._center;
}
CMap.prototype.getBounds=function(){ //获取地图范围
var p1= new CPoint(this._center.x-this.holder.offsetWidth/2,this._center.y-this.holder.offsetHeight/2);
var p2= new CPoint(this._center.x+this.holder.offsetWidth/2,this._center.y+this.holder.offsetHeight/2)
return new CBounds(p1,p2);
}
</script>
</head>
<body>
<div id="d_map" style="width:100%; background-color:gray; position:relative;overflow:hidden;">
</div>
</body>
</html>
<script type="text/javascript">
function fit(){
document.getElementById("d_map").style.height = document.documentElement.clientHeight+"px";
}
fit();
var map = new CMap(document.getElementById("d_map"));
map.setCenter(new CPoint(1024,2048));
map.init();
map.onresize=fit;
</script>
暂时先写到这里了,有时间再继续下去~~~