ghtyan

导航

类似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 )
 

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<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 == 1return 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="&nbsp;";
        
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>



暂时先写到这里了,有时间再继续下去~~~

posted on 2007-09-05 13:47  ghtyan  阅读(7051)  评论(36编辑  收藏  举报