自动导航项目 - 微信小程序实践
笔记来源:拉勾教育 - 大前端就业集训营
文章内容:学习过程中的笔记、感悟、和经验
提示:项目实战类文章资源无法上传,仅供参考
开发高德地图小项目
申请高德地图开发者
- 注册高德地图开发者账号:官网地址
- 创建应用:管理应用 => 我的应用 => 创建新应用
- 创建应用密钥:应用不同,密钥不同(安卓 / ios / web / 小程序)
详解高德微信小程序SDK
- 下载小程序SDK
- 本质上SDK是
wx.request
对WEB服务API的封装,无论哪个平台兼容性有保障 - 小程序的运行环境实际上就是内置的浏览器环境
- 本质上SDK是
高德小程序SDK部分常用接口
API | 描述 |
---|---|
getWxLocation | 获取位置信息(通过高德API获取) |
getRegeo | 获取地理描述信息(将经纬度转成行政区划名称) |
getWeather | 当前位置的天气信息,数据较少建议使用和风天气 |
getPoiAround | 查询周围兴趣点(POI: Point of Interest),例如搜索 |
getStaticmap | 获取静态地图(不能拖动) |
getInputtips | 获取输入提示词(关键字查询) |
getWalkingRoute | 步行路线规划 |
getDrivingRoute | 驾车路线规划 |
getRidingRoute | 骑行路线规划 |
getTransitRoute | 公交地铁路线规划(和前三个参数有区别) |
高德地图的SDK中的接口函数大部实际上就是将小程序的云API进行二次封装的产物
首页接入高德地图
- 小程序启动时获取当前位置,并存到本地
- 拷贝高德SDK文件到项目中,并创建高德SDK配置文件
- 创建高德SDK实例对象,写入KEY,最后导出实例对象
- 首页页面加载时进行逆地址解析(高德 - getRegeo)获取实际地址
- 书写map地图组件,添加相关属性
- 首页js文件中解析之前存在本地的经纬度数据
- 直接使用高德返回的数据做定位点数据
- 调整样式,让地图全屏显示
- 添加底部地址栏,绝对定位,使用getRegeo返回数据
// app.json
{
"pages": [
// 添加一个页面
"pages/home/home",
"pages/index/index",
"pages/logs/logs"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "Weixin",
"navigationBarTextStyle": "black"
},
"style": "v2",
"sitemapLocation": "sitemap.json",
// 获取当前位置
"permission": {
"scope.userLocation": {
"desc": "尝试获取位置信息"
}
},
// 设置底部导航
"tabBar": {
"list": [{
"pagePath": "pages/home/home",
"text": "首页",
"iconPath": "./static/images/featured.png",
"selectedIconPath": "./static/images/featured-actived.png"
}, {
"pagePath": "pages/index/index",
"text": "欢迎",
"iconPath": "./static/images/featured.png",
"selectedIconPath": "./static/images/featured-actived.png"
}, {
"pagePath": "pages/logs/logs",
"text": "日志",
"iconPath": "./static/images/featured.png",
"selectedIconPath": "./static/images/featured-actived.png"
}]
}
}
// app.js
App({
onLaunch() {
// 展示本地存储能力
const logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
// 登录
wx.login({
success: res => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
}
})
// 获取当前位置
wx.getLocation({
type: 'gcj02',
// 成功后存储经纬度到本地
success (res) {
wx.setStorageSync('latitude', res.latitude)
wx.setStorageSync('longitude', res.longitude)
}
})
},
globalData: {
userInfo: null
}
})
// utils/amap-config.js 高德地图SDK配置
// 引入高德SDK
const amapfile = require('./amap-wx.130')
// 创建实例
const map = new amapfile.AMapWX({
key: '842828a8a2bce6537a91450390dd5471'
})
// 导出
module.exports = {
map
}
// pages/home/home.js
const amap = require('../../utils/amap-config')
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 存储this指向
const that = this
// 读取本地位置信息,存储进data
that.setData({
longitude: wx.getStorageSync('longitude'),
latitude: wx.getStorageSync('latitude')
})
// 调用高德逆地址解析,获取坐标的位置信息
amap.map.getRegeo({
// 成功调用
success: res => {
console.log(res);
// 使用数据作为标记信息
// 设置标记使用图片
res[0].iconPath = '/static/images/location.png'
// 存储标记数据到本地
that.setData({
markers: res
})
},
// 失败打印失败信息
fail: err => {
console.log(err)
}
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
<!--pages/home/home.wxml-->
<view class="container">
<!-- 地图组件,设置经纬度、标记点信息 -->
<map class="map" name="" longitude='{{longitude}}' latitude='{{latitude}}' markers='{{markers}}' scale="14"></map>
<!-- 底部浮动位置展示 -->
<view class="map-text">
<text>{{markers[0].name}}</text>
<text>{{markers[0].desc}}</text>
<view class="button-sp-area">
<a class="weui-btn weui-btn_mini weui-btn_primary">按钮</a>
</view>
</view>
</view>
/* pages/home/home.wxss */
/* // 首页单独设置样式 */
.container {
height: 100vh;
width: 100vw;
}
/* 地图 */
.map {
width: 100%;
height: 100%;
}
/* 底部文本 */
.map-text {
position: relative;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
background-color: #fff;
/* text-align: center; */
}
.map-text > text {
display: block;
margin: 10px 20px;
}
.map-text > text:nth-child(2) {
font-size: 14px;
color: #666;
}
.map-text > .button-sp-area {
position: absolute;
right: 20px;
top: 50%;
margin-top: -16px;
}
输入提示
使用高德地图API - getInputtips()
- 搜索框使用WEUI搜索框组件,拷贝js和WXML
- 在输入事件中添加逻辑,使用输入的内容调用API获取输入提示
- 可以把这个逻辑单独封装为一个函数
- 存储拿到的提示数据到data中
- 使用weUI的列表组件展示输入提示数据,如果有提示数据才显示
- 遍历数据列表,创建结构
- 可能会和之前写的文字提示冲突,把底部文字提示改成固定定位即可
- 添加判断条件,如果输入内容为空,清空数据,不为空才请求数据
- 记得取消按钮也要清空数据
/**app.wxss**/
/* 引入WeUI */
@import './weui.wxss';
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
/* padding: 200rpx 0; */
box-sizing: border-box;
}
<!--pages/home/home.wxml-->
<view class="container">
<!-- 顶部搜索框 -->
<view class="page__bd">
<view class="weui-search-bar {{inputShowed ? 'weui-search-bar_focusing' : ''}}" id="searchBar">
<form class="weui-search-bar__form">
<view class="weui-search-bar__box">
<i class="weui-icon-search"></i>
<input type="text" class="weui-search-bar__input" placeholder="搜索" value="{{inputVal}}"
focus="{{inputShowed}}" bindinput="inputTyping" />
<span class="weui-icon-clear" wx:if="{{inputVal.length > 0}}" bindtap="clearInput"></span>
</view>
<label class="weui-search-bar__label" bindtap="showInput">
<i class="weui-icon-search"></i>
<span class="weui-search-bar__text">搜索</span>
</label>
</form>
<view class="weui-search-bar__cancel-btn" bindtap="hideInput">取消</view>
</view>
<view class="weui-cells searchbar-result" wx:if="{{inputVal.length > 0}}">
<!-- 遍历数据创建搜索提示结果 -->
<view class="weui-cell weui-cell_active weui-cell_access" wx:for="{{tips}}" wx:key='{{item.id}}}'>
<view class="weui-cell__bd weui-cell_primary">
<view>{{item.name}}</view>
</view>
</view>
</view>
</view>
<!-- 地图组件,设置经纬度、标记点信息 -->
<map class="map" name="" longitude='{{longitude}}' latitude='{{latitude}}' markers='{{markers}}' scale="14"></map>
<!-- 底部浮动位置展示 -->
<view class="map-text">
<text>{{markers[0].name}}</text>
<text>{{markers[0].desc}}</text>
<view class="button-sp-area">
<a class="weui-btn weui-btn_mini weui-btn_primary">按钮</a>
</view>
</view>
</view>
/* pages/home/home.wxss */
/* // 首页单独设置样式 */
.container {
height: 100vh;
width: 100vw;
}
/* 地图 */
.map {
width: 100%;
height: 100%;
}
/* 底部文本 */
.map-text {
position: relative;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
background-color: #fff;
/* text-align: center; */
}
.map-text > text {
display: block;
margin: 10px 20px;
}
.map-text > text:nth-child(2) {
font-size: 14px;
color: #666;
}
.map-text > .button-sp-area {
position: absolute;
right: 20px;
top: 50%;
margin-top: -16px;
}
/* 搜索框 */
.page__bd {
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 1;
}
.searchbar-result {
margin-top: 0;
font-size: 12px
}
.searchbar-result .weui-cell {
padding: 10px;
}
.searchbar-result .weui-cell__bd {
padding: 0 0 0 20px;
color: #999;
}
.searchbar-result:before {
display: none
}
// pages/home/home.js
const amap = require('../../utils/amap-config')
Page({
/**
* 页面的初始数据
*/
data: {
inputShowed: false,
inputVal: ""
},
// 点击电视搜索框时间事件函数
showInput: function () {
this.setData({
inputShowed: true
});
},
// 取消按钮点击事件函数
hideInput: function () {
this.setData({
inputVal: "",
inputShowed: false
});
},
// 清空按钮点击事件函数
clearInput: function () {
this.setData({
inputVal: ""
});
},
// 文本框输入事件
inputTyping: function (e) {
//存储输入内容
this.setData({
inputVal: e.detail.value
});
// 调用方法获取提示数据
this.getTips(e.detail.value)
},
// 获取输入提示数据
getTips: function (keywords) {
// 存储this指向
const that = this
// 调用高德方法获取输入提醒
amap.map.getInputtips({
// 输入内容
keywords,
// 位置信息
location: that.data.longitude + ',' + that.data.latitude,
// 成功后调用
success: (res) => {
// 如果获取成功存储数据
if(res && res.tips) that.setData({tips: res.tips})
console.log(that.data.tips);
}
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 存储this指向
const that = this
// 读取本地位置信息,存储进data
that.setData({
longitude: wx.getStorageSync('longitude'),
latitude: wx.getStorageSync('latitude')
})
// 调用高德逆地址解析,获取坐标的位置信息
amap.map.getRegeo({
// 成功调用
success: res => {
console.log(res);
// 使用数据作为标记信息
// 设置标记使用图片
res[0].iconPath = '/static/images/location.png'
// 存储标记数据到本地
that.setData({
markers: res
})
},
// 失败打印失败信息
fail: err => {
console.log(err)
}
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
查找周围兴趣点
调用高德地图 getPoiAround()方法获取兴趣点数据
-
选中一个点:展示当前点文本信息、高亮当前点、导航到该点(后续再说)
-
另起一页:借助WeUI的九宫格组件,给出一些常用关键词,设置图标图片、尺寸
-
在新页面下另起一页,放置兴趣点地图
-
给九宫格添加点击事件,跳转兴趣点地图
- 跳转需要传参,使用data-参数名,读取使用
e.currentTarget.dataset.属性名
- 书写点击事件,判断是否有关键词属性,有则跳转 - navigateTo,使用?传参
- 跳转需要传参,使用data-参数名,读取使用
-
页面接受参数,使用onLader中参数可以获取
-
将关键字设置为标题 - setNavigationBarTitle
-
使用高德地图API - 查找周围兴趣点
- 页面引入高德地图,调用API,传递参数,获取数据
-
返回的markers可以作为定位点标记数据,注意保存一份全局数据,以便后期使用
-
存储定位点标记和当前位置经纬度数据到data
-
书写地图标签,展示地图,绑定数据 (参考首页地图)
-
添加两个函数:
- 展示当前点信息,存储到data中,然后绑定给底部展示区,记得绑定位置,后面导航需要用
- 修改当前点图标:更改当前点图标,恢复其他点图标,保存新的标记数据到data - markers
-
添加点击标记点事件 - bindmarkertap:参数e带有当前点信息,从里面获取id,再次调用上面的展示当前点信息和修改当前点图标两个方法
{
"pages": [
// 新页面
"pages/index/index",
// 二级页面
"pages/index/around",
"pages/home/home",
"pages/logs/logs"
],
...............
}
<!--index.wxml-->
<view class="page" data-weui-theme="{{theme}}">
<view class="page__hd">
<view class="page__desc">查找周边</view>
</view>
<!-- 宫格组件 -->
<view class="weui-grids">
<!-- 遍历数据创建宫格组件,添加点击事件跳转,设置自定义属性方便携带参数 -->
<a class="weui-grid" wx:for="{{around_title}}" wx:key="key" bindtap="toAround" data-keyword="{{item}}">
<view class="weui-grid__icon">
<image src="../../static/images/featured.png" alt></image>
</view>
<view class="weui-grid__label">{{item}}</view>
</a>
</view>
</view>
// index.js
const app = getApp()
Page({
data: {
// 创建宫格使用的文本
around_title: ['美食','酒店','加油站','医院','药店','银行','地铁','公交站','厕所']
},
onLoad() {
},
// 点击事件函数
toAround: (e) => {
// 微信跳转API
wx.navigateTo({
// 给路由携带参数
url: `around?keyword=${e.currentTarget.dataset.keyword}`,
})
}
})
/**index.wxss**/
.weui-grid image {
height: 30px;
width: 30px;
}
.page__hd {
margin-left: 20px;
}
<!--pages/index/around.wxml-->
<view class="container">
<!-- 地图组件,绑定数据,添加点击标记点事件 -->
<map class="map" name="" longitude='{{longitude}}' latitude='{{latitude}}' markers='{{markers}}' scale="14" bindmarkertap="chagenMarker"></map>
<!-- 底部浮动位置展示 -->
<view class="map-text">
<!-- 绑定数据 -->
<text>{{info.name}}</text>
<text>{{info.address}}</text>
<view class="button-sp-area">
<a class="weui-btn weui-btn_mini weui-btn_primary">按钮</a>
</view>
</view>
</view>
// pages/index/around.js
// 引入高德地图
const {
map
} = require('../../utils/amap-config')
// 创建全局标记点数据,后面获取到数据存到这
let markersData = []
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 绑定this指向
const that = this
// 获取搜索关键词
const keyword = options.keyword
// 修改顶部标题
wx.setNavigationBarTitle({
title: '周边' + keyword,
})
// 调用高德API获取兴趣点
map.getPoiAround({
// API参数 - 关键词
querykeywords: keyword,
// 成功后调用
success: res => {
// 存储获取数据
markersData = res.markers
// 如果获取到内容
if (markersData.length > 0) {
// 保存数据到data
that.setData({
markers: markersData,
// 经纬度
latitude: wx.getStorageSync('latitude'),
longitude: wx.getStorageSync('longitude')
})
}
// 调用方法修改文字,默认选中第一个数据
that.saveInfo(markersData, 0)
// 调用方法修改选中图标,默认选中第一个数据
that.chagenIcon(markersData, 0)
}
})
},
// 修改底部文字方法
// 参数:数据源、索引值
saveInfo: function (data, index) {
// 直接修改data中的数据
this.setData({
info: {
name: data[index].name,
address: data[index].address,
id: data[index].id
}
})
},
// 修改图标方法
// // 参数:数据源、索引值
chagenIcon: function (data, index) {
// 创建空数组获存放新数据
let newMarkersData = []
// 遍历数据源
for (let i = 0; i < data.length; i++) {
// 数据
const markerItem = data[i]
// 修改选中数据的图标,把其他图标恢复成默认
if (i === index) {
markerItem.iconPath = '../../static/images/marker_checked.png'
} else {
markerItem.iconPath = '../../static/images/marker.png'
}
// 将数据存放到数组
newMarkersData.push(markerItem)
}
// 最后保存新数据修改data
this.setData({
markers: newMarkersData
})
},
// 点击标记点事件函数
chagenMarker: function (e) {
// 获取id
const id = e.detail.markerId
// 直接调用上面两个函数传参即可
this.saveInfo(markersData, id)
this.chagenIcon(markersData, id)
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
/* pages/index/around.wxss */
/* // 首页单独设置样式 */
.container {
height: 100vh;
width: 100vw;
}
/* 地图 */
.map {
width: 100%;
height: 100%;
}
/* 底部文本 */
.map-text {
position: relative;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
background-color: #fff;
/* text-align: center; */
}
/* // 底部文本 */
.map-text > text {
display: block;
margin: 10px 20px;
}
.map-text > text:nth-child(2) {
font-size: 14px;
color: #666;
}
.map-text > .button-sp-area {
position: absolute;
right: 20px;
top: 50%;
margin-top: -16px;
}
路径规划
步行出行
使用高德getWalkingRoute
-
两个点坐标
- 起点:当前定位
- 终点:其他方式获取,搜索或者兴趣点
-
底部可以查看步行导航描述
-
创建路径规划页面
-
给搜索框下拉列表绑定点击事件,点击后跳转到路径规划页面 - bindtap
-
跳转时携带参数,参数为终点坐标
-
路径规划页面顶部tab菜单使用WeUI - tabbar进行改造
-
tab上面不同的选项跳转到不同的页面(步行、骑行、驾车、公交),所以需要有四个导航页面,给四个tab都添加自定义属性(和页面名称一致),方便之后跳转
-
设置tab图标,调整样式
-
tab添加点击事件,点击后跳转相应页面,跳转路径不仅要有路径,还要传经纬度
-
跳转过来时判断是否传递了经纬度,没传递报错
-
给定起点和终点标记数据(图标、经纬度)
-
放入地图组件,绑定数据查看起点终点是否加载
-
起点终点连线使用polyline属性设置,绑定data数据
-
调用高德地图步行导航接口getWalkingRoute,传递起点和终点坐标,接收数据
-
返回数据中的steps中的每一个成员的polyline值即为路径每一个经过的点坐标,把所有点坐标拼接(成数组)起来就成为一条线
-
一个polyline可拆分出多个坐标,最红把这些都拆分成一组组经纬度
-
最后把这些点数据传递给data,另外设置颜色、宽度
-
获取导航文本信息,从路径接口返回数据中(保存在data中)
-
同时还需要把距离、预计时间保存
-
页面底部添加盒子书写结构 绑定数据
-
添加一个详情按钮,点击可展开详情,这里建议底部高度使用动态高度,在data里面指定,点击的时候只需要修改值即可
-
另外,文本列表也可以动态显示,没有点击详情时不显示,点击后才加载
-
给详情按钮添加点击事件,每次判断当前是否处于展开状态(列表是否显示),进行data修改
-
最后进行样式调整
// 添加四个页面分别放置四个导航页
"pages": [
"pages/home/home",
"pages/index/index",
"pages/index/around",
"pages/logs/logs",
"pages/rout