首页 > 其他分享 >vue 将百度地图或者高德地图组件化

vue 将百度地图或者高德地图组件化

时间:2023-12-29 10:12:11浏览次数:34  
标签:map vue const 地图 value BMap props position 高德

一、前言

百度地图已经有了 react 相关的组件库,本人用的百度地图 v3.0 和 vue3
我仅仅是抛砖引玉,百度地图 webgl、高德地图都是一样的,因为底层都是通过 js 控制地图
如果用组件的方式开发,比如我将 BMap.Marker 作为一个组件,我暴露一个参数position,其目的是控制 BMap.Marker 位置
那么只要我修改positionBMap.Marker 在地图中的就会自动改变
和用js相比,无疑会减少很多步骤,方便省心(同理,其实用vue开发,比用原生html、js 更快!),以下是我自己在使用的代码截图

二、实现

这里我主要讲讲 BMap、BMap.Marker、自定义 Overlay
其实其他的覆盖物,相信只要你看了 BMap.Marker,就知道如何生成了,原理一样的
因为我只写了我自己需要的,有很多没用到的操作,我懒就没写了
核心其实就一个,如何给子组件注入已经生成的地图对象,就没了!

2.1 BMap实现

注意控制地图的生命周期,以及如何暴露 map 元素给子组件

<template>
    <div class="c-BMap" ref="targetDom"></div>
    <!-- 等地图挂载后,再加载子组件 -->
    <slot v-if="isMapMount"></slot>
</template>

<script lang="ts" setup>
import { onMounted, onUnmounted, provide, ref, shallowRef, watch } from 'vue';

const props = withDefaults(defineProps<{
    zoom?: number,
    center?: BMap.Point | BMap.Point[],
}>(), {
    zoom: 15,
    center: () => new BMap.Point(114.13581398675089, 22.61457979837459)
});
const isMapMount = ref(false);
const emits = defineEmits(['onMapMount']);

let map = shallowRef<BMap.Map | null>(null);
const targetDom = ref<HTMLDivElement>();

// 暴露地图给子组件
provide('map', map);

watch(() => props.zoom, (zoom) => {
    if (map.value) {
        map.value.setZoom(zoom);
    }
})

watch(() => props.center, (position) => {
    if (map.value) {
        if (Array.isArray(position)) {
            map.value.setViewport(position);
        } else {
            map.value.setViewport({
                center: position,
                zoom: props.zoom
            })
        }
    }
})
// 初始化地图
onMounted(() => {
    if (targetDom.value) {
        const _map = new BMap.Map(targetDom.value);
        if (Array.isArray(props.center)) {
            _map.setViewport(props.center);
        } else {
            _map.setViewport({
                center: props.center,
                zoom: props.zoom
            })
        }
        _map.enableScrollWheelZoom();
        map.value = _map;
        isMapMount.value = true;
        emits('onMapMount', _map);
    }
})
// 地图销毁
onUnmounted(()=>{
    if(map.value){
        map.value.destroy();
    }
})
</script>

<style lang="less">
.c-BMap {
    height: 100%;
    width: 100%;
}

// 百度地图logo
.anchorBL {
    display: none;
}
</style>

2.2 BMap.Marker 实现

<template></template>

<script lang="ts" setup>
import { watch } from 'vue';
import useBMapOverlay from "./composition/useBMapOverlay";

const props = withDefaults(defineProps<{
    position: BMap.Point | null,
    icon?: BMap.Icon,
    rotation?: number
    onClick?: (e: any) => void,
}>(), {

})

const target = new BMap.Marker(props.position || new BMap.Point(0, 0));
if (props.icon) {
    target.setIcon(props.icon);
}
target.disableMassClear();
if (props.onClick) {
    target.addEventListener('click', props.onClick)
}

useBMapOverlay(target);

watch(() => props.position, (position) => {
    if (target) {
        target.setPosition(position || new BMap.Point(0, 0));
    }
})
watch(() => props.rotation, (rotation) => {
    target.setRotation(rotation || 0);
})
</script>

上面有一个函数useBMapOverlay(),因为后续很多BMap的覆盖物,挂载在 BMap上的步骤相同,我给提取出来了,内容如下:

import { inject, onBeforeUnmount, onMounted, type ShallowRef } from "vue";

const useBMapOverlay = (overlay: BMap.Overlay) => {
    // 获取父组件提供的地图,注意必须先生成地图
    const map = inject('map') as ShallowRef<BMap.Map | null>;
    // 将覆盖物挂载在地图上
    onMounted(() => {
        if (map.value) {
            map.value.addOverlay(overlay);
        }
    });
    // 销毁覆盖物
    onBeforeUnmount(() => {
        if (map.value) {
            map.value.removeOverlay(overlay);
        }
    })
}

export default useBMapOverlay;

2.3 自定义 Ovarlay

看过百度地图Api的都知道,覆盖物都继承自 BMap.Overlay
只要实现了BMap.Overlay,就可以创建自己的覆盖物
我将 vue 生成的 dom 传递给自定义 Ovarlay,那么这个覆盖物就有了响应式的特点
当然你弄个什么图表 Echart 什么的,一样都可以

<template>
  <div ref="targetDom">
    <slot></slot>
  </div>
</template>
  
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref, watch, type ShallowRef, inject } from 'vue';
import MyOverlay from "./lib/MyOverlay";

const props = withDefaults(defineProps<{
  position: BMap.Point | null,
  show?: boolean,
  offset?: BMap.Size
}>(), {
  position: () => new BMap.Point(0, 0),
  show: true,
})

let overlay: MyOverlay | null = null;
const targetDom = ref<HTMLDivElement>();
const map = inject('map') as ShallowRef<BMap.Map | null>;

const init = () => {
  console.log("创建一次")
  if (targetDom.value && map.value && !overlay) {
    overlay = new MyOverlay(targetDom.value, props.position || new BMap.Point(0, 0));
    map.value.addOverlay(overlay);
    if (props.offset) {
      overlay.setOffset(props.offset);
    }
  }
}

watch(() => props.position, (position) => {
  if (overlay) {
    overlay.setPosition(position || new BMap.Point(0, 0));
  }
})

watch(() => props.show, (show) => {
  if (overlay) {
    if (show) {
      overlay.show();
    } else {
      overlay.hide();
    }
  }
})

onMounted(init);

onBeforeUnmount(() => {
  if (overlay) {
    overlay.destroy();
  }
})
</script>
export default class MyOverlay extends BMap.Overlay {
    private map: any;
    private dom: HTMLDivElement;
    private position: BMap.Point;
    enableMassClear = false;

    constructor(dom: HTMLDivElement, position: BMap.Point) {
        super();
        dom.style.position = 'absolute';
        this.dom = dom;
        this.position = position;
    }

    // 使用 BMap.addOverlay 的时候会自动触发本函数
    initialize(map: BMap.Map) {
        const pane = map.getPanes().markerPane;
        if (pane) {
            pane.appendChild(this.dom);
        }
        this.map = map;
        return this.dom;
    }

    private offset: BMap.Size = new BMap.Size(0, 0)
    setOffset(offset: BMap.Size) {
        this.offset = offset;
        this.draw();
    }

    hide(): void {
        this.dom.style.display = 'none';
    }

    show(): void {
        this.dom.style.display = 'block';
    }

    // 百度地图需要重新绘制的时候触发,比如zoom、地图移动就会触发
    draw() {
        const pixel = this.map.pointToOverlayPixel(this.position);
        const { height, width } = this.offset;
        this.dom.style.left = pixel.x + width + "px";
        this.dom.style.top = pixel.y + height + "px";
    }

    setPosition(position: BMap.Point) {
        this.position = position;
        this.draw();
    }

    destroy() {
        if (this.map) {
            this.map.removeOverlay(this);
        }
    }
}

三、优缺点总结

优点就是大大减少代码
缺点,该系列的组件,本质上套了一层vue,比直接操作js代码,性能上是有问题的,但是问题不是很大,可以牺牲

注意动画,我有这样一个场景,60帧的帧率,Maker 的移动很丝滑,但是自定义 Overlay的移动有点不丝滑
猜测是自定义 Overlay,移动位置是百度地图操作,修改内容是vue操作,这两个动作不同步,所以....

标签:map,vue,const,地图,value,BMap,props,position,高德
From: https://www.cnblogs.com/panshaojun/p/17934138.html

相关文章

  • vue3+ts打开echarts的正确方式
    实例项目使用vite5+vue3+ts,项目地址vite-vue3-charts,预览地址https://weizwz.com/vite-vue3-charts准备工作1.注册为百度地图开发者官网地址,然后在应用管理->我的应用里,创建应用,创建好后复制AK2.在根目录的index.html里引入百度地图<head><metacharse......
  • vue中get和post请求
    vue中和后台交互,首先要引用vue-resource.jsvue-resource.js是专门和后台进行交互<!--==============引入vue-resource插件=================--><scriptsrc="../js/vueJs/vue-resource.js"></script>vue中get请求functiongetRequest(url,params){returnnewPromis......
  • vue3+lottie实现动画
    1、安装lottie-webnpmilottie-web2、使用在线json文件<template><divclass="box"><divid="lottie_box"style="width:800px;height:800px;margin-left:1000px;background-color:pink"></div><butt......
  • Vue中$router.push()路由切换、如何传参和获取参数 和获取不到$router.push 参数问题
    路由的两种传参方式:  一:声明式<router-link:to="{path:'/login'}">Home</router-link> 二:编程式$router.push(...) //该方法的参数可以是一个字符串路径,或者一个描述地址的对象。不带参数写法://字符串(对应填写上面的path) this.$router.push('/login') //对......
  • HighCharts 地图画航线,以及在城市点画圈
    需求:生成一个世界地图,在城市点处画一个响应式的圈,及在城市点间画一条指示性的航线分析:生成一幅世界地图需导入相关地图js文件与获取json文件,在城市点画一个响应式的圈和一条指示性的航线,需要生成序列,并指定类型,航线类型(flowmap),响应式的圆圈用render进行画圆圈,具体请看下图解决:源......
  • 封装一个表情包组件(支持自定义表情图片)(基于vue3语法)
    效果图文件图直接贴代码emotion.vue<template><divclass="emotion-containerbeauty-scroll-livechat"><divclass="emotion-btn"@click="toggleEmotionShow"><spanclass="iconfonticon-biaoqing1&quo......
  • vuejs+echarts实现时间轴
    1、效果图2、具体需求描述①可以设置时间轴起始值;②时间轴可以缩放、左右拖动;③鼠标移入时间轴显示当前刻度信息;④点击时间轴时添加蓝色图标,鼠标移入图标显示此时图标信息且隐藏刻度信息,按住图标可以拖动图标;3、实现①结构代码<divid="timeAxisEchart"style="width:10......
  • vuejs+echarts实现x轴为时间轴且数据区域可缩放
    1、效果图2、具体功能描述①选择的时间为时间轴的开始和结束时间;②鼠标可以左右拖动x轴时间轴;③鼠标放入图表中可以缩放数据,x轴会相应的更改当前坐标轴间隔值,最小间隔值为1分钟,最大间隔值为1年,且在缩放时可以获取到数据窗口范围的起始数值;④点击y轴名称会对相应数据显示隐......
  • (数据科学学习手札156)地图可视化神器kepler.gl 3.0版本发布
    本文已收录至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes1简介大家好我是费老师,地图可视化神器kepler.gl终于带来了其3.0大版本的更新......
  • vue前端node内存溢出问题解决
    前端项目运行时,如果经常运行慢,崩溃停止服务,报如下错误:FATALERROR:CALL_AND_RETRY_LASTAllocationfailed-JavaScriptheapoutofmemory(JavaScript堆内存不足) 原因:因为在Node中,通过JavaScript使用内存时只能使用部分内存(64位系统:1.4GB,32位系统:0.7GB),这个时候,如......