你是否真的需要实现一个3D地图

背景

随着数据可视化近几年的快速发展,我们逐渐不再满足于仅仅是将抽象的数据用简单的图表展示出来; 而是希望能够将抽象无序的数据通过可视化、可操作、可交互且视觉效果炫酷的形式表达出来。 一般我们会通过通过数据分类、交互设置等附加形式将数据呈现给用户以易于感知的图形符号,让用户交互且直观的理解数据。

做什么?

最近接到一个需求,要求快速交付一套数据驾驶舱。其中主屏视图是由 3D 地图 + 雷达图 + 折线图组成;主要用于展示各区划指标数据及指标变化趋势,同时点击地图或雷达图需要有交互效果,设计图如下。

当接到这种需要快速交付且视觉&交互要求相对高的需求时,首先需要分析本次需求的目的是什么、需要突出什么以及重点是什么;能够通过什么方式能够快速进行交付。

分析

主视图是 3D 地图,地图上需要显示对应区划下的得分以及其组成的指标得分。具体交互为可选择区划查看该区划下各指标得分,也可选择某指标查看全部区划的单一指标得分;并且地图并不要求有可翻转旋转,放大缩小等交互效果。所以为了能够快速交付,地图可以考虑使用伪 3D 效果替代真实 3D 来实现。

拆解

那么如果需要使用平面地图来实现设计效果,就需要通过多层地图叠加来实现伪 3D 的视觉效果,下面就需要将 3D 地图进行拆解。 从原型说明我们知道:

  1. 指标得分总分 100,分为 5 档,每 20 分一档,并且每个区间固定颜色,包块区划边界也需要根据数据显示对应颜色;
  2. 整个地图边界统一颜色;
  3. 地图需要有空间角度&厚度,营造3D效果;
  4. 点击地图后需要突出该区划;

怎么做?

上面我们将地图进行了拆解,发现只需要将 3 ~ 4 层平面地图进行叠加再加上点击效果即可实现需求。

开干。

分层

第一层图层

第一层为主视图,主要渲染 5 种色块分布和具体数据信息:

yaml 复制代码
{
  name: "浙江",
  type: "map",
  map: "zhejiang",
  zlevel: 99,
  aspectScale,
  zoom,
  layoutCenter,
  layoutSize,
  roam,
  showLegendSymbol: false,
  silent: true,
  itemStyle: {
    normal: {
      // sth...
    },
    emphasis: {
      areaColor: "transparent",
    },
  },
  markPoint: {
    symbol: "none",
  },
},
第二层图层

第二层图层展示黑底及 5 色区间边框:

yaml 复制代码
{
  map: "zhejiang",
  aspectScale,
  zoom,
  layoutCenter,
  layoutSize,
  roam,
  show: true,
  zlevel: 3,
  label: {
    emphasis: {
      show: false,
    },
  },
  itemStyle: {
    normal: {
      // sth...
    },
  },
},

第三层图层

第三层图层展示透明底及纯色边框:

yaml 复制代码
{
  map: "zhejiangOverview",
  aspectScale,
  zoom,
  layoutCenter,
  layoutSize,
  roam,
  show: true,
  zlevel: 2,
  label: {
    emphasis: {
      show: false,
    },
  },
  itemStyle: {
    normal: {
      // sth...
    },
    emphasis: {
      areaColor: "transparent",
    },
  },
},

合并

按照上述步骤我们能够得到一个不同区划根据数值展示不同颜色的平面地图; 将 3 层地图进行叠加拼接即可:

伪3D

现在我们还缺少地图厚度,我们只需要模拟出地图厚度即可得要一个伪 3D 地图,以下提供两种方案实现。

方案一

再增加第四层地图,通过调整 layoutCenter 使得地图之间错位造成视觉上的 3D 效果。

yaml 复制代码
{
  map: "zhejiang",
  aspectScale,
  zoom,
  layoutCenter: ["50.6%", "52%"],
  layoutSize,
  roam,
  show: true,
  zlevel: 3,
  label: {
    emphasis: {
      show: false,
    },
  },
  itemStyle: {
    normal: {
      // sth...
    },
  },
},

方案二

方案二最省事,可以直接使用背景图贴在地图下即可,但是这套方案有缺陷即:在不同尺寸的屏幕下背景图存在错位(PS: 可以通过媒体查询解决)。

css 复制代码
@media screen and (min-height: 1080px) and (max-height: 1080px) {
  .map-echart {
    cursor: pointer;
    background-image: url(XXXXX);
    background-repeat: no-repeat;
    background-size: 110%;
    background-position: -46px 194px;
  }
}

点击高亮

基础效果

增加点击高亮效果,只需要在点击地图时在 series 中的 data 里增加 itemStyle 样式即可;

加钱效果

除了上面说的基础效果,可以通过在 series 中增加散点(气泡)图或者带有涟漪特效动画的散点(气泡)图同时可以设置背景为图片,来增加视觉效果:

yaml 复制代码
// 带有涟漪特效动画的散点(气泡)图
{
  type: "effectScatter",
  coordinateSystem: "geo",
  rippleEffect: {
    color: "#fff",
    scale: 6,
    brushType: "stroke", // fill
  },
  showEffectOn: "render",
  zlevel: 9999,
  symbol: "circle",
  symbolSize: [15, 5],
},
  // 散点(气泡)图
{
  type: "scatter",
  coordinateSystem: "geo",
  symbolSize: [32, 41],
  zlevel: 9999,
  symbol: "image://图片链接",
  symbolOffset: [0, -20],
  showEffectOn: "render",
  rippleEffect: {
    brushType: "stroke",
  },
  hoverAnimation: true,
},
ini 复制代码
const imageDom = new Image();
imageDom.src = 'https://XXXXXXX';
imageDom.alt = '图片';
​
 color: {
  image: imageDom,
  repeat: 'repeat'
}

至此我们就能快速的完成一个伪 3D 地图效果的页面。

可以看出,根据实际情况对需求进行拆解,可以在一定程度上将某一类需求拆解并转化为另外一种实现成本更低、复杂程度更低、能够快速实现上线的真实需求。

Tips

1、zlevel 决定地图的层级,值越大地图靠上; 2、map 对应地图的 Json 文件,文件可从阿里云下载,注意是否需要包含子区域; 3、visualMap 决定颜色的分段,根据以下规则进行划分:

lt(小于,less than),gt(大于,greater than),lte(小于等于 less than or equals),gte(大于等于,greater than or equals)

yaml 复制代码
 visualMap: {
  type: "piecewise",
  show: false,
  pieces: [
    {
      lt: 20,
      color: XXX,
    },
    {
      gte: 20,
      lt: 40,
      color: XXX,
    },
    {
      gte: 40,
      lt: 60,
      color: XXX,
    },
    {
      gte: 60,
      lt: 80,
      color: XXX,
    },
    {
      gte: 80,
      color: XXX,
    },
  ],
},

参考文献

echarts.apache.org/zh/index.ht...

推荐阅读

深入分析 RocketMQ 的 push 消费方式实现

浅谈MySQL分页查询的工作原理

shardingjdbc启动优化

权限管理------多系统下的数据权限通用控制

SpringBoot自动装配

招贤纳士

政采云技术团队(Zero),Base 杭州,一个富有激情和技术匠心精神的成长型团队。规模 500 人左右,在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。

如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊......如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 zcy-tc@cai-inc.com

微信公众号

文章同步发布,政采云技术团队公众号,欢迎关注

相关推荐
Мартин.8 分钟前
[Meachines] [Easy] Sea WonderCMS-XSS-RCE+System Monitor 命令注入
前端·xss
昨天;明天。今天。2 小时前
案例-表白墙简单实现
前端·javascript·css
数云界2 小时前
如何在 DAX 中计算多个周期的移动平均线
java·服务器·前端
风清扬_jd2 小时前
Chromium 如何定义一个chrome.settingsPrivate接口给前端调用c++
前端·c++·chrome
安冬的码畜日常2 小时前
【玩转 JS 函数式编程_006】2.2 小试牛刀:用函数式编程(FP)实现事件只触发一次
开发语言·前端·javascript·函数式编程·tdd·fp·jasmine
ChinaDragonDreamer2 小时前
Vite:为什么选 Vite
前端
小御姐@stella2 小时前
Vue 之组件插槽Slot用法(组件间通信一种方式)
前端·javascript·vue.js
GISer_Jing2 小时前
【React】增量传输与渲染
前端·javascript·面试
eHackyd2 小时前
前端知识汇总(持续更新)
前端
万叶学编程5 小时前
Day02-JavaScript-Vue
前端·javascript·vue.js