因项目需要,在uniapp中集成使用腾讯地图,为了方便维护,希望通过一套代码实现H5和APP同时可用。H5显示相对简单,APP端比较麻烦,记录下实现过程
一、集成步骤
1.使用 renderjs
script标签使用renderjs,因为JavaScript Api需要调用DOM对象,APP需要使用renderjs技术,保证script运行在webview环境,才能调用DOM对象。
<script lang="renderjs" module="test">
</script>
2.引用地图script
导入腾讯地图JS脚本,因为腾讯地图js不是按照uniapp格式编写,所以不能直接import导入,需要包装成一个js插件,使用 Promise,js加载成功,调用resolve,js加载失败,调用reject。
创建loadJs.js 文件
function loadJs(src) {
return new Promise((resolve,reject)=>{
let script = document.createElement('script');
script.type = "text/javascript";
script.src= src;
document.body.appendChild(script);
script.onload = ()=>{
resolve();
}
script.onerror = ()=>{
reject();
}
}).catch((e) => {})
}
export default loadJs
在页面中使用
<script lang="renderjs" module="test">
import loadJs from "../../common/loadJs.js"
export default {
data() {
return {
}
},
mounted(){
console.log('renderjs初始化完毕')
loadJs('https://map.qq.com/api/gljs?v=1.exp&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77').then(()=>{
// 加载成功,进行后续操作
})
},
methods: {
}
}
</script>
3.修改js,兼容uniapp
下载腾讯官网的例子,改造成uniapp兼容的格式。有两种方式,
方式一:
将官网例子中 script 封装成一个个的function,定义在 vue文件的 methods 中,这样就可以直接调用。
方式二:
将官网例子中 script 代码全部拷贝到一个js文件中,再把需要调用的 function 通过 export 关键字导出,在vue文件中进行 import 调用。
4.修改监听事件
腾讯官网例子都是web端的,点击事件都是click,H5端运行需要改成touchend,否则点击无响应
Web端
svg.addEventListener('click', this.onClick); web端用click事件
H5端
svg.addEventListener('touchend', this.onClick); // H5端用touchend事件
二、示例
腾讯官网例子
以自定义覆盖物 -> DOMOverlay 为例,实操下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>DOMOverlay</title>
</head>
<script charset="utf-8" src="https://map.qq.com/api/gljs?libraries=tools&v=1.exp&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77"></script>
<style type="text/css">
html,
body {
height: 100%;
margin: 0px;
padding: 0px;
}
#container {
width: 100%;
height: 100%;
}
</style>
<body onload="initMap()">
<div id="container"></div>
<script>
var SVG_NS = 'http://www.w3.org/2000/svg';
// 自定义环状饼图 - 继承DOMOverlay
function Donut(options) {
TMap.DOMOverlay.call(this, options);
}
Donut.prototype = new TMap.DOMOverlay();
// 初始化
Donut.prototype.onInit = function(options) {
this.position = options.position;
this.data = options.data;
this.minRadius = options.minRadius || 0;
this.maxRadius = options.maxRadius || 50;
};
// 销毁时需解绑事件监听
Donut.prototype.onDestroy = function() {
if (this.onClick) {
this.dom.removeEventListener(this.onClick);
}
};
// 创建DOM元素,返回一个DOMElement,使用this.dom可以获取到这个元素
Donut.prototype.createDOM = function() {
let svg = document.createElementNS(SVG_NS, 'svg');
svg.setAttribute('version', '1.1');
svg.setAttribute('baseProfile', 'full');
let r = this.maxRadius;
svg.setAttribute('viewBox', [-r, -r, r * 2, r * 2].join(' '));
svg.setAttribute('width', r * 2);
svg.setAttribute('height', r * 2);
svg.style.cssText = 'position:absolute;top:0px;left:0px;';
let donut = createDonut(this.data, this.minRadius, this.maxRadius);
svg.appendChild(donut);
// click事件监听
this.onClick = () => {
// DOMOverlay继承自EventEmitter,可以使用emit触发事件
this.emit('click');
};
// pc端注册click事件,移动端注册touchend事件
svg.addEventListener('click', this.onClick);
return svg;
};
// 更新DOM元素,在地图移动/缩放后执行
Donut.prototype.updateDOM = function() {
if (!this.map) {
return;
}
// 经纬度坐标转容器像素坐标
let pixel = this.map.projectToContainer(this.position);
// 使饼图中心点对齐经纬度坐标点
let left = pixel.getX() - this.dom.clientWidth / 2 + 'px';
let top = pixel.getY() - this.dom.clientHeight / 2 + 'px';
this.dom.style.transform = `translate(${left}, ${top})`;
};
// 使用SVG创建环状饼图
function createDonut(data, minRadius, maxRadius) {
const colorList = [
'#7AF4FF',
'#67D7FF',
'#52B5FF',
'#295BFF'
];
let sum = data.reduce((prev, curr) => prev + curr, 0);
let angle = 0;
let group = document.createElementNS(SVG_NS, "g");
data.forEach((d, i) => {
let delta = d / sum * Math.PI * 2;
color = colorList[i],
r = maxRadius,
startAngle = angle,
endAngle = angle + delta;
angle += delta;
// 对每个数据创建一个扇形
let fanShape = document.createElementNS(SVG_NS, 'path');
fanShape.setAttribute('style', `fill: ${color};`);
fanShape.setAttribute('d', [
'M0 0',
`L${r * Math.sin(startAngle)} ${-r * Math.cos(startAngle)}`,
`A${r} ${r} 0 ${delta > Math.PI ? 1 : 0} 1 ${r * Math.sin(endAngle)} ${-r * Math.cos(endAngle)}`,
].join(' ') + ' z');
group.appendChild(fanShape);
});
// 在中心创建一个圆形
let circleShape = document.createElementNS(SVG_NS, 'circle');
circleShape.setAttribute('style', 'fill: #FFFFFF');
circleShape.setAttribute('cx', 0);
circleShape.setAttribute('cy', 0);
circleShape.setAttribute('r', minRadius);
group.appendChild(circleShape);
// 绘制文字
let textShape = document.createElementNS(SVG_NS, 'text');
textShape.setAttribute('x', 0);
textShape.setAttribute('y', '0.3em');
textShape.setAttribute('text-anchor', 'middle');
textShape.innerHTML = sum;
group.appendChild(textShape);
return group;
}
window.Donut = Donut;
</script>
<script type="text/javascript">
var map;
function initMap() {
// 初始化地图
map = new TMap.Map("container", {
zoom:12, // 设置地图缩放级别
center: new TMap.LatLng(39.984104, 116.307503) // 设置地图中心点坐标
});
let donutList = [
new Donut({
map,
position: new TMap.LatLng(39.96030543872138, 116.25809083213608),
data: [12, 24],
minRadius: 13,
maxRadius: 20
}),
new Donut({
map,
position: new TMap.LatLng(39.9986945980902, 116.33598362780685),
data: [23, 99, 101, 400],
minRadius: 25,
maxRadius: 35
}),
new Donut({
map,
position: new TMap.LatLng(40.02906301748584, 116.25499991104516),
data: [18, 41, 50],
minRadius: 20,
maxRadius: 28
})
];
donutList.forEach((donut, index) => {
donut.on('click', () => {
console.log(`第${index}个环形图被点击,位置为${donut.position}`);
});
});
}
</script>
</body>
</html>
适配后uniapp代码
主要三个文件 DOMOverlay.js、loadJs.js、map.vue
DOMOverlay.js
一般来说先把script 全部复制到一个单独js文件,然后直接运行,运气好直接正常使用,运气不好就哪里报错改哪里,DOMOverlay.js文件修改过我都加了注释“适配uniapp修改过的”
var SVG_NS = 'http://www.w3.org/2000/svg';
// 自定义环状饼图 - 继承DOMOverlay
function Donut(options) {
TMap.DOMOverlay.call(this, options);
}
/**
* ----------------适配uniapp修改过的----------------
*
* 因为 TMap 对象依赖于 https://map.qq.com/api/gljs?v=1.exp&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77
* 所以要封装一个方法,在加载完依赖脚本后,再运行
*/
function initDonut(){
Donut.prototype = new TMap.DOMOverlay();
// 初始化
Donut.prototype.onInit = function(options) {
this.position = options.position;
this.data = options.data;
this.minRadius = options.minRadius || 0;
this.maxRadius = options.maxRadius || 50;
};
// 销毁时需解绑事件监听
Donut.prototype.onDestroy = function() {
if (this.onClick) {
this.dom.removeEventListener(this.onClick);
}
};
// 创建DOM元素,返回一个DOMElement,使用this.dom可以获取到这个元素
Donut.prototype.createDOM = function() {
let svg = document.createElementNS(SVG_NS, 'svg');
svg.setAttribute('version', '1.1');
svg.setAttribute('baseProfile', 'full');
let r = this.maxRadius;
svg.setAttribute('viewBox', [-r, -r, r * 2, r * 2].join(' '));
svg.setAttribute('width', r * 2);
svg.setAttribute('height', r * 2);
svg.style.cssText = 'position:absolute;top:0px;left:0px;';
let donut = createDonut(this.data, this.minRadius, this.maxRadius);
svg.appendChild(donut);
// click事件监听
this.onClick = () => {
// DOMOverlay继承自EventEmitter,可以使用emit触发事件
this.emit('click');
};
// ----------------适配uniapp修改过的----------------
// pc端注册click事件,移动端注册touchend事件
// svg.addEventListener('click', this.onClick); web端用click事件
svg.addEventListener('touchend', this.onClick); // H5端用touchend事件
return svg;
};
// 更新DOM元素,在地图移动/缩放后执行
Donut.prototype.updateDOM = function() {
if (!this.map) {
return;
}
// 经纬度坐标转容器像素坐标
let pixel = this.map.projectToContainer(this.position);
// 使饼图中心点对齐经纬度坐标点
let left = pixel.getX() - this.dom.clientWidth / 2 + 'px';
let top = pixel.getY() - this.dom.clientHeight / 2 + 'px';
this.dom.style.transform = `translate(${left}, ${top})`;
};
}
// 使用SVG创建环状饼图
function createDonut(data, minRadius, maxRadius) {
const colorList = [
'#7AF4FF',
'#67D7FF',
'#52B5FF',
'#295BFF'
];
let sum = data.reduce((prev, curr) => prev + curr, 0);
let angle = 0;
let group = document.createElementNS(SVG_NS, "g");
data.forEach((d, i) => {
let delta = d / sum * Math.PI * 2;
let color = colorList[i],
r = maxRadius,
startAngle = angle,
endAngle = angle + delta;
angle += delta;
// 对每个数据创建一个扇形
let fanShape = document.createElementNS(SVG_NS, 'path');
fanShape.setAttribute('style', `fill: ${color};`);
fanShape.setAttribute('d', [
'M0 0',
`L${r * Math.sin(startAngle)} ${-r * Math.cos(startAngle)}`,
`A${r} ${r} 0 ${delta > Math.PI ? 1 : 0} 1 ${r * Math.sin(endAngle)} ${-r * Math.cos(endAngle)}`,
].join(' ') + ' z');
group.appendChild(fanShape);
});
// 在中心创建一个圆形
let circleShape = document.createElementNS(SVG_NS, 'circle');
circleShape.setAttribute('style', 'fill: #FFFFFF');
circleShape.setAttribute('cx', 0);
circleShape.setAttribute('cy', 0);
circleShape.setAttribute('r', minRadius);
group.appendChild(circleShape);
// 绘制文字
let textShape = document.createElementNS(SVG_NS, 'text');
textShape.setAttribute('x', 0);
textShape.setAttribute('y', '0.3em');
textShape.setAttribute('text-anchor', 'middle');
textShape.innerHTML = sum;
group.appendChild(textShape);
return group;
}
window.Donut = Donut;
var map;
function initMap() {
// ----------------适配uniapp修改过的----------------
// 调用封装后的initDount()
initDonut()
// 初始化地图
map = new TMap.Map("mapContainer", {
zoom:12, // 设置地图缩放级别
center: new TMap.LatLng(39.984104, 116.307503) // 设置地图中心点坐标
});
let donutList = [
new Donut({
map,
position: new TMap.LatLng(39.96030543872138, 116.25809083213608),
data: [12, 24],
minRadius: 13,
maxRadius: 20
}),
new Donut({
map,
position: new TMap.LatLng(39.9986945980902, 116.33598362780685),
data: [23, 99, 101, 400],
minRadius: 25,
maxRadius: 35
}),
new Donut({
map,
position: new TMap.LatLng(40.02906301748584, 116.25499991104516),
data: [18, 41, 50],
minRadius: 20,
maxRadius: 28
})
];
donutList.forEach((donut, index) => {
donut.on('click', () => {
console.log(`第${index}个环形图被点击,位置为${donut.position}`);
alert(`第${index}个环形图被点击,位置为${donut.position}`)
});
});
}
/**
* ----------------适配uniapp修改过的----------------
* 导出initMap方法
*/
export {
initMap
}
loadJs.js
用来加载第三方js
function loadJs(src) {
return new Promise((resolve,reject)=>{
let script = document.createElement('script');
script.type = "text/javascript";
script.src= src;
document.body.appendChild(script);
script.onload = ()=>{
resolve();
}
script.onerror = ()=>{
reject();
}
}).catch((e) => {})
}
export default loadJs
map.vue
<template>
<view id="mapContainer" style="height: 100%;" @click="log">
</view>
</template>
<script>
export default {
data() {
return {
}
},
mounted() {
}
}
</script>
<script lang="renderjs" module="test">
import loadJs from "../../common/loadJs.js"
// 导入适配后的 DOMOverlay.js
import {initMap} from "../../common/DOMOverlay.js"
export default {
data() {
return {
}
},
mounted(){
console.log("mounted")
loadJs('https://map.qq.com/api/gljs?v=1.exp&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77').then(()=>{
// 调用初始化地图方法
initMap()
})
},
methods: {
},
created() {
}
}
</script>
<style>
</style>
三、适配效果
APP运行效果图,与官网一致