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 {
    width: 100%;
    position: relative;
    
    .img {
        width: 100%;
        height: 706rpx;
    }
    
    .content-header {
        width: 690rpx;
        position: absolute;
        top: -2rpx;
        left: 0;
        text-align: center;
        z-index: 1;
        
        .cur-info {
            min-width: 276rpx;
            height: 136rpx;
            padding-top: 32rpx;
            position: relative;
            display: inline-block;
            
            .info-bg {
                width: 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 {
                    width: 80rpx;
                    height: 80rpx;
                    margin-left: 20rpx;
                    
                    .logo {
                        width: 100%;
                        height: 100%;
                        border-radius: 10rpx;
                    }
                }
            }
        }
    }
    
    .content-map {
        width: 690rpx;
        position: absolute;
        top: 80rpx;
        left: 0;
    }
    
    .content-cur-site {
        min-width: 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-width: 140rpx;
                margin-top: 4rpx;
                font-size: 24rpx;
                font-weight: 400;
                color: #CBA86A;
                line-height: 36rpx;
            }
        }
        .cur-site-right {
            width: 80rpx;
            height: 80rpx;
            .right-img {
                width: 100%;
                height: 100%;
            }
        }
    }
    
    .content-site-position {
        position: absolute;
         .site-label {
            width: 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 {
                width: 12rpx;
                height: 12rpx;
                position: absolute;
                top: 6rpx;
                right: 0;
                left: 0;
                margin: auto;
                border-radius: 50%;
                background-color: #FFF;
            }
            .header-sign{
                width: 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-width: 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="width: 40px;height: 40px;margin-left:10px">  
172                                                <image style="width: 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         width: 690rpx;
374         height: 500rpx;
375         margin: 0 auto auto;
376         position: relative;
377         
378         #mapChart {
379             width: 690rpx;
380             height: 500rpx;
381         }
382         
383         .map-section-9 {
384             width: 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                 width: 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-width: 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM