下面是优化过度的效果
高德镜头效果-center切换镜头抬起
可以看到如果两个点距离很近的话则正常过度,如果较远则抬起镜头过度,更符合观感,废话不多说。
主要思路
首先来分析抬起镜头的逻辑判断是什么,实际的米数?显然不是,如果在一个很小的zoom的情况下两点都在屏幕内的话,距离就不管用了,所以这里我觉得采用两点的像素距比较合适,像素距离也就是在屏幕内距离是多少像素,并且是否同时在屏幕内,满足这两个条件触发抬起镜头才合适,
所以这里设定 :
let x = 屏幕宽度 , if 两点的像素距离 > x && 两点不同时在屏幕内 , 触发。
好的,知道逻辑那就开干。
1、获取点信息,获取屏幕宽度,将经纬度转为像素坐标和是否在包围盒(屏幕)内
const cameraMoveTarget = (position) => {
loca.animate.start();
const curZoom = map.getZoom();
const curPitch = map.getPitch();
const curRotation = map.getRotation();
const curCenter = [map.getCenter().lng, map.getCenter().lat];
const targZoom = 17;
const targPitch = 0;
const targRotation = 0;
const targCenter = position;//目标点位
const point1 = curCenter; //当前地图的中心点
const point2 = targCenter; // 需要跳过去的点位
// 获取地图容器尺寸
const container = map.getContainer();
const screenWidth = container.clientWidth;
// 转换地理坐标为像素坐标
const pixelPoint1 = map.lngLatToContainer(new AMap.LngLat(point1[0], point1[1]));
const pixelPoint2 = map.lngLatToContainer(new AMap.LngLat(point2[0], point2[1]));
// 计算像素距离
const pixelDistance = Math.sqrt(Math.pow(pixelPoint1.getX() - pixelPoint2.getX(), 2) + Math.pow(pixelPoint1.getY() - pixelPoint2.getY(), 2));
// 判断点是否在屏幕内
const isPoint1InView = map.getBounds().contains(new AMap.LngLat(point1[0], point1[1]));
const isPoint2InView = map.getBounds().contains(new AMap.LngLat(point2[0], point2[1]));
}
2、通过线性插值计算新的缩放级别
maxZoomOutLevel是自己调整的,想让镜头抬起的幅度多大
function calculateLiftZoom(pixelDistance, screenWidth, curZoom) {
// 假设最小和最大抬起缩放级别差异
const maxZoomOutLevel = 3; // 可根据需要调整
const minZoomOutLevel = 0;
// 计算像素距离与屏幕宽度的比例
let distanceRatio = pixelDistance / screenWidth;
distanceRatio = Math.min(Math.max(distanceRatio, 0), 1);
// 通过线性插值计算新的缩放级别
const liftZoom = curZoom - (maxZoomOutLevel - minZoomOutLevel) * distanceRatio;
return liftZoom;
}
3、使用loca镜头控制器控制视角
//loca是加载好了的
await AMapLoader.load({
key: AMAP_KEY,
version: AMAP_VERSION,
Loca: {
version: "2.0.0",
},
plugins: AMAP_PLUGINS.concat(["Loca"]),
}).then((AMap) => {
instance = AMap;
map = new instance.Map(mapRef.value, {
...AMAP_DEFAULT_THEME,
zoom: 14,
showLabel: false,
pitch: 0,
center: [117.260355, 31.875092],
viewMode: "2D",
skyColor: "#0D121F",
});
// 创建 Loca 容器
loca = new Loca.Container({
map: map,
});
});
};
//主要代码
const cameraMoveTarget = (position) => {
loca.animate.start();
const curZoom = map.getZoom();
const curPitch = map.getPitch();
const curRotation = map.getRotation();
const curCenter = [map.getCenter().lng, map.getCenter().lat];
const targZoom = 17;
const targPitch = 0;
const targRotation = 0;
const targCenter = position;
const point1 = curCenter;
const point2 = targCenter;
// 获取地图容器尺寸
const container = map.getContainer();
const screenWidth = container.clientWidth;
// 转换地理坐标为像素坐标
const pixelPoint1 = map.lngLatToContainer(new AMap.LngLat(point1[0], point1[1]));
const pixelPoint2 = map.lngLatToContainer(new AMap.LngLat(point2[0], point2[1]));
// 计算像素距离
const pixelDistance = Math.sqrt(Math.pow(pixelPoint1.getX() - pixelPoint2.getX(), 2) + Math.pow(pixelPoint1.getY() - pixelPoint2.getY(), 2));
// 判断点是否在屏幕内
const isPoint1InView = map.getBounds().contains(new AMap.LngLat(point1[0], point1[1]));
const isPoint2InView = map.getBounds().contains(new AMap.LngLat(point2[0], point2[1]));
// 决定是否抬起镜头
let newZoom = targZoom;
let zoomControl;
let timingCurve;
let zoomDuration;
let centerDuration;
let centerCurve;
if (pixelDistance > screenWidth && !(isPoint1InView && isPoint2InView)) {
newZoom = calculateLiftZoom(pixelDistance, screenWidth, curZoom);
zoomControl = [
[0, curZoom],
[0.5, newZoom],
[1, targZoom],
];
timingCurve = [0.0, 0.52, 0.315, 1.0];
zoomDuration = 3000;
centerDuration = 2000;
centerCurve = [0.15, 0.39, 0.315, 1];
console.log("抬起");
} else {
zoomControl = [
[0, curZoom],
[1, targZoom],
];
timingCurve = [0.13, 0.31, 0.105, 1];
zoomDuration = 1500;
centerDuration = 1500;
centerCurve = [0.13, 0.31, 0.105, 1];
}
const route = [
{
pitch: {
value: targPitch,
duration: 1000,
control: [
[0, curPitch],
[1, targPitch],
],
timing: [0.23, 1.0, 0.32, 1.0],
},
zoom: {
value: targZoom,
duration: 2000,
control: zoomControl,
timing: timingCurve,
},
rotation: {
value: targRotation,
duration: 1000,
control: [
[0, curRotation],
[1, targRotation],
],
timing: [0.23, 1.0, 0.32, 1.0],
},
center: {
value: targCenter,
duration: centerDuration,
control: [curCenter, targCenter],
timing: centerCurve,
},
},
];
loca.viewControl.addAnimates(route, () => {
console.log("动画结束");
});
};
function calculateLiftZoom(pixelDistance, screenWidth, curZoom) {
// 假设最小和最大抬起缩放级别差异
const maxZoomOutLevel = 3; // 可根据需要调整
const minZoomOutLevel = 0;
// 计算像素距离与屏幕宽度的比例
let distanceRatio = pixelDistance / screenWidth;
distanceRatio = Math.min(Math.max(distanceRatio, 0), 1);
// 通过线性插值计算新的缩放级别
const liftZoom = curZoom - (maxZoomOutLevel - minZoomOutLevel) * distanceRatio;
return liftZoom;
}
ok完成,不懂得评论区问就行,我尽量回答