uni-app 使用map标签实现自定义点标记,点聚合

一、需求&效果图
需求:

1、实现地图显示标记点markers与聚合点,点击标记点或聚合点,页面下弹出详情弹框,
      详情弹窗支持左右滑动,支持拨打电话与复制文本
2、视野展示所有经纬度
3、实现按钮功能:刷新地图

重点:需要编译在小程序开发工具中使用 “ 真机预览 ” 才可展示如图所示效果

效果图      


图中绿色1点标记为自定义点标记,聚合点则使用的默认的聚合样式,目前存在设置自定义点聚合簇之后,markerClusterClick 事件在ios上触发不了,至今官方还没给出解答,我就用的默认样式,当然代码中也写了我自定义的样式,只是enableDefaultStyle设置了true,当把该属性改为false的时候聚合点就显示了自定义的样式了,废话不多说直接上代码。。。

注意:点击聚合点,拿到聚合点下用户的数据,ios跟安卓系统返回的数据类型不一致,因此我都转成了number型

二、具体实现

1.使用地图组件

<view>
    <map id="alarm_map" class="map" :latitude="latitude" :longitude="longitude" :show-location="true" scale="12"
      :markers="allMarkers" @markertap="markerTap" @labeltap='markerTap'></map>
    <view class="tab">
      <view class="tabs" :class="status==1 ? 'tab1' : 'tab2'" @click="tabAction(1)">待接单</view>
      <view class="tabs" :class="status==2 ? 'tab1' : 'tab2'" @click="tabAction(2)">待服务</view>
    </view>
    <view class="uButton" v-if="isShow">
      <swiper class="scroll-view" :indicator-dots="false" :autoplay="false" :circular="true" :current="current"
        @change="swiperChange">
        <swiper-item class="scroll-item" :style="{width: windowWidth + 'px' }" v-for="(item,index) in infoList"
          :key="index">
          <p class="username">{{item.username}}</p>
          <p @click="confirmNumber(item.phone)">联系电话:<text class="decoration">{{item.phone}}</text></p>
          <p v-if="item.backup_phone.length>0">备用电话:<text class="decoration" v-for="(items,indexs) in item.backup_phone"
              :key="indexs" @click="confirmNumber(items)">{{items}}{{item.backup_phone.length!==indexs+1?'、':''}}</text>
          </p>
          <p>区市街道:{{item.county.name}}{{item.street.name}}</p>
          <view @click="copyText(item.detail_address)">详细地址:<text class="decoration">{{item.detail_address}}</text>
          </view>
          <p class="page">{{index+1}} / {{infoList.length}}</p>
        </swiper-item>
      </swiper>
    </view>
    <view class="refresh">
      <view @click="getallStations()">
        <image style="width: 60rpx; height: 60rpx" src="/static/refreshs.png" />
        <view>刷新</view>
      </view>
    </view>
  </view>

2、逻辑 

data() {
      return {
        latitude: 30.6635, // 纬度
        longitude: 104.07243, // 经度
        allMarkers: [],
        status: 2,
        allList: [],
        infoList: [],
        windowWidth: 0,
        current: 0,
        isShow: false
      }
    },
    onReady() {
      uni.getSystemInfo({
        success: (res) => {
          this.windowWidth = res.windowWidth;
        },
      });
      // 1.页面准备好后,获取到map组件的执行上下文。注意:这里是取的map的id属性
      this.mapContext = uni.createMapContext("alarm_map", this);
      // 2.请求数据
      this.getallStations();
    },
  methods: {
      swiperChange(e) {
        this.current = e.detail.current;
      },
     
      //自动拨打电话
      confirmNumber(number) {
        wx.makePhoneCall({
          phoneNumber: number,
        });
      },
      //复制功能
      copyText(value) {
        uni.setClipboardData({
          data: value,
          success() {
            uni.showToast({
              title: '已复制到剪贴板',
              icon: 'none',
            });
          },
        });
      },
      //切换状态  
      tabAction(e) {
        this.status = e;
        this.getallStations();
      },
      //点击点标记
      markerTap(e) {
        this.getInfo(e.detail.markerId)
      },
      //获取点标记或者聚合下的参保人信息
      getInfo(data) {
        this.isShow = true
        this.infoList = this.allList?.filter(item => item.id == data)
      },
      async getallStations() {
        this.isShow = false;
        uni.showLoading();
        const {
          data: res
        } = await getMapElecList({
          status: this.status
        });
        uni.hideLoading();
        if (!Array.isArray(res?.list) || res?.list?.length <= 0) {
          this.infoList = [];
          this.allList = [];
          this.allMarkers = [];
          return;
        };
        this.allList = res.list?.map(item => {
          return {
            ...item,
            latitude: +item.latitude,
            longitude: +item.longitude,
          }
        })
        // 	缩放视野展示所有经纬度
        this.mapContext.includePoints({
          points: this.allList,
          padding: [10, 10, 10, 10]
        })
        // 拿到请求数据后,把数据传给点聚合功能;
        this.setMarkersAndCluster(this.allList) // 3、调用聚合功能
      },
      // 聚合功能
      setMarkersAndCluster(markerList) {
        // 1.组装数据之后,并赋值给地图上的marker
        this.allMarkers = Array.from(markerList).map((item, i) => {
          return {
            ...item,
            width: 1,
            height: 1,
            iconPath: 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/marker_blue.png',
            joinCluster: true, // 这个属性很重要,必须要,否则将无法开启点聚合功能
            label: {
              content: '1',
              fontSize: 16,
              width: 40,
              height: 40,
              color: '#ffffff',
              bgColor: '#0DC7BE',
              borderRadius: 25,
              textAlign: 'center',
              anchorX: 0,
              anchorY: -20,
            }
          }
        });
        // 2.初始化点聚合的配置,未调用时采用默认配置
        this.mapContext.initMarkerCluster({
          enableDefaultStyle: true, // 是否启用默认的聚合样式(是否用自定义图标)
          zoomOnClick: true,
          gridSize: 60,
          complete(res) {
            console.log('initMarkerCluster', res)
          }
        });
        // 3.发生聚合时,给聚合点设置marker标签
        this.mapContext.on('markerClusterCreate', res => {
          const clusters = res.clusters // 新产生的聚合簇
          const zhou = clusters.map(item => {
            const {
              center, // 聚合点的经纬度数组
              clusterId, // 聚合簇id
              markerIds // 已经聚合了的标记点id数组
            } = item
            return {
              ...center,
              width: 10,
              height: 10,
              clusterId, // 必须有
              iconPath: 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/marker_blue.png',
              // iconPath: '',
              // borderRadius: 8,
              joinCluster: true,
              label: { // 定制聚合点样式
                content: markerIds.length + '',
                fontSize: 16,
                width: 50,
                height: 50,
                color: '#ffffff',
                bgColor: '#0DC7BE',
                borderRadius: 25,
                textAlign: 'center',
                anchorX: 0,
                anchorY: -20,
              }
            }
          })
          // 4. 添加聚合簇标签
          this.mapContext.addMarkers({
            markers: zhou,
            clear: false, //是否先清空地图上所有的marker
          })
        })
        // 点击聚合点,拿到聚合点下老人的数据
        this.mapContext.on('markerClusterClick', (res) => {
          let numArray = res?.cluster?.markerIds.map(Number);//安卓跟ios反的数据类型不一样,因为要处理下
          this.isShow = true
          this.infoList = this.allList.filter(item => numArray.includes(item.id))
        })
      },
    },
  }

3.页面样式

<style>
  .map {
    width: 100%;
    height: 100vh;
  }

  .tab {
    width: 400rpx;
    height: 60rpx;
    background-color: #fff;
    position: fixed;
    top: 100rpx;
    left: 50%;
    transform: translate(-50%, -50%);
    border: 4rpx solid #0DC7BE;
    border-radius: 8rpx;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .tabs {
    width: 50%;
    height: 90%;
    display: flex;
    justify-content: center;
    align-items: center;
    margin: 0 4rpx;
    border-radius: 4rpx;
  }

  .tab1 {
    background-color: #0DC7BE;
    color: #fff;
  }

  .tab2 {
    background-color: #fff;
    color: #0DC7BE;
  }

  .uButton {
    width: 100%;
    position: fixed;
    bottom: 0;
    right: 0;
    padding-bottom: calc(env(safe-area-inset-bottom)+40rpx);
    background-color: #fff;
    font-size: 30rpx;
    border-radius: 4rpx;
  }

  .scroll-view {
    padding: 10rpx 30rpx 10rpx;
    height: 380rpx;
  }

  .scroll-item {
    display: inline-block;
    box-sizing: border-box;
    vertical-align: top;
    position: relative;
  }

  .refresh {
    width: 80rpx;
    position: fixed;
    bottom: 60%;
    right: 0;
    background-color: #fff;
    text-align: center;
    font-size: 24rpx;
    padding: 20rpx 0;
    border-radius: 4rpx;
  }

  .username {
    font-size: 36rpx;
    font-weight: 600;
  }

  .decoration {
    text-decoration: underline;
  }

  .page {
    position: absolute;
    bottom: 0%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
</style>

好的,关于你的问题,我来尝试回答一下。 在uni-app中,我们可以通过自定义指令来实现全局图片dpr适配。具体步骤如下: 1. 在项目的根目录下,创建一个`/common/directives`目录。 2. 在该目录下,新建一个`index.js`文件,用于存放自定义指令。 3. 在`index.js`文件中,定义一个`img`指令,用于处理所有`<img>`标签。 4. 在`img`指令中,通过`uni.getSystemInfoSync()`方法获取设备的dpr值。 5. 根据dpr值,设置`<img>`标签的`src`属性,以实现图片dpr适配。 下面是示例代码: ```javascript // /common/directives/index.js export default { img: { // 指令定义函数 bind: function(el, binding) { // 获取设备的dpr值 const dpr = uni.getSystemInfoSync().pixelRatio; // 根据dpr值,设置图片的src属性 const imgPath = binding.value; el.setAttribute('src', imgPath + '@' + dpr + 'x'); } } } ``` 使用自定义指令时,需要在`main.js`文件中全局注册自定义指令: ```javascript // /main.js import directives from './common/directives'; // 注册自定义指令 Object.keys(directives).forEach(key => { Vue.directive(key, directives[key]); }); ``` 完成上述步骤后,即可在项目中使用`v-img`指令实现全局图片dpr适配: ```html <template> <img v-img="'/static/img/test.png'" /> </template> ``` 这样,`<img>`标签的`src`属性就会根据设备的dpr值自动添加相应的后缀,以达到图片dpr适配的效果。 希望我的回答能够帮到你,如有疑问请随时提出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值