一、问题产生
问题产生很简单。公司要做一个需求,定位飞行功能,即,点击定位,获取经纬度,一个飞行的动画,飞到目标点和层级。拿到这个需求的时候,心里想,这简单啊。直接查OpenLayer API.找到了这样的一个函数:
view.animate(var_args)
于是手气刀落,敲下了这样一段代码:
//获取目标定位点等函数省略
this.map.getView().animate({
center: center, zoom: 13}) // 同时移动和放大
顺带提一下。注意参数不同,表现也是不同的。
同时配置了center和zoom
,那么表现就是同时移动和放大。先配置center,再配置zoom
,那么表现就是先平移,再缩放反之,先配置zoom,再配置center
,表现是先缩放,再平移
问题来了:
没错。跨地区过快的平移速度会导致瓦片加载跟不上屏幕移动的速度,用户体验很差。
二、问题分析:
可以采用先拉伸视角,再平移,再缩放视角的方式解决。于是我们就有了这样一段代码:
this.map.getView().animate({
zoom: 7,duration:2000}, {
center: center,duration:2000},{
zoom:13,duration:3000});
吼吼。对于跨地区的场景表现确实很完美。
然而。对于本身就在附近的定位点,定位起来确很恶心。
对。即使在附近,也要拉伸再缩放一下。这感觉真是太难受了。。。
三、问题解决
直接上代码。同样的处理流程,不过做了优化算法。封装了一个smooth_animate
函数,核心是内部判断当前中心点和目标中心点之间的距离,再通过设定瓦片加载速度,计算出一个拉伸的zoom,在不影响瓦片加载的情况下进行平移操作。开放了3个配置项.
load_speed
:4, //–一秒钟可以加载几张瓦片,服务器越好,这个值可以越大move_time_limit
:3, //–平移时间必须在3s以内level_change_time
: 200 //–拉升一级(缩放一级)需要多少毫秒
import * as turf from '@turf/turf';
/**
* 地图视角由一个地方缓缓转向另外一个地方.
* 主要原理是计算瓦片距离,控制拉升到某个层级进行平移,达到瓦片能够在这个层级平移的时候,加载速度跟得上平移速度
* @param {ol.Map} map
* @param {Array[lon,lat]} target_location 目标的经纬度
* @param {Number} target_zoom 目标的层级
* @param {Object} options
* @author daiyujie
* load_speed:4, //--一秒钟可以加载几张瓦片
* move_time_limit:3, //--平移时间必须在3s以内
* level_change_time: 200 //--拉升一级(缩放一级)需要多少毫秒
*/
export const smooth_animate = function(map,target_location,target_zoom,options){
var defaults = {
load_speed:4, //--一秒钟可以加载几张瓦片
move_time_limit:3, //--平移时间必须在3s以内
level_change_time: 200 //--拉升一级(缩放一级)需要多少毫秒
}
var opts = Object.assign({
}, defaults, options || {
});
var view = map.getView();
var cur_zoom = view.getZoom();
var cur_center= view.getCenter();
var min_zoom = Math.min(cur_zoom,target_zoom);
var distance = turf.distance(cur_center,target_location);
var pull_up_zoom = min_zoom;
var max_tile_num = opts.load_speed * opts.move_time_limit;
//--核心逻辑在这里
while(view.getResolutionForZoom(pull_up_zoom)*256*max_tile_num*100 < distance){
pull_up_zoom--;
}
var pull_up_subs_zoom = cur_zoom - pull_up_zoom;
var duration_pull_up = pull_up_subs_zoom * opts.level_change_time;
var pull_down_subs_zoom = target_zoom - pull_up_zoom;
var duration_pull_down = pull_down_subs_zoom * opts.level_change_time;
view.animate(
{
zoom:pull_up_zoom,
duration:duration_pull_up
},
{
center: target_location,
duration:opts.move_time_limit*1000
},
{
zoom:target_zoom,
duration:duration_pull_down
},
)
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
},duration_pull_down+duration_pull_up+opts.move_time_limit*1000 + 100)
})
}
使用上面函数之后,对于同级别距离的地点转移,不会再进行先拉升后缩放的操作,因为计算出来的拉升的zoom就等于target_zoom。