1. 问题重现:那个“不听话”的include-points属性
如果你正在用uniapp开发一个带地图功能的App,比如做个门店展示、物流轨迹或者活动地点导航,那你大概率用过或者想用<map>组件。这个组件有个听起来特别省心的属性叫include-points。按照官方文档的说法,你只要把一个包含所有坐标点的数组传给它,地图就会自动调整缩放级别和中心位置,把所有的点都“装”进屏幕视野里。这功能想想就美,不用自己算来算去,一行属性搞定视野适配。
理想很丰满,但现实是,很多开发者在App端(尤其是安卓和iOS原生渲染环境下)实际一用,就发现不对劲了。代码明明写了,数据也传了,可地图就是“纹丝不动”,视野范围压根没变,还是默认的级别和中心点。我刚开始也以为是自己的代码写错了,反复检查points数组的格式,确认纬度、经度都没问题,但地图视图就是不给面子。
后来去社区一看,好家伙,原来我不是一个人。从HBuilderX 2.8.3到更新的版本,这个问题在App端一直存在,而且相当普遍。官方示例跑起来有时候也不灵光,这就很尴尬了。include-points属性在微信小程序端可能工作正常,但一到需要打包成原生App的场景,它就“失效”了。这直接导致我们想实现的那个“一键展示所有地点”的核心用户体验打了水漂。
所以,我们面临的核心问题很明确:在uniapp的App开发中,<map>组件的include-points属性不可靠,无法实现自动调整视野以包含所有标记点的功能。 我们不能干等着官方修复(虽然希望他们尽快),业务还得继续,功能必须得上。这就需要我们抛开这个“失效”的属性,自己动手,实现一套逻辑上等效的替代方案。
2. 思路转换:从依赖属性到自主计算
既然现成的“自动挡”挂了,那我们只能切回“手动挡”。include-points属性的本质是帮我们做了两件事:1. 根据一组坐标点,计算出一个能涵盖它们所有点的地理范围;2. 将这个范围转换为地图的缩放级别(scale)和中心点坐标(latitude, longitude),并设置给地图组件。
现在属性失效,意味着这两步计算都得我们自己来。中心点相对好办,我们可以取所有坐标点的平均值,或者直接取第一个点作为初始中心。真正的难点和核心在于如何根据一组分散的坐标点,计算出一个合适的地图缩放级别(scale)。 地图的scale值是个数字,比如3、5、10、16,数字越小,地图显示的范围越大(缩放级别越小)。我们需要找到一个scale值,使得在这个缩放级别下,离中心点最远的那个坐标点,仍然能在地图视野区域内。
这就转化成了一个数学和地理计算问题:我们需要计算所有坐标点中,离我们设定的中心点最远的那一个的距离。然后,根据这个最远距离,反推出一个合适的地图scale值。简单来说,距离越远,需要的视野范围就越大,对应的scale值就应该越小(因为scale值小,视野范围大)。理解了这个逻辑,我们就知道该怎么动手了。
3. 核心实现:计算最远点距离与动态Scale
3.1 第一步:计算两点间的地理距离
这是整个方案的地基。地图上的坐标是经纬度,计算它们之间的直线距离不能简单地用勾股定理,因为地球是个球体(近似)。这里我们需要用到球面三角学中的半正矢公式。别被名字吓到,它的目的就是根据两点的经纬度,算出它们在地球球面上的最短弧长距离。
我直接给出一个在JavaScript里封装好的函数,这也是经过实践验证比较可靠的:
// 地球半径,单位米
const EARTH_RADIUS = 6378137.0;
const PI = Math.PI;
// 将角度转换为弧度
function getRad(d) {
return d * PI / 180.0;
}
/**
* 根据半正矢公式计算两个经纬度坐标间的距离
* @param {Array} coord1 - 坐标1,格式 [纬度, 经度]
* @param {Array} coord2 - 坐标2,格式 [纬度, 经度]
* @returns {Number} 距离,单位米
*/
function getGreatCircleDistance(coord1, coord2) {
const lat1 = coord1[0];
const lng1 = coord1[1];
const lat2 = coord2[0];
const lng2 = coord2[1];
const radLat1 = getRad(lat1);
const radLat2 = getRad(lat2);
const a = radLat1 - radLat2;
const b = getRad(lng1) - getRad(lng2);
let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2), 2) +
Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b/2), 2)));
s = s * EARTH_RADIUS;
s = Math.round(s * 10000) / 10000.0; // 保留四位小数
return s;
}
这个getGreatCircleDistance函数就是


2273

被折叠的 条评论
为什么被折叠?



