Echarts 实现中国地图并轮播指定的地区?

前言

在最近遇到一个新的项目需求,在我们的首页区一个模块展示一个中国地图,并特别标注指定的地区进行轮播展示~

现在给大家分享一下,我的搬砖历程...

此次需求使用 uni-app H5端实现

Part.1  效果展示

Part.2  代码构思

首先接到这个需求,我的脑海中就出现了 Echarts,这个大家肯定都很熟悉,因为这个我们做图表方面的需求应用得很多。

好的,废话不多说,我们开始整理思路。

1. 确认引用 Echarts

2. 使用 Echarts 实现中国地图

3. 高亮展示指定的区域

4. 高亮区域数据实现轮播展示

Part.3  代码编写

1. 引入 Echarts 库,这个不用多说,引入的方式有很多,我这里是通过 CDN 引入的。

    特别需要强调的是版本问题,这个是我遇到的一个很大的问题,版本不同导致API的不同,从而导致在构思的 第3步(高亮展示指定的区域)和 第四步(高亮区域数据实现轮播展示)无法实现

    我这里使用的是 @4.1.0

    我的引用:

<script src="https://cdn.bootcdn.net/ajax/libs/echarts/4.1.0/echarts.min.js"></script>

2. 实现中国地图,这里需要先引用两个文件后续会用到

     chinaData.json  地址:https://gitee.com/langxiyu/china-data.json/blob/master/chinaData.json     

     china.js  地址: https://gitee.com/langxiyu/china-data.json/blob/master/china.js

第3步,第4步将在源码中展示 

Part.4  源码

<template> 
     <view class="site-content">
          <image class="img" src="/static/index/map-bg.png" mode="aspectFill"></image>
          
         <view class="content-header">
              <view class="cur-info">
                  <image class="info-bg" src="/static/index/local-info-bg.png"></image>
                  <view class="info">
                      <view class="left">
                          <text class="city-name">{{ curSiteInfo.venueName }}馆</text>
                          <text class="city-desc">{{ curSiteInfo.venueText }}</text>
                      </view>
                      <view class="right">
                          <image class="logo" :src="curSiteInfo.venueHeadUrl"></image>
                      </view>
                  </view>
              </view>
          </view>
          
          <view class="content-map">
              <localMap @getCurSiteInfo="getCurSiteInfo"></localMap>
          </view>
     </view>

</template>

<script>
import localMap from './component/localMap/localMap.vue'
export default {
    components: {
        localMap
    },
    data() {
        return {
            // 当前信息
            curSiteInfo: {}

        }
    },
    methods: {
        // 展示当前信息
        getCurSiteInfo(e) {
            this.curSiteInfo = e
        }
    }
}
</script>

<style lang="scss" scoped>
.site-content {
     100%;
    position: relative;
    
    .img {
         100%;
        height: 706rpx;
    }
    
    .content-header {
         690rpx;
        position: absolute;
        top: -2rpx;
        left: 0;
        text-align: center;
        z-index: 1;
        
        .cur-info {
            min- 276rpx;
            height: 136rpx;
            padding-top: 32rpx;
            position: relative;
            display: inline-block;
            
            .info-bg {
                 100%;
                height: 136rpx;
                position: absolute;
                top: 0;
                left: 0;
            }
        
            .info {
                padding-left: 30rpx;
                padding-right: 30rpx;
                position: relative;
                display: flex;
                align-items: center;
                z-index: 1;
                
                .left {
                    display: flex;
                    flex-direction: column;
                    text-align: left;
                    
                    .city-name {
                        font-size: 44rpx;
                        font-family: YouSheBiaoTiHei;
                        color: #8A4424;
                        line-height: 58rpx;
                        background: linear-gradient(180deg, #9C4A23 0%, #713F29 100%);
                        -webkit-background-clip: text;
                        -webkit-text-fill-color: transparent;
                    }
                    
                    .city-desc {
                        font-size: 26rpx;
                        font-weight: 400;
                        color: #8A4424;
                        line-height: 40rpx;
                    }
                }
            
                .right {
                     80rpx;
                    height: 80rpx;
                    margin-left: 20rpx;
                    
                    .logo {
                         100%;
                        height: 100%;
                        border-radius: 10rpx;
                    }
                }
            }
        }
    }
    
    .content-map {
         690rpx;
        position: absolute;
        top: 80rpx;
        left: 0;
    }
    
    .content-cur-site {
        min- 262rpx;
        padding: 18rpx 16rpx;
        background: linear-gradient(125deg, #FFFFFF 0%, #FFFFFF 100%);
        box-shadow: 0 0 18rpx 0 rgba(237, 151, 0, 0.6) inset;
        border-radius: 20rpx;
        display: flex;
        justify-content: space-between;
        position: absolute;
        z-index: 1;
        transition: all 0.5s;
        .cur-site-left {
            display: flex;
            flex-direction: column;
            .left-title {
                margin-top: 4rpx;
                font-family: YouSheBiaoTiHei;
                font-size: 34rpx;
                white-space: nowrap;
                color: rgba(179, 135, 60, 1);
                line-height: 36rpx;
            }
            .left-desc {
                max- 140rpx;
                margin-top: 4rpx;
                font-size: 24rpx;
                font-weight: 400;
                color: #CBA86A;
                line-height: 36rpx;
            }
        }
        .cur-site-right {
             80rpx;
            height: 80rpx;
            .right-img {
                 100%;
                height: 100%;
            }
        }
    }
    
    .content-site-position {
        position: absolute;
         .site-label {
             24rpx;
            height: 36rpx;
            border-radius: 50% 50% 50% 50% / 30% 30% 70% 70%;
            background: linear-gradient(39deg, #C9D94F 0%, #899922 100%);
            position: relative;
            transition: all 2s;
            .label-white {
                 12rpx;
                height: 12rpx;
                position: absolute;
                top: 6rpx;
                right: 0;
                left: 0;
                margin: auto;
                border-radius: 50%;
                background-color: #FFF;
            }
            .header-sign{
                 12rpx;
                height: 12rpx;
                position: absolute;
                top: 26rpx;
                left: 6rpx;
                border-radius: 50%
            }
            &.active {
                transform: scale(1.5);
                background: linear-gradient(39deg, rgba(250, 217, 97, 1) 0%, rgba(247, 107, 28, 1) 100%);
                .header-sign {
                    animation: waterWave 1s ease-out;
                    animation-iteration-count: infinite;
                }
            }
            @keyframes waterWave {
                form   {
                    transform: scale(1);
                    background: rgba(248, 140, 49, 0.34);
                }
                to {
                    transform: scale(2);
                    background: rgba(248, 140, 49, 0.24);
                }
                50%  {
                    transform: scale(3);
                    background: rgba(248, 140, 49, 0.14);
                }
                75%  {
                    transform: scale(4);
                    background: rgba(248, 140, 49, 0.04);
                }
                100% {
                    transform: scale(5);
                    background: rgba(248, 140, 49, 0);
                }
            }
        }
    }
}
</style>
index.vue
  1 <template>
  2     <view class="qiun-charts">    
  3         <view id="mapChart" ref="mapChart"></view>
  4         
  5         <!-- 九段线图片 -->
  6         <image class="map-section-9" 
  7                src="/static/index/section-9.png"></image>
  8         
  9         <!-- 推荐场馆 -->
 10         <view class="site-recommend">
 11             <view v-for="(item, index) in rankingList"
 12                   :key="index"  
 13                   class="item">
 14                 <text class="num">No.{{ index + 1 }}</text>
 15                 <text class="name">{{ item.venueName }}</text>
 16             </view>
 17             
 18             <!-- 全国地方馆入口 -->
 19             <view class="item">
 20                 <text class="entry">全国</text>
 21                 <text class="entry">地方馆</text>
 22             </view>
 23         </view>
 24     </view>
 25 </template>
 26 
 27 <script>
 28     import chinaData from '@/common/chinaData.json'
 29     import defaultData from './js/china.js'
 30     export default {
 31         data() {
 32             return {
 33                 mapChart: null,
 34                 
 35                 // 默认全国数据
 36                 defaultData: defaultData,
 37                 
 38                 // 已经开放地区
 39                 openAreasArr: [],
 40                 // 当前循环数据索引
 41                 curIndex: 0,
 42                 
 43                 // 地方馆排名
 44                 rankingList: [],
 45                 
 46                 // 场馆更换定时器
 47                 timer: null,
 48                 // 场馆重新启动定时器
 49                 timeOut: null
 50             }
 51         },
 52         mounted() {
 53             // 获取地方馆地图推荐列表地方馆
 54             this.getVenueOfFirstPage()    
 55         },
 56         methods: {
 57             // 获取地图推荐列表
 58             getVenueOfFirstPage() {
 59                 // 接口请求数据 - 示例
 60                 // 逻辑可自行修改
 61                 this.$api.getVenueOfFirstPage(null, res => {
 62                     if (res.code == 10000) {
 63                         let data = res.data;
 64                         let defaultDataLen = this.defaultData.length;
 65                         let i = 0;
 66                         
 67                         if (data && data.length != 0) {
 68                             data.map(item => {
 69                                 // 去除 ‘馆’
 70                                 item.venueName = item.venueName.split('馆')[0];
 71                                 
 72                                 // 判断是否存在 logo
 73                                 if (item.venueHeadUrl == null || item.venueHeadUrl == '') {
 74                                     item.venueHeadUrl = '/static/logo-two.png'
 75                                 } else {
 76                                     item.venueHeadUrl = this.$util.formatImg(item.venueHeadUrl)
 77                                 };
 78                                 
 79                                 for (i = 0; i < defaultDataLen; i++) {
 80                                     if (this.defaultData[i].name.indexOf(item.venueName) > -1) {
 81                                         // 默认展示标识
 82                                         this.defaultData[i].value = 1;
 83                                         // 增加定位字段 - 用于 markPoint
 84                                         this.defaultData[i].coord = chinaData.features[i].properties.centroid;
 85                                         // 合并对象
 86                                         Object.assign(this.defaultData[i], item)
 87                                         break
 88                                     }
 89                                 }
 90                             })
 91                         };
 92                         
 93                         // 初始化配置
 94                         this.initOptions();
 95                         
 96                         // 添加监听点击
 97                         this.addMouseover()
 98                     };
 99                     
100                     // 获取排名列表
101                     this.getVenueHeatOfFirstPage()
102                 })
103             },
104             
105             // 获取排名列表
106             getVenueHeatOfFirstPage() {
107                 // 接口请求数据 - 示例
108                 // 逻辑可自行修改
109                 this.$api.getVenueHeatOfFirstPage(null, res => {
110                     if (res.code == 10000) {
111                         let data = res.data;
112                         
113                         if (data != '' && data != null) {
114                             this.rankingList = data.splice(0, 5);
115                         }
116                     }
117                 })
118             },
119             
120             // 初始化配置
121             initOptions() {
122                 echarts.registerMap('china', chinaData);
123                 this.mapChart = echarts.init(document.getElementById('mapChart'));
124 
125                 this.openAreasArr = [];
126                 this.defaultData.map((item, index) => {
127                     // 已经开馆地区
128                     if (item.value > 0) {
129                         this.openAreasArr.push(item)
130                     }
131                 });
132                   
133                 // 更新配置
134                 this.updateOption(this.openAreasArr[this.curIndex], this.openAreasArr[this.curIndex].name);
135 
136                 // 开始循环展示地方馆信息
137                 this.circulateSiteInfo()
138             },
139 
140             // 更新配置
141             updateOption(markPointData, name) {
142                 // 初始化配置
143                 let option = {
144                     tooltip: {
145                         triggerOn: 'click',
146                         confine: true,
147                         extraCssText: 'z-index: 2',
148                         formatter: params => {
149                             let data = params.data;
150 
151                             if (data.value == 0) {
152                                 return
153                             };
154                 
155                             let html = `<view style="display: flex;pointer-events: all;">
156                                            <view style="display: flex;flex-direction: column;">
157                                                <text style="margin-top: 2px;
158                                                             font-family: YouSheBiaoTiHei;
159                                                             font-size: 17px;
160                                                             white-space: nowrap;
161                                                             color: #FFFFFF;
162                                                             line-height: 18px;">${data.venueName}馆</text>
163                                                <text style="max- 75px;
164                                                             height: 18px;
165                                                             margin-top: 2px;
166                                                             font-size: 12px;
167                                                             font-weight: 400;
168                                                             color: #FFFFFF;
169                                                             line-height: 18px;">${data.venueText}</text>
170                                            </view>
171                                            <view style=" 40px;height: 40px;margin-left:10px">  
172                                                <image style=" 40px;height: 40px;" 
173                                                       src="${data.venueHeadUrl}"></image>
174                                            </view>
175                                         </view>`
176                                 
177                             return html;
178                         },
179                         backgroundColor: "rgba(133, 68, 39, 0.8)",//提示标签背景颜色
180                         textStyle: { 
181                             color: "#fff",
182                         }
183                     },
184                     geo: {
185                         map: 'china',
186                         zoom: 1.2,
187                         label: {
188                             normal: {
189                                  show: true,
190                                  textStyle: {
191                                     color: "#D49655",
192                                     fontSize: 5
193                                  }
194                             },
195                             emphasis: {
196                                 show: false, // 
197                             }
198                         },
199                         itemStyle: {
200                             normal: {
201                                 borderWidth: 1,
202                                 borderColor: '#D49655',
203                             }
204                         },
205                         regions: [{
206                             name: '南海诸岛', 
207                             value: 0, 
208                             itemStyle: {
209                               normal: {
210                                  opacity: 0,
211                                  label: {
212                                    show: false
213                                  }
214                               }
215                             }
216                         }]
217                     },
218                     series: [{
219                         map: "china",
220                         type: 'map',
221                         mapType: 'china',
222                         geoIndex: 0, 
223                         data: this.defaultData,
224                         itemStyle:{
225                             normal: {
226                                 label: {
227                                     show: true,
228                                     textStyle: {
229                                        color: "#D49655",
230                                        fontSize: 5
231                                     }
232                                 },
233                                 color: function(parameter) {
234                                     if (parameter.data) {
235                                         let value = parameter.data.value;
236                                         return value == 0? 'transparent' : '#F7FFD3'
237                                     }
238                                 }
239                             },
240                             emphasis: {
241                                 label: {
242                                     show: true,
243                                     textStyle: {
244                                        color: "#D49655",
245                                        fontSize: 5
246                                     }
247                                 },
248                                 areaColor: '#FFE602'
249                             }
250                         },
251                         markPoint: {
252                             symbol: 'image://static/index/location-ico.png',
253                             symbolSize: [22, 36],
254                             silent: true,
255                             label: {
256                                show: false 
257                             },
258                             data: [markPointData]
259                         }
260                     }]
261                 };
262                 
263                 this.mapChart.setOption(option);
264                 
265                 // 高亮展示某个区域
266                 this.highlight(name);
267                 
268                 // 头部展示当前高亮场馆信息
269                 this.curSiteInfo(markPointData);
270             },
271             
272             // 开始循环展示地方馆信息
273             circulateSiteInfo() {
274                 let len = this.openAreasArr.length;
275                 
276                 if (len == 0) {
277                     return
278                 };
279 
280                 let lastIndex = len - 1; // 最后一条数据的索引
281                 
282                 if (this.timer != null) {
283                     clearInterval(this.timer)
284                 };
285                 
286                 // 启动定时器,更换展示
287                 this.timer = setInterval(() => {
288                     // 是否已经循环到了最后一条数据
289                     if (this.curIndex >= lastIndex) {
290                         this.curIndex = 0
291                     } else {
292                         this.curIndex++    
293                     };
294 
295                     // 更新配置
296                     this.updateOption(this.openAreasArr[this.curIndex], this.openAreasArr[this.curIndex].name);
297                 }, 4000);
298             },
299             
300             // 高亮展示某个区域
301             highlight(name) {
302                 // 区域背景颜色高亮
303                 this.mapChart.dispatchAction({
304                      type: 'highlight',
305                      name: name
306                 });
307                 
308                 // 提示框变化
309                 this.mapChart.dispatchAction({
310                      type: 'showTip',
311                      name: name,
312                      seriesIndex: 0
313                 })
314             },
315             
316             // 头部展示当前高亮场馆信息
317             curSiteInfo(obj) {
318                 this.$emit('getCurSiteInfo', obj)
319             },
320             
321             
322             // 监听点击
323             // 只有默认选中区域才可点击
324             addMouseover() {
325                 this.mapChart.on("mouseover", params => {
326                     if (params.value != 0) {
327                         // 取消正在循环的高亮地区
328                          this.mapChart.dispatchAction({
329                              type: 'downplay',
330                              name: this.openAreasArr[this.curIndex].name
331                          });
332                          
333                          // 高亮展示当前地区
334                          this.updateOption(params.data, params.name);
335                          
336                          // 如果定时器启动,清除定时器
337                          if (this.timer != null) {
338                              clearInterval(this.timer)
339                          };
340                          
341                          // 如果已经开启延时,清除延时,以最新点击为准
342                          if (this.timeOut != null) {
343                              clearTimeout(this.timeOut)
344                          };
345                          
346                          // 5秒后重启定时器
347                          this.timeOut = setTimeout(() => {
348                              // 清除3秒延时
349                              clearTimeout(this.timeOut);
350                              
351                              // 重新开始循环场馆信息
352                              this.circulateSiteInfo()
353                          }, 4000)
354                     } else {
355                         // 取消区域背景颜色高亮
356                         this.mapChart.dispatchAction({
357                              type: 'downplay',
358                              name: params.name
359                         })
360                     }
361                 })
362             }
363         },
364         destroyed() {
365             clearInterval(this.timer)
366         }
367     }
368 </script>
369 
370 
371 <style lang="scss" scoped>
372     .qiun-charts {
373          690rpx;
374         height: 500rpx;
375         margin: 0 auto auto;
376         position: relative;
377         
378         #mapChart {
379              690rpx;
380             height: 500rpx;
381         }
382         
383         .map-section-9 {
384              90rpx;
385             height: 134rpx;
386             position: absolute;
387             bottom: 30rpx;
388             right: 46rpx;
389         }
390         
391         .site-recommend {
392             display: flex;
393             align-items: center;
394             margin-top: 10rpx;
395             
396             .item {
397                  98rpx;
398                 height: 98rpx;
399                 margin-left: 14rpx;
400                 display: flex;
401                 flex-direction: column;
402                 align-items: center;
403                 justify-content: center;
404                 background: linear-gradient(308deg, #FEEAC3 0%, #FCD090 100%);
405                 border-radius: 20rpx;
406                 border: 2rpx solid #F7D9A8;
407 
408                 .num, .name {    
409                     font-size: 26rpx;
410                     font-family: YouSheBiaoTiHei;
411                     color: #FFFFFF;
412                     line-height: 34rpx;
413                     background: linear-gradient(180deg, #9C4A23 0%, #713F29 100%);
414                     -webkit-background-clip: text;
415                     -webkit-text-fill-color: transparent;
416                 }
417                 
418                 .name {
419                     max- 72rpx;
420                     height: 34rpx;
421                     margin-top: 4rpx;
422                     overflow: hidden;
423                 }
424                 
425                 .entry {
426                     font-size: 26rpx;
427                     font-family: YouSheBiaoTiHei;
428                     color: #F77E05;
429                     line-height: 28rpx;
430                 }
431             }
432         }
433     }
434 </style>
localMap.vue
原文地址:https://www.cnblogs.com/langxiyu/p/14688770.html