从0开始搭建一个疫情地图小程序——小程序篇
为什么选小程序
主要还是服务器的费用的问题,小程序开发结合云服务器可以省掉很多运维的问题,加上我这次开发主要是为了熟悉小程序的组件开发与echart在小程序绘制地图的性能表现。
前端页面展示
运行
首页
切换页面
各地详细页面
源代码
前端实现
这里并不全部讲前端的代码,主要讲我在开发的时候遇到的坑。
组件开发
拿我首页上,每天数据的显示,我将它封装为一个组件。
和VUE框架有很多的相同点,比如要使用的时候,需要声明引用这个组件
不过小程序是在引用的文件对应的JSON
文件下配置:
还需要在组件的对应的JSON文件
声明这个是一个组件
当然组件也可以引用其他组件,在usingComponents
这个对象下,引用即可。
插槽
和VUE框架差不多,直接看小程序的文档实例
wxss注意事项
在组件wxss中不应使用ID选择器、属性选择器和标签名选择器。
component的构造函数
微信官方文档
基本理解了生命周期、组件之间通信等,基本就可以完成大部分的组件开发,这一部分建议阅读微信开发文档学习。
每日疫情数据变化组件开发
wxml
通过绑定incrData
数据进行显示
js
// components/dailyData.js
Component({/*** 组件的属性列表*/properties: {// 传递过来的数据dailyTextContent:{type:Object}},// 监听数据变化observers:{dailyTextContent: function (dailyTextContent){// 这里相当于是入口,需要进行错误判断,如果传入的数据格式错误,应该提示使用try{// 更新时间this.getDate(dailyTextContent.updated_at)// 更新数据this.setIncr(dailyTextContent)}catch(e){console.log('daily commponet 传入格式错误',e)}}},data: {updateTime:'',incrData:{curedIncr: '',deadIncr: '',seriousIncr: '',suspectedIncr: '',confirmedIncr: ''}},/*** 组件的方法列表*/methods: {// 设置数据setIncr(result){let data = this.getIncr(result)for (let [key, value] of Object.entries(data)){if(value > 0) {data[key] = `+${value}`}else {data[key] = `-${value}`}}this.setData({incrData:data})},// 获取需要的数据getIncr(result){let { curedIncr,deadIncr,seriousIncr,suspectedIncr,confirmedIncr } = resultreturn {curedIncr,deadIncr,seriousIncr,suspectedIncr,confirmedIncr}},// 获取时间格式getDate(time){let date = new Date()date.setTime(time)this.setData({updateTime: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}`})// return `${date.getYear()}-${date.getMount() + 1}-${date.getDate()}`}}
})
首页使用
<!-- index.wxml -->
<view class="container"><view class="textContent"><daily-display dailyTextContent='{{dailyTextContent}}'></daily-display></view>
</view>
Page({data:{dailyTextContent: {},},onLoad(){let dailyData = {}this.setData({dailyTextContent : dailyData// 请求到的数据})}
})
使用echartjs
echartjs
有一个官方的小程序demo
,为了适配小程序的框架,echartjs
也封装了一个组件供开发者使用。
echartjs小程序版本下载
ec的onInit函数
这个相当于整个echart绘制的入口,我们只需要在这个函数中,声明我们需要进行的绘制操作就可以绘制出我们需要的画面。这个我在获取数据之后再进行详细描述。
如何进行异步渲染?
看了下ec-canvas
组件,理解了这个组件运行的顺序。由于绘制地图需要一些地图的数据,所以我在ec-cavnas
组件增加了变量保存这些数据。并且新增一些方法去修改这些数据,刷新页面和显示/隐藏页面的方法。
获取mapData
// 处理 mapData dataFormate是我提前设计好的一种格式// 封装到utils.js文件中mapData = utils.handleData(dataDaily, dataFormate);
dataFormate
module.exports = [{ en_name: 'guangdong', name: '广东',long_name:'广东省' },{ en_name: 'beijing', name: '北京',long_name:'北京市' },{ en_name: 'tianjin', name: '天津' ,long_name:'天津市'},{ en_name: 'hebei', name: '河北',long_name:'河北省' },{ en_name: 'liaoning', name: '辽宁',long_name:'辽宁省' },{ en_name: 'jilin', name: '吉林',long_name:'吉林省' },{ en_name: 'heilongjiang', name: '黑龙江',long_name:'黑龙江省' },{ en_name: 'shandong', name: '山东',long_name:'山东省' },{ en_name: 'jiangsu', name: '江苏' ,long_name:'江苏省'},{ en_name: 'shanghai', name: '上海',long_name:'上海市' },{ en_name: 'zhejiang', name: '浙江' ,long_name:'浙江省'},{ en_name: 'anhui', name: '安徽',long_name:'安徽省' },{ en_name: 'fujian', name: '福建' ,long_name:'福建省'},{ en_name: 'jiangxi', name: '江西' ,long_name:'江西省'},{ en_name: 'guangxi', name: '广西' ,long_name:'广西壮族自治区'},{ en_name: 'hainan', name: '海南' ,long_name:'海南省'},{ en_name: 'henan', name: '河南' ,long_name:'河南省'},{ en_name: 'hunan', name: '湖南',long_name:'湖南省' },{ en_name: 'shanxi2', name: '陕西',long_name:'陕西省' },{ en_name: 'hubei', name: '湖北',long_name:'河北省' },{ en_name: 'shanxi1', name: '山西',long_name:'山西省' },{ en_name: 'neimenggu', name: '内蒙古' ,long_name:'内蒙古自治区'},{ en_name: 'ningxia', name: '宁夏',long_name:'宁夏回族自治区' },{ en_name: 'qinghai', name: '青海' ,long_name:'青海省'},{ en_name: 'gansu', name: '甘肃',long_name:'甘肃省' },{ en_name: 'xinjiang', name: '新疆',long_name:'新疆维吾尔自治区' },{ en_name: 'sichuan', name: '四川',long_name:'四川省' },{ en_name: 'guizhou', name: '贵州',long_name:'贵州省' },{ en_name: 'yunnan', name: '云南' ,long_name:'云南省'},{ en_name: 'chongqing', name: '重庆' ,long_name:'重庆市'},{ en_name: 'xizang', name: '西藏',long_name:'西藏自治区' },{ en_name: 'hongkong', name: '香港' ,long_name:'香港特别行政区'},{ en_name: 'aomen', name: '澳门',long_name:'澳门特别行政区' },{ en_name: 'taiwan', name: '台湾',long_name:'台湾省' }]
dataDaily数据
全国各省各市的病例数据
module.exports = [
{ "provinceName": "湖北省","provinceShortName": "湖北","currentConfirmedCount": 19486, "confirmedCount": 67707, "suspectedCount": 0, "curedCount": 45235,"deadCount": 2986, "comment": "", "locationId": 420000, "statisticsData": "https://file1.dxycdn.com/2020/0223/618/3398299751673487511-135.json", "cities": [{ "cityName": "武汉","currentConfirmedCount": 17565, "confirmedCount": 49912, "suspectedCount": 0, "curedCount": 29977, "deadCount": 2370, "locationId": 420100 }, { "cityName": "孝感", "currentConfirmedCount": 369, "confirmedCount": 3518, "suspectedCount": 0, "curedCount": 3024, "deadCount": 125, "locationId": 420900 },{ "cityName": "鄂州", "currentConfirmedCount": 347, "confirmedCount": 1394, "suspectedCount": 0, "curedCount": 993, "deadCount": 54, "locationId": 420700 },……………………………………"https://file1.dxycdn.com/2020/0223/353/3398299755968039885-135.json", "cities": [{ "cityName": "拉萨", "currentConfirmedCount": 0, "confirmedCount": 1, "suspectedCount": 0, "curedCount": 1, "deadCount": 0, "locationId": 540100 }] }]
handleData()
// getTextName 获取对应的城市名字
function getTextName(dataFormate, data) {for (let item of dataFormate) {if (data.provinceShortName === item.name) {// 如果城市名称一样return {name: `${item.name}`,en_name: item.en_name};}}return false;
}handleData(dataDaily, dataFormate) {let res = [];for (let item of dataDaily) {let { name, en_name } = getTextName(dataFormate, item);if (name) {let data = item;let cities = [];// 由于台湾 澳门 香港 上海等没有市级地区 所以自身就是一个城市if (data.cities.length < 1) {cities.push({cityName: data.provinceShortName,confirmedCount: data.confirmedCount,curedCount: data.curedCount,deadCount: data.deadCount,locationId: data.locationId,suspectedCount: data.suspectedCount});} else {cities = data.cities;}// 地图绘制的数据res.push({name,key: en_name,value: data.confirmedCount,cities: cities});}}return res;},
输出结果
erchatjs
在开发的时候,我把关于echartjs相关的样式操作,统一封装到一个chart.js
文件中去。
在前文中,我们知道了首页绑定的ec
中的onInit
变量是一个回调函数,我们所有关于绘制的操作都在这个函数里面封装。
所以在我们封装的chartjs
文件中,最重要的函数也是这个。
这里面的函数主要都是一些api的调用与样式的option的修改,我都会在注释一些说明,大家只要熟悉一些echartjs
的使用,就可以看的明白了。
注意:我修改了ec-canvas
的组件中的init
的原方法:
我修改的方法,使用的是我们之前保存的mapData
数据,里面保存了所有省市的病例数据。
// 地图 svg数据
import geoJson from "./mapData";
// api 文件
import echarts from "../ec-canvas/echarts";
module.exports = {// 绘制操作 seriesData这个参数是我修改了ec-canvas组件 里面保存了所有省市的病例数据initChart: function(canvas, width, height, seriesData) {// 初始化echartconst chart = echarts.init(canvas, null, {width: width,height: height});// 绑定canvascanvas.setChart(chart);// 绘制地图echarts.registerMap("china", geoJson);// 这里是map的option的数据显示const option = {// 点击显示的 浮动框tooltip: {// 下面代码},// 地图value的分级与样式visualMap: {// 下面代码},textStyle: {color: "#ecf7f7"},// 地图样式series: [{type: "map",mapType: "china",label: {show: true,emphasis: {textStyle: {color: "#fff",fontSize: 8}},textStyle: {color: "#000",fontSize: 8},},itemStyle: {normal: {borderColor: "#AAAAAA",areaColor: "#fff"// fontSize:8,},emphasis: {areaColor: "#0000AA",borderWidth: 0// fontSize:8,}},animation: false,// 数据绑定data: seriesData}]};// 将option设置到echart中去chart.setOption(option);return chart;}
};
点击浮动框
tooltip: {trigger: "item",formatter:function(obj){return `${obj.data.name} 确诊人数:${obj.data.value}`},textStyle:{fontWeight:'bold',width:'30px'},// 自适应点击位置,避免因为数据过长导致看不全浮动框position:function(point,dom,rect,size){let windowWidth =wx.getSystemInfoSync().windowWidthif(point[0]> windowWidth /2 ){return [parseInt(point[0]-size.width),point[1]]}else {return [point[0],point[1]]}}},
地图分级样式
valueMap:{min: 0,max: 34000,// splitNumber: 5,top: "top",pieces: [{ min: 5001 }, // 不指定 max,表示 max 为无限大(Infinity)。{ min: 3001, max: 5000 },{ min: 1001, max: 3000 },{ min: 101, max: 1000 },{ min: 11, max: 100 },// {value: 123, label: '123(自定义特殊颜色)', color: 'grey'}, // 表示 value 等于 123 的情况。{ max: 10 } // 不指定 min,表示 min 为无限大(-Infinity)。],// 颜色会自动匹配color: ["#c0e3e4","#92bfc7","#639aa9","#35768c","#06516e"].reverse(),// 字体样式textStyle: {color: "#000",fontSize: 12}}
canvas的层级问题
由于小程序中的canvas
是原生的,微信提供的标签是在真机上无法覆盖canvas
的。
为了解决这个问题,我们需要使用微信官方提供的cover-view
原理是底层覆盖了一个黑色透明的遮罩层,在覆盖一个显示区域即可。主要区别是使用cover-view
替换所有的view
定位直接使用fixed
就可以实现下面的效果
全国各地数据:手风琴
这个使用的是一个小程序的组件库,由于这些组件库是可以按需引入的,所以这个大家自己去找自己需要的组件,直接复制到小程序目录就可以使用了。
这个是我使用的小程序ui库
iView是TalkingData发布的一款高质量的基于Vue.js组件库,而iView weapp则是它们的小程序版本。
GitHub地址
npm下载:npm i iview-weapp
总结
这个项目大概花了3天左右的时间开发,虽然由于微信官方的原因,导致无法上线我觉得是挺可惜的。因为我后面都已经打算继续把国外的疫情地图,根据用户定位结合腾讯的地图定位去做一个显示用户当地的疫情地图这2个功能的,可惜没有通过,导致没有动力做下去。
在这次开发中,主要还是学习到了小程序的组件开发和echartjs
中的小程序的用法和api的使用,还是感叹这个echartjs
的强大之处,就是非常容易上手,功能丰富。也不知道未来能不能参与这种库的开发。
最近也在公司实习,学习前端也有2年多了,感觉自己还是有很多模糊的概念,很多时候都在想程序员的终点是什么? 在看完雷军的大学时期的经历和一些文章,其实也感觉的到程序员的终点也是交互设计工程师。
如果只是一个提供技术力的程序员是活不长久的。
在这次实习的时候,公司安排我一个人负责一个内部的设备管理系统,由于第一次做这种比较完整的项目,虽然这种系统并不复杂,但是在开发到后面的时候,你永远不知道会有什么需求会出现,所以如果我们设计的页面比较死板的话,就会导致后期的需求开发周期变长和变难。
无论多复杂的系统都是从最简单的开始的,都是一砖一石搭建出来的,如果你觉得你能够在产品开发前就已经设计好一个很完美的系统,我觉得是不可能的。因为每一个系统的需求都会因为它面对的用户而变得不同。(也有可能是我太菜了)
像这个疫情地图,从一开始只有一个页面,到后面的组件,代码的复杂度越来越大,但是我的目录文件和代码管理都不是很清晰。我感觉是当局者迷,过几天反思就会发现项目中的问题。
还有几个月就毕业了,祝大家能够获得自己心仪的工作。
从0开始搭建一个疫情地图小程序——小程序篇相关推荐
- 怎样从0开始搭建一个测试框架_0
怎样从0开始搭建一个测试框架_0 在开始之前,请让我先声明几点: 这个"从0开始"并不是说你不需要任何基础知识,而是指框架从无到有的过程,要开始搭建还是需要一定基础 请确保你已经掌 ...
- 从 0 开始搭建一个技术博客,私藏干货~
2019独角兽企业重金招聘Python工程师标准>>> 技术博客的选型有很多种,如:博客园.CSDN.开源中国.简书.知乎等--都可以用来写文章,形成自己的技术博客. 上面的博客都是 ...
- 手把手教你从0开始搭建一个vue项目(完结)
前言 上一节webpack实战之(手把手教你从0开始搭建一个vue项目)最后我们完成了css样式的配置: webpack.config.js: const path = require("p ...
- 【React进阶-1】从0搭建一个完整的React项目(入门篇)
这篇文章带领大家从零开始手动撸一个React项目的基础框架,集成React全家桶.万字长文,请各位有足够的时间时再来阅读和学习. 概述 平时工作中一直在用React提供的脚手架工具搭建React项目, ...
- 从0开始搭建一个战棋游戏的AI(初级教程)
战棋类游戏一直以高策略性著称,其中不乏经典之作如"三国志英杰传"."三国曹操传"."炎龙骑士团"."金庸群侠传"等等. ...
- 怎样0成本搭建一个高效CDN加速图床
这次给大家分享一个干货: 快速搭建一个免费极速图床! 怎么搭建呢?大体上来说需要用到三个工具:GitHub.PicGo和jsDelivr.其中GitHub用于存储,PicGo用于上传.jsDelivr ...
- 从0开始搭建一台服务器开发环境【开始篇】
通过本博客你将搭建的服务,以及配置如下: 0,用SeceruCRM连接服务器后台 1,设置linux字符编码 2,统一服务器时区 3,安装jdk环境 4,安装maven 5,安装tomcat 6,安装 ...
- 怎样从0开始搭建一个测试框架_1
这一步我们用到了selenium的基本的知识,以及一些unittest和PyYaml库的内容,有问题的同学可以参考我之前的博客: Python Selenium自动化测试详解 Python必会的单元测 ...
- django2.1.7从0开始搭建一个个人博客网站第5天
技术引用:uuid UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分.其目的, ...
最新文章
- cachecloud:Redis云管理平台
- Flutter 中的基本路由
- 分布式系统原理 之9 CAP 理论
- SVC调参总结+调参实例
- (转载)用C#实现MySQL建库及建表
- 3月25日 编译的四个过程
- 虚拟机安装Oracle服务器和本地Oracle安装教程
- Axure rp8元件库载入失败怎么解决
- JavaScript 3D实时线图
- 无头浏览器与Puppeteer中PDF生成应用指南
- 显卡故障软件测试,显卡软硬件故障检查与排除实战
- 【Ruby on Rails全栈课程】2.1 ruby语言入门
- excel高级筛选怎么用_Excel高级筛选使用
- 黑客帝国角色 之 先知的另类解读
- 用户太多:互联网巨头之惑
- fatal: unable to access github.com schannel failed to receive handshake, SSL/TLS connection failed
- Java POI 对Excel合并单元格的数据处理
- 图片无损压缩(ubuntu 安装 )
- 使用蒲公英来做iOS测试应用的分发 - 唐巧的技术博客
- 基于APW7137的负电源(-12V)方案原理分析
热门文章
- 数据结构中数据、数据对象、数据元素、数据项之间的关系
- 'utf-8' codec can't decode byte 0xca in position 94: invalid continuation byte问题
- MySQL常见命令及基本查询
- 常见错误:无效的文件dsn_DSN1COPY错误-SP映射页面错误
- 成都月嫂提醒你,宝宝的疫苗接种时间
- Mysql中的常用类型 说明
- MySQL中如何定义一个自己写的函数
- [转]GotW#63 狂乱的代码
- 基于Robotics Toolbox的机械臂工作空间求解
- 新浪微博定位页面代码解析