• 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
  • 相关阅读:
    超哥笔记--linux准备知识(1)
    爬虫系列---scrapy全栈数据爬取框架(Crawlspider)
    爬虫系列---scrapy post请求、框架组件和下载中间件+boss直聘爬取
    pymongo 一篇文章搞定
    一篇文章搞定mongodb
    python进阶(四) windows下虚拟环境使用
    java基础(四) -变量类型
    java基础(二) -对象和类
    java基础(一) -语法
    Linux常用命令大全
  • 原文地址:https://www.cnblogs.com/langxiyu/p/14688770.html
Copyright © 2020-2023  润新知