Maps JavaScript API 提供了两种不同的地图实现方式:光栅和矢量。光栅地图会将地图加载为基于像素的光栅图片图块网格,这些图块由 Google Maps Platform 服务器端生成,然后提供给您的 Web 应用。矢量地图由基于矢量的图块组成,这些图块在加载时使用 WebGL 在客户端绘制,WebGL 是一种 Web 技术,可让浏览器访问用户设备上的 GPU 以渲染 2D 和 3D 图形。
用户熟悉的 Google 地图就是矢量地图,与默认的光栅图块地图相比优势众多,最值得注意的是基于矢量的图像的锐度,以及在较高缩放级别添加了 3D 建筑。矢量地图支持以下功能:
- 以程序化方式控制倾斜度和朝向
- 增强型相机控制
- 小数缩放,缩放更流畅
对于使用
div
元素和 JavaScript 加载的地图,默认渲染类型为google.maps.RenderingType.RASTER
。对于使用
gmp-map
元素加载的地图,默认渲染类型为google.maps.RenderingType.VECTOR
,并启用了倾斜度和航向控制。
倾斜和旋转
您可以在初始化矢量地图时加入 heading
和 tilt
属性,并在地图上调用 setTilt
和 setHeading
方法,从而设置地图的倾斜度和旋转度(朝向)。以下示例将向地图添加一些按钮,这些按钮能够以程序化方式按 20 度的增量调整倾斜度和朝向。
TypeScript
function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { center: { lat: 37.7893719, lng: -122.3942, }, zoom: 16, heading: 320, tilt: 47.5, mapId: "90f87356969d889c", } ); const buttons: [string, string, number, google.maps.ControlPosition][] = [ ["Rotate Left", "rotate", 20, google.maps.ControlPosition.LEFT_CENTER], ["Rotate Right", "rotate", -20, google.maps.ControlPosition.RIGHT_CENTER], ["Tilt Down", "tilt", 20, google.maps.ControlPosition.TOP_CENTER], ["Tilt Up", "tilt", -20, google.maps.ControlPosition.BOTTOM_CENTER], ]; buttons.forEach(([text, mode, amount, position]) => { const controlDiv = document.createElement("div"); const controlUI = document.createElement("button"); controlUI.classList.add("ui-button"); controlUI.innerText = `${text}`; controlUI.addEventListener("click", () => { adjustMap(mode, amount); }); controlDiv.appendChild(controlUI); map.controls[position].push(controlDiv); }); const adjustMap = function (mode: string, amount: number) { switch (mode) { case "tilt": map.setTilt(map.getTilt()! + amount); break; case "rotate": map.setHeading(map.getHeading()! + amount); break; default: break; } }; } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
function initMap() { const map = new google.maps.Map(document.getElementById("map"), { center: { lat: 37.7893719, lng: -122.3942, }, zoom: 16, heading: 320, tilt: 47.5, mapId: "90f87356969d889c", }); const buttons = [ ["Rotate Left", "rotate", 20, google.maps.ControlPosition.LEFT_CENTER], ["Rotate Right", "rotate", -20, google.maps.ControlPosition.RIGHT_CENTER], ["Tilt Down", "tilt", 20, google.maps.ControlPosition.TOP_CENTER], ["Tilt Up", "tilt", -20, google.maps.ControlPosition.BOTTOM_CENTER], ]; buttons.forEach(([text, mode, amount, position]) => { const controlDiv = document.createElement("div"); const controlUI = document.createElement("button"); controlUI.classList.add("ui-button"); controlUI.innerText = `${text}`; controlUI.addEventListener("click", () => { adjustMap(mode, amount); }); controlDiv.appendChild(controlUI); map.controls[position].push(controlDiv); }); const adjustMap = function (mode, amount) { switch (mode) { case "tilt": map.setTilt(map.getTilt() + amount); break; case "rotate": map.setHeading(map.getHeading() + amount); break; default: break; } }; } window.initMap = initMap;
CSS
/* * Always set the map height explicitly to define the size of the div element * that contains the map. */ #map { height: 100%; } /* * Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } .ui-button { background-color: #fff; border: 0; border-radius: 2px; box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3); margin: 10px; padding: 0 0.5em; font: 400 18px Roboto, Arial, sans-serif; overflow: hidden; height: 40px; cursor: pointer; } .ui-button:hover { background: rgb(235, 235, 235); }
HTML
<html> <head> <title>Tilt and Rotation</title> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="map"></div> <!-- The `defer` attribute causes the script to execute after the full HTML document has been parsed. For non-blocking uses, avoiding race conditions, and consistent behavior across browsers, consider loading using Promises. See https://developers.google.com/maps/documentation/javascript/load-maps-js-api for more information. --> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly" defer ></script> </body> </html>
试用示例
使用鼠标和键盘手势
如果已启用倾斜和旋转(航向)用户互动(通过编程或在 Google Cloud 控制台中),用户可以使用鼠标和键盘调整倾斜和旋转:
- 使用鼠标,按住 Shift 键,然后按住鼠标左键并上下拖动调整倾斜度,左右拖动调整朝向。
- 使用键盘,按住 Shift 键,然后使用向上键和向下键调整倾斜度,使用向右键和向左键调整朝向。
以程序化方式调整倾斜度和朝向
使用 setTilt()
和 setHeading()
方法,能够以程序化方式调整矢量地图的倾斜度和朝向。朝向是指镜头面向的方向与正北方形成的顺时针角度,因此 map.setHeading(90)
会旋转地图,使东方成为向上的方向。倾斜角度从顶部测量,因此 map.setTilt(0)
代表垂直俯视,而 map.setTilt(45)
将产生倾斜视图。
- 调用
setTilt()
可设置地图的倾斜角度。使用getTilt()
可获取当前倾斜度值。 - 调用
setHeading()
可设置地图的朝向。使用getHeading()
可获取当前朝向值。
要更改地图中心,同时保持倾斜度和朝向不变,请使用 map.setCenter()
或 map.panBy()
。
请注意,可以使用的角度范围因当前的缩放级别而异。系统将把超出此范围的值强制转化到当前允许的范围内。
您还可以使用 moveCamera
方法,以程序化方式更改朝向、倾斜度、中心和缩放级别。了解详情。
对其他方法的影响
对地图应用倾斜或旋转后,其他 Maps JavaScript API 方法的行为会受到影响:
map.getBounds()
会始终返回包含可见区域的最小边界框;应用倾斜后,返回的边界所代表的区域可能大于地图视口的可见区域。map.fitBounds()
会先将倾斜度和朝向重置为零,然后再拟合边界。map.panToBounds()
会先将倾斜度和朝向重置为零,然后再平移边界。map.setTilt()
接受任何值,但会根据当前地图缩放级别限制最大倾斜度。map.setHeading()
接受任何值,并会对相应值进行修改,使其适合 [0, 360] 的范围。
控制镜头
使用 map.moveCamera()
函数一次更新镜头属性的任意组合。map.moveCamera()
接受包含要更新的所有镜头属性的单个参数。以下示例展示了如何调用 map.moveCamera()
以同时设置 center
、zoom
、heading
和 tilt
:
map.moveCamera({
center: new google.maps.LatLng(37.7893719, -122.3942),
zoom: 16,
heading: 320,
tilt: 47.5
});
您可以调用 map.moveCamera()
以动画方式呈现镜头属性,并设置动画循环,如下所示:
const degreesPerSecond = 3;
function animateCamera(time) {
// Update the heading, leave everything else as-is.
map.moveCamera({
heading: (time / 1000) * degreesPerSecond
});
requestAnimationFrame(animateCamera);
}
// Start the animation.
requestAnimationFrame(animateCamera);
相机位置
地图视图被建模成一个俯视某个平面的镜头。下列属性指定了镜头的位置(也进而决定了地图的渲染效果):目标(纬度/经度位置)、方向角、倾斜度和缩放级别。
目标(位置)
镜头目标是地图中心的位置,以纬度和经度坐标形式指定。
纬度可以介于 -85 度(含)和 85 度(含)之间。不在此范围内的值会被调整为此范围内最接近的值。例如,如果将纬度指定为 100,系统会将该值设为 85。经度范围为 -180 度(含)到 180 度(含)。不在此范围内的值会被换算为 (-180, 180) 范围内的值。例如,480、840 和 1200 都会换算为 120 度。方向角(方向)
镜头方向角指定了罗盘方向,以相对于正北方(对应于地图的顶部边缘)的角度来表示。从地图中心到顶部边缘画一条垂直线,镜头朝向相对于正北方的角度(以度为单位)即为方向角。
方向角为 0 表示地图顶部指向正北。方向角为 90 表示地图顶部指向正东方(罗盘上为 90 度)。方向角为 180 表示地图顶部指向正南方。
您可以使用 Maps API 更改地图的方向角。例如,驾车的用户通常会旋转道路地图使其与他们的行驶方向一致,而远足者使用地图和罗盘时通常会调整地图方向,使地图上有一条垂直线指向北方。
倾斜度(视角)
倾斜度是指镜头在地图中心位置正上方圆弧上所处的位置,以相对于天底(相机正下方)的角度来表示。值为 0 时,镜头竖直朝下。值大于 0 时,镜头按指定角度朝地平线倾斜。更改视角时,地图会以透视法呈现:远处的地图项显示得较小,而近处的地图项显示得较大。以下插图展示了这种情况。
在下面的图片中,视角为 0 度。第一张图片是视角为 0 度时的示意图;位置 1 是镜头位置,位置 2 是当前的地图位置。生成的地图如下所示。
以下图片中的视角为 45 度。请注意,镜头沿圆弧移到了正上(0 度)与地面(90 度)中间的位置,也就是位置 3。镜头仍指向地图的中心点,但现在位置 4 处的线表示的区域会显示出来。
此屏幕截图中的地图仍与原始地图采用同一个中心点,但地图顶部显示的地图项增加了。当您将角度增加到 45 度以上时,镜头和地图位置之间的地图项会按比例显示得较大,而地图位置远处的地图项会按比例显示得较小,从而产生三维效果。
缩放级别
镜头的缩放级别决定了地图的比例。缩放级别越大,屏幕上显示的信息越详细;缩放级别越小,屏幕上显示的世界范围越广。
缩放级别的值不必是整数。地图允许的缩放级别范围取决于多个因素,包括目标、地图类型和屏幕尺寸。超出该范围的任何数值都将转换为最接近的有效值(可以是最小缩放级别或最大缩放级别)。以下列表显示了您在每个缩放级别看到的地图的大致详细程度:
- 1:世界
- 5:大陆/洲
- 10:城市
- 15:街道
- 20:建筑物
小数缩放
矢量地图支持小数缩放,可让您使用小数值(而非整数)进行缩放。虽然光栅地图和矢量地图都支持小数缩放,但对于矢量地图,该功能默认处于启用状态,而对于光栅地图,则默认处于停用状态。使用 isFractionalZoomEnabled
地图选项可启用和停用小数缩放。
以下示例展示了如何在初始化地图时启用小数缩放:
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 8,
isFractionalZoomEnabled: true
});
您还可以通过设置 isFractionalZoomEnabled
地图选项来启用和停用小数缩放,如下所示:
// Using map.set
map.set('isFractionalZoomEnabled', true);
// Using map.setOptions
map.setOptions({isFractionalZoomEnabled: true});
您可以设置监听器来检测是否启用了小数缩放;如果您尚未明确将 isFractionalZoomEnabled
设置为 true
或 false
,此方法会非常有用。以下示例代码会检查小数缩放是否已启用:
map.addListener('isfractionalzoomenabled_changed', () => {
const isFractionalZoomEnabled = map.get('isFractionalZoomEnabled');
if (isFractionalZoomEnabled === false) {
console.log('not using fractional zoom');
} else if (isFractionalZoomEnabled === true) {
console.log('using fractional zoom');
} else {
console.log('map not done initializing yet');
}
});