地图切换引擎(多种地图之间一键切换)

业务场景

在工作中遇到的项目场景。由于项目有国内和国外用户,对地图的需求不同,国内人士倾向于使用百度,高德等地图;而国外用户则更多的使用Here地图。而很麻烦的是,百度,腾讯等地图对国外的支持并不友好,很多地方都是模糊的。Here地图确刚好相反,由于天朝的种种原因,导致Here地图在国内的地图是无法访问的。基于这种情况,就需要项目本身支持多种地图的切换。这个demo只是基于 百度地图 以及Here地图之间的相互切换,不过我本身对代码做了拓展,想要支持别的地图,按照我的注释添加对应的地图引擎即可。(第一次写博客,不足之处希望大家多多指教,一个前端菜鸡)

HTML页面准备

这部分没啥说的啦,抄起键盘就是一顿CV。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>地图引擎测试</title>
    <style>
        #MapEngineContant{
            width: 640px;
            height: 640px;
        }
    </style>
</head>
<body>
    <!-- 可显示地图列表 -->
    <select id="MapEngineList">
    </select>
    <!-- 显示地图区域 -->
    <div id="MapEngineContant"></div>
    <script src="./MapEngine.js"></script>
</body>
</html>

参数准备

在MapEngine.js下我准备了一些常量用于引擎本身,当然,这些常量我都已经抽取出来了,方便外部直接赋值(这只是一个测试实例,我并没有使用模块化开发,需要模块化开发的完整实例,可以联系我)

//默认地图引擎,由系统设置提供
//具体数据猜测应该放在redux仓库中 通过模块导入
const defaultMapEngine = 'BaiduMap'

//可支持的地图引擎
//随便写的几个,想要地图拓展就在这里加就好了,目前可用的只有百度和Here
const MapEngineList = {
    BaiduMap: '百度地图',
    HereMap: 'Here地图',
    TencentMap: '腾讯地图',
    GaodeMap: '高德地图',
    BinyingMap: '必应地图'
}

//起始坐标点,外部参数,(坐标定位暂时不处理,需要根据算法重新换算)
const defaulePoint = {
    x: 0,
    y: 0
}

可支持地图列表的显示

工作中我的设定是,可支持的地图引擎由后台存放,返回前端。如果没有明确的要求,这里也是可以写死的

//根据 MapEngineList 来渲染页面的地图选择选项
//MapList -> Object 可支持的地图引擎
function RenderMapList(MapList) {
    //获取地图显示列表
    let MapShowList = document.getElementById('MapEngineList')

    //渲染地图显示列表
    for (Mapitem in MapList) {
        let MapListNode = document.createElement('option')
        let text = document.createTextNode(`${MapList[Mapitem]}`)
        MapListNode.appendChild(text);
        MapShowList.appendChild(MapListNode)
        //默认显示 默认加载项
        if (`${Mapitem}` === defaultMapEngine)
            MapListNode.setAttribute('selected', 'selected')
    }
}

核心函数,用于动态加载Script脚本

这个函数是实现功能的核心函数,用于动态向html中添加脚本,用的思路是在网上查找到的文档,核心思路就是动态添加一个script元素,然后修改他的src属性达到动态加载的目的。但是这里有一个坑,那就是同步加载和异步加载的问题,需要根据具体的使用场景来实现,我在这里采用了回调函数的方式(我的代码拷贝自此处,没联系到本人,冒昧引用,如有侵犯,联系我删除哦!!)

https://blog.csdn.net/u010289111/article/details/62892623

//动态加载script
//url -> String 资源加载地址
function loadScript(url, callback) {
    let script = document.createElement('script')
    script.type = 'text/javascript'
    if (script.readyState) {
        //IE浏览器
        script.onreadystatechange = function () {
            if (script.readyState == 'loaded' || script.readyState == 'complete') {
                script.onreadystatechange = null;
                callback()
            }
        };
    } else {
        //标准浏览器
        script.onload = function () {
            callback()
        };
    }
    script.src = url
    document.getElementsByTagName('head')[0].appendChild(script)
}

实现函数,接受参数并渲染出对应的地图

使用switch case分支语句对需要进行渲染选项进行基础渲染,注意哦,这里只是基础渲染,没有附加功能的(在我的项目中,我这里是需要后台数据配合的,为了公用,我把他抽取处理了一下,只会加载基础的地图部分,最多就是消除一下这个水印了,我也不知道这个犯法不,哈哈,不过一个应用中就算用到了这个地图,光溜溜的显示一个百度和here的logo确实看着怪怪的。对应的密钥,手令。大家就自己去申请了,这个简单哦。)

//根据参数加载对应地图(并去掉对应的水印)
//MapEngine -> String 地图引擎
function LoadMap(MapEngine) {
    switch (MapEngine) {
        //Here地图
        case 'HereMap':
            loadScript('http://js.api.here.com/v3/3.0/mapsjs-core.js', function () {
                loadScript('http://js.api.here.com/v3/3.0/mapsjs-service.js', function () {
                    removeMapArea()
                    console.log('Here地图加载OK');
                    let platform = new H.service.Platform({
                        'app_id': 'Here地图开发者ID',
                        'app_code': 'Here地图开发者手令'
                    });
                    let defaultLayers = platform.createDefaultLayers()
                    new H.Map(
                        MapEngineContant,
                        defaultLayers.normal.map,
                        {
                            zoom: 14,
                            center: { lat: 52.5, lng: 13.4 }
                        });
                    //去除相关水印
                    let HereMaker = document.querySelector('#MapEngineContant>div:last-child>div:last-child')
                    HereMaker.parentNode.removeChild(HereMaker)

                })
            })
            break;
        //百度地图
        case 'BaiduMap':
            loadScript('http://api.map.baidu.com/api?v=2.0&ak=百度地图开发者密钥', function () {
                loadScript('http://api.map.baidu.com/getscript?v=2.0&ak=百度地图开发者密钥&services=&t=20190102133327', function () {
                    removeMapArea()
                    console.log('百度地图加载OK');
                    // 创建地图实例
                    let map = new BMap.Map('MapEngineContant')
                    // 创建点坐标 
                    let point = new BMap.Point(116.404, 39.915)
                    // 初始化地图,设置中心点坐标和地图级别 
                    map.centerAndZoom(point, 22)
                    //开启鼠标滚轮缩放
                    map.enableScrollWheelZoom(true)
                    //去除对应水印 
                    removeMaker('BMap_cpyCtrl')
                    removeMaker('anchorBL')
                })
            })
            break;
		//如果需要添加别的地图引擎,请在这里书写case 语句
        default:
            console.log('error')
            break;
    }
}

百度开发者密钥申请:

http://lbsyun.baidu.com/index.php?title=jspopular/guide/getkey

Here地图手令申请:

https://openlocation.here.com/contact

通过类名清除水印函数,百度腾讯可用

不要小看这个函数,这里有好两个大坑。
1.document.getElementsByClassName这个获取的是一个Htmlconnection对象,而且是动态的,无法直接通过js对其操作
2.由于script和dom树同步执行,执行这段代码时,ele并未正确加载,此时无法对这个Htmlconnection对象操作,只能等待他正确的加载了dom树之后再去操作这个水印(我知道这段代码很野鸡,不过暂时没有想到好的方法去实现他,而且30ms也是人眼无法察觉的误差,我就默许了)

//通过类名修改隐藏元素 -> 变相去除水印
//className -> String 类名
function removeMaker(className) {
    let ele = document.getElementsByClassName(className)
    setTimeout(() => {
        for (item of ele) {
            item.parentNode.removeChild(item)
        }
    }, 30)
}

清除地图容器内容,避免地图之间的重复渲染

我在地图之间的切换过程中发现,百度地图的一些附加的东西在Here地图加载的时候并没有完全清除掉,索性一不做二不休,每次渲染之前把之前渲染的地图直接干掉(友情提示:此操作增加了dom操作,降低了性能,可以忽略)

//清空地图容器,避免重复渲染
function removeMapArea() {

    let MapEngineContant = document.getElementById('MapEngineContant')
    let childs = MapEngineContant.childNodes
    for (let i = childs.length - 1; i >= 0; i--) {
        MapEngineContant.removeChild(childs[i]);
    }
}

地图之间的相互切换

在只有以上函数的情况下,demo也能正常跑起来,不过需要手动设置默认的地图类型。这个函数主要就是切换地图使用。

//地图切换函数
function MapChange() {
    let MapSelected = document.getElementById('MapEngineList')
    //获取当前选择项的值
    let selectedMap = MapSelected.options[MapSelected.selectedIndex].value
    //获取对应的地图引擎
    for (Mapitem in MapEngineList) {
        MapEngineList[Mapitem] === selectedMap ? LoadMap(Mapitem) : null
    }
}

写完啦,完整js部分代码如下

这个demo看着很简单,但是很符合一些对应的业务逻辑使用场景,而且,差不多的变量我都抽取出来了,在模块化开发中可以很快的和别的模块之间进行对接。由于只是功能的实现,并没有多少样式的调整,大家将就看一下,多多包涵。

/*
*   地图引擎:
*       需求:拿到 ->地图引擎名<-,去匹配对应的引擎,并加载初始化地图
*            返回 ->对应的地图引擎<- 提供给MapHelper.js用于初始化
*
*/

//默认地图引擎,由系统设置提供
//具体数据猜测应该放在redux仓库中 通过模块导入
const defaultMapEngine = 'BaiduMap'

//可支持的地图引擎
const MapEngineList = {
    BaiduMap: '百度地图',
    HereMap: 'Here地图',
    TencentMap: '腾讯地图',
    GaodeMap: '高德地图',
    BinyingMap: '必应地图'
}

//起始坐标点,外部参数,(坐标定位暂时不处理,需要根据算法重新换算)
const defaulePoint = {
    x: 0,
    y: 0
}

//显示可选地图列表
RenderMapList(MapEngineList)

//加载默认地图
LoadMap(defaultMapEngine)

//绑定地图切换事件
document.getElementById('MapEngineList').onchange = MapChange

//地图切换函数
function MapChange() {
    let MapSelected = document.getElementById('MapEngineList')
    //获取当前选择项的值
    let selectedMap = MapSelected.options[MapSelected.selectedIndex].value
    //获取对应的地图引擎
    for (Mapitem in MapEngineList) {
        MapEngineList[Mapitem] === selectedMap ? LoadMap(Mapitem) : null
    }
}

//根据 MapEngineList 来渲染页面的地图选择选项
//MapList -> Object 可支持的地图引擎
function RenderMapList(MapList) {
    //获取地图显示列表
    let MapShowList = document.getElementById('MapEngineList')

    //渲染地图显示列表
    for (Mapitem in MapList) {
        let MapListNode = document.createElement('option')
        let text = document.createTextNode(`${MapList[Mapitem]}`)
        MapListNode.appendChild(text);
        MapShowList.appendChild(MapListNode)
        //默认显示 默认加载项
        if (`${Mapitem}` === defaultMapEngine)
            MapListNode.setAttribute('selected', 'selected')
    }
}

//根据参数加载对应地图(并去掉对应的水印)
//MapEngine -> String 地图引擎
function LoadMap(MapEngine) {
    switch (MapEngine) {
        //Here地图
        case 'HereMap':
            loadScript('http://js.api.here.com/v3/3.0/mapsjs-core.js', function () {
                loadScript('http://js.api.here.com/v3/3.0/mapsjs-service.js', function () {
                    removeMapArea()
                    console.log('Here地图加载OK');
                    let platform = new H.service.Platform({
                        'app_id': '******************',
                        'app_code': '******************'
                    });
                    let defaultLayers = platform.createDefaultLayers()
                    new H.Map(
                        MapEngineContant,
                        defaultLayers.normal.map,
                        {
                            zoom: 14,
                            center: { lat: 52.5, lng: 13.4 }
                        });
                    //去除相关水印
                    let HereMaker = document.querySelector('#MapEngineContant>div:last-child>div:last-child')
                    HereMaker.parentNode.removeChild(HereMaker)
                })
            })
            break;
        //百度地图
        case 'BaiduMap':
            loadScript('http://api.map.baidu.com/api?v=2.0&ak=******************', function () {
                loadScript('http://api.map.baidu.com/getscript?v=2.0&ak=******************&services=&t=20190102133327', function () {
                    removeMapArea()
                    console.log('百度地图加载OK');
                    // 创建地图实例
                    let map = new BMap.Map('MapEngineContant')
                    // 创建点坐标 
                    let point = new BMap.Point(116.404, 39.915)
                    // 初始化地图,设置中心点坐标和地图级别 
                    map.centerAndZoom(point, 22)
                    //开启鼠标滚轮缩放
                    map.enableScrollWheelZoom(true)
                    //去除对应水印 
                    removeMaker('BMap_cpyCtrl')
                    removeMaker('anchorBL')
                })
            })
            break;
        default:
            console.log('error')
            break;
    }
}

//动态加载script
//url -> String 资源加载地址
function loadScript(url, callback) {
    let script = document.createElement('script')
    script.type = 'text/javascript'
    if (script.readyState) {
        //IE浏览器
        script.onreadystatechange = function () {
            if (script.readyState == 'loaded' || script.readyState == 'complete') {
                script.onreadystatechange = null;
                callback()
            }
        };
    } else {
        //标准浏览器
        script.onload = function () {
            callback()
        };
    }
    script.src = url
    document.getElementsByTagName('head')[0].appendChild(script)
}

//通过类名修改隐藏元素 -> 变相去除水印
//className -> String 类名
function removeMaker(className) {
    let ele = document.getElementsByClassName(className)
    setTimeout(() => {
        for (item of ele) {
            item.parentNode.removeChild(item)
        }
    }, 50)
}
//清空地图容器,避免重复渲染
function removeMapArea() {
    let MapEngineContant = document.getElementById('MapEngineContant')
    let childs = MapEngineContant.childNodes
    for (let i = childs.length - 1; i >= 0; i--) {
        MapEngineContant.removeChild(childs[i]);
    }
}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值