1.用于被显示的base地图,可以用svg等矢量图形文件来表示。
2.用于控制地图缩放的“放大”,“缩小”按钮组件。
3.用于控制地图显示视图移动的“↑”,“↓”,“←”,“→”按钮组件。
构成总结:根据以上的素材,或者用更贴合vue框架的语言来说,拥有以上组件,可以完成一个地图组件所需的基本功能,即:
地图显示地图缩放地图视图移动由此脑海里已经有了一个大致的地图组件的雏形(大概长这样)
实际在项目中遇到的情况很可能就是从这一步开始的,按照画面的式样去做需求分析,这里为了记叙一个完整的思路,所以引进一下前面的构思做铺垫。
有了画面和初步的功能分析,现在来思考一下具体实现的框架。
由于现在这个地图充其量不过是画面的一个组件,或者通俗一点说是画面的一个小的功能,所以整体还是采用画面包含组件的结构来部署资源,如下:
到此为止需求分析明确了,目录结构也很清晰,可以着手实现功能了。
功能作成(coding) 分析功能在着手做以前,最好脑子里可以先过一遍,我到底需要实现几个功能,尤其是试做一个全新的机能,让自己先有点谱,比做到哪里算哪里要有建设性的多。
为了做这样的一个可以完成显示,移动控制和缩放功能的地图,我需要提出疑问:
1.固定大小的矢量图形地图,如何缩放?
2.如何来控制显示区域的移动?
带着疑问,根据拿到的素材,即svg文件做了简单分析。
svg的显示原理是其实是通过viewBox属性去控制的,本身只是一个固定像素的矢量图形。
这时只要简单地百度一下就会知道viewBox的显示原理。好记性不如烂笔头,这里也记录一下我的分析和理解。
做一个简单的svg画布,宽为1000,高为800
放一些用于标记位置坐标的矩形,宽高都为200,并在对应的矩形中写上当前矩形右下角到画布起点(左上角)的横向距离和纵向距离,目的是为了标定了画布起点到该矩形的范围。
浏览器预览效果如下:
可以看到现在viewBox=“0 0 1000 800”,其中这四个参数分别代表了
0:从svg横坐标为0的点开始显示
0:从svg纵坐标为0的点开始显示
1000:显示宽度为1000
800:显示高度为800
通过设定viewBox的前两个参数可以控制整个svg画布的显示起始位置
->同理可以控制地图的显示起始位置
->让地图看起来向各个方向移动
举例
修改viewBox="0 0 1000 800"为viewBox=“200 0 1000 800”
即从画布横向距离为200,纵向距离为0的点开始,取一个宽为1000,高为200的范围进行显示。
效果如下:
可以看到之前靠左侧宽度为200的一列矩形消失不见了,取而代之现在顶格显示的是原先距离左上角横向距离200,显示范围为400x200的矩形,即显示画布的视图向右边移动了200。
举例
同理修改viewBox="0 0 1000 800"为viewBox=“0 200 1000 800”,即纵坐标向下200。就可得到视图向下移动200的效果
效果如下:
移动的原理说到这里已经可以理解了,接着要着手看缩放的原理。
之前也提到了viewBox的后两位参数是用来控制显示画布的范围,当画布上的地图1:1显示时,显示区域应该等于地图的实际大小。
但如果要放大地图,就等同于显示的窗口大小不变,显示的范围缩小了,从原本可以看到地图的全貌变成只能看到地图放大以后的一部分。
svg会将这部分视图重新显示在固定高宽的窗口中,不过既然显示的视图变小了,svg需要对这部分图形做扩充,从而让看到的内容还是充满整个窗口的。由于svg是矢量图形,扩充以后也不会失真,从而达到了视图上的“放大”的效果。
理解完毕以后来实 *** 一下。
举例
修改viewBox="0 0 1000 800"为viewBox=“0 0 500 400”,即将显示的区域缩小为原先的1/2。就可得到放大1倍的视图效果
效果如下:
通过标记的每个矩形的显示范围也可以看出,当前窗口显示了高为400,宽为500的画布大小。
分析功能总结由此之前的两个疑问,即如何达成地图视图的移动和缩放就完成了。
代码分块快乐的写函数时间(假的,这一步没有之前的功能分析,很容易写的很乱,增熵行为在协同coding是大忌)
大概列出需要的函数:
MapMoveUp()
MapMoveDown()
...其余两个move函数
MapZoomIn()
MapzoomOut()
DisplayMap()
根据这些函数可以列出需要的data:
iDisplayTop
iDisplayiLeft
iDisplayHeight
iDisplayWidth
↑需要向地图组件传的参数
iMapShowScale
iMapMoveXStep
iMapMoveYStep
↑ *** 作以后带来的自变量
具体功能实现
在每个函数中做好自己的一小部分就好,时刻注意代码的框架管理。以下举几个例子,主要要记住每个函数 *** 作的关键data是谁,不要浪费脑细胞去想不相关的。
DisplayMap()
{
this.$ref.refMapName.SetMapData(this.iDisplayiLeft,this.iDisplayTop,this.iDisplayHeight,this.iDisplayWidth);
}
↑在Display函数中只需要直接影响地图显示的4个量,
最多涉及4个量的相关转换,具体各个量的变化应该放在相关的触发函数里去做。
MapMoveUp()
{
if(this.iDisplayTop <= 0 )
{
this.iDisplayTop = 0;
}
else
{
this.iDisplayTop = this.iDisplayTop - this.iMapMoveYStep;
}
this.DisplayMap();
}
向上的按钮实则在改变的就是显示视图的纵坐标,每次按下则上移(减去)固定的步长。不过视图不可能一直上移,当iDisplayTop小于0,即显示的纵坐标已经跑到地图上面,则需要限制它为0,不然就会无止境的上移。
凝练一下每个Move函数在做的内容其实就是
这也符合上面说的函数只关心自己的关键data这点。
同理MapMoveLeft()也可如法炮制,但在做MapMoveDown()和MapMoveRight()的时候要注意,向右或向下移动时,iDisplayTop和iDisplayLeft的范围限制会随着显示视图缩小的倍率 发生变化。究竟是怎么一回事儿呢?可以来思考一下:
1倍的视图下,并不能下移或者右移,当然也不能左移或者上移。
后者很简单,因为有iShowTop和iShowLeft的<0的条件限制,前者是为什么呢?因为当前的显示区域等于地图的实际大小,也就是说没有多余的可以向右或者向下移动的空间了,但是当显示区域减小(地图视觉效果放大)时,一部分的地图超出了显示区域,这就是当前可以右移或者下移的空间。
如图:
所以这段随着地图缩放的区域的大小限制了视图最大的右移和下移,即限制iDisplayLeft和iDisplayTop的条件。那就来看看不同倍率下,iDisplayLeft可以达到的最大值为多少,如图:
通过图示法不难发现,iDisplayLeft可以达到的最大值即iDisplayMaxLeft=iMapWidth - 当前显示区域的宽度(iDisplayWidth)
同理iDisplayMaxTop=iMapHeight - 当前显示区域的高度(iDisplayHeight)
很显然iDisplayWidth和iDisplayHeight是随iDisplayScale自变量产生的应变量,这不应该被定义在data的成员变量中,而是可以通过iMapHeight/iMapScale 和 iMapWidth/iMapSale 得到的。
那么剩下两个Move函数也可以得到了,以MapMoveRight()为例:
MapMoveRight()
{
let iDisplayMaxLeft = iMapwidth - iMapWidth/iMapScale;
if(iDisplayLeft >= iDisplayMapMaxLeft)
{
iDisplayLeft = iDisplayMapMaxLeft ;
}
else
{
iDisplayLeft = iDisplayLeft + iMapMoveXStep;
}
this.DisplayMap();
}
控制移动的函数到此为止基本完成,速速把缩放的功能也写了,这一步依然牢记控制的关键data,很简单,每一次缩放只针对iMapScale做倍乘而已,当然不可能一味放大或缩小,和之前一样缩放函数中要做的还是两点,或者说一点:
控制iMapScale 控制iMapScale的范围让iMapScale倍乘或倍除某个值达到缩放的效果而改变地图的高宽,即对地图缩放显示的并不在此处理,统一放在显示地图的DIsplayMap()中处理。此函数里只关心iMapSale,最多涉及其他与地图4个主要参数无关但相应产生的应变量,例如iMaxDisplayLeft。
它在之前的data定义中没有定义,而是通过每一次进入函数申请临时变量,通过进一步计算来取得,但如果在后续的处理中需要很方便的获取它,也可以进行定义,并在缩放函数中去进行刷新。
举例
iMaxLeftDisplayLeft : 0,
UpdateDisplayMaxTopLeft()
{
this.iMaxDisplayLeft = this.iMapWidth - this.iMapWidth/this.iMapScale;
this.iMaxDisplayTop = this.iMapHeight - this.iMapHeight/this.iMapScale;
}
MapZoomIn()
{
if(this.iMapScale >= 8)//以8倍为例,实际可以定义最大的倍率常量,方便后续很容易的更改
{
this.iMapScale = 8;
}
else
{
this.iMapScale = this.iMapScale * 2;//以2为例,实际可以定义倍率步进,方便后续很容易的更改
}
this.UpdateDisplayMaxTopLeft();
this.DisplayMap();
}
而此时不要忘了,地图的显示处理中,要将刚刚变化的iMapScale绑定进去。
DisplauMap()
{
this.iDisplayHeight = this.iDisplayHeight/this.iMapScale;
this.iDisplayWidrh = this.iDispWidrh/this.iMapScale;
this.$ref.refMapName.SetMapData(this.iDisplayiLeft,this.iDisplayTop,this.iDisplayHeight,this.iDisplayWidth);
}
由此,基本的地图控制基本完成。
优化及改进在实际测试时,我发现,当在8倍大的缩放情况下挪至地图最右端后,再按下缩小键,显示区域明显超过了当前地图的范围,也就是当前的iDisplayLeft > iMaxDisplayLeft,这是由于在较大的倍率时,可以移动的范围比较小倍率时要多,所以从大倍率回到小倍率时,已经在大倍率下最大left位置的视图就会在小倍率视图中相对偏移出地图。
所以需要做进一步的显示控制,这就涉及到DisplayTop和iDisplayLeft的check,前面说了,地图的显示统一做在DisplayMap()中,这里容易混淆的点是,是否因为点击了放大缩小按钮,画面显示不正确,所以要在缩放按钮里做判断。从一开始就提到的每个函数要有一个关键的data处理可以得到结论,这样做完全不符合对数据的保密性和单一控制性,缩放最关心的依旧还是iMapScale,而不是其他,这里要完善的措施应该做在显示地图时,即DisplayMap()中添加如下部分代码:
if(this.iDisplayTop > this.iMaxDisplayTop)
{
this.iDisplayTop = this.iMaxDisplayTop;
}
if(this.iDisplayLeft > this.iMaxDisplayLeft)
{
this.iDisplayLeft = this.iMaxDisplayLeft;
}
最新的DisplayMap()应该是这样:
DisplauMap()
{
if(this.iDisplayTop > this.iMaxDisplayTop)
{
this.iDisplayTop = this.iMaxDisplayTop;
}
if(this.iDisplayLeft > this.iMaxDisplayLeft)
{
this.iDisplayLeft = this.iMaxDisplayLeft;
}
this.iDisplayHeight = this.iDisplayHeight/this.iMapScale;
this.iDisplayWidrh = this.iDispWidrh/this.iMapScale;
this.$ref.refMapName.SetMapData(this.iDisplayiLeft,this.iDisplayTop,this.iDisplayHeight,this.iDisplayWidth);
}
改善部分可以对按钮的式样做一些调整,比如设置按钮的活性非活性,由于之前创建的变量已经足够支撑条件判断,在这一步的函数会很简单,只需要判断当前的iDisplayTop和iDisplayLeft是否超过其边界值,如果没有则可以压下,超过则不可压下,而完全不需要对这些控制地图显示的变量进行任何 *** 作,这块函数只关心是否要把按钮的状态设为true或者false,仅此而已。
总结以上就是一次开发的思路记录,搭积木就是宏观的规划,不堆积不必要的,永远关注这一块稳不稳固,有无精简的可能,及时调整不合理的地方,从小到大,逐步完善。希望自己在以后的开发实践中也能贯彻这样的思路,写出不仅能跑还能各种变更也不会头昏眼花的好coding。bug或许永远改不完,但若能控制它生长的范围,是一件听上去很有希望的好事儿。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)