向逼真的 3D 地图添加标记和动画

1. 准备工作

本教程将探讨如何在应用中添加 3D 标记并设置其样式。您还将学习如何通过飞往特定位置并在其周围飞行来为应用添加动画效果。

本教程以第一个 codelab 中介绍的概念为基础。如果您尚未完成该 Codelab,请先完成该 Codelab,以掌握此应用所需的基础知识。

实践内容

“包含标记的完整地图。

此应用简要介绍了 Google 在欧洲的主要办事处。用户可以选择某个办公室,进入并环绕该办公室进行探索,然后缩小视图以返回概览视图。这些功能通常出现在旅行和探索应用中,可为用户提供更具沉浸感的体验。

在此 Codelab 中,您将构建一个 3D Web 应用,用于执行以下操作:

  • 动态加载 Maps JavaScript API。
  • 向地图添加 3D 标记。
  • 使用 SVG 设置标记的样式。
  • 添加了飞到标记和围绕标记飞行的功能。
  • 将代码中的位置提取为数组。

学习内容

  • 标记的运作方式。
  • 如何设置标记样式。
  • 动画如何与内置函数搭配使用。
  • 摆放相机位置与点位置,以便更好地取景。
  • 一些实用技巧,可帮助您捕获摄像头参数,以便更好地取景。

前提条件

您需要熟悉此处的各项内容,才能完成此 Codelab。如果您已经能够熟练使用 Google Maps Platform,请直接跳到该 Codelab。

所需的 Google Maps Platform 产品

在此 Codelab 中,您将使用以下 Google Maps Platform 产品:

  • Maps JavaScript API

完成此 Codelab 的其他要求

要完成此 Codelab,您需要以下账号、服务和工具:

  • 已启用结算功能的 Google Cloud 账号。
  • 已启用 Maps JavaScript API 的 Google Maps Platform API 密钥。
  • 具备 JavaScript、HTML 和 CSS 方面的基础知识。
  • 您常用的文本编辑器或 IDE,用于保存要查看的修改后的文件。
  • 一个网络浏览器,用于在工作时查看文件。

2. 进行设置

设置 Google Maps Platform

如果您还没有已启用结算功能的 Google Cloud Platform 账号和项目,请参阅 Google Maps Platform 使用入门指南,创建结算账号和项目。

  1. Cloud Console 中,点击项目下拉菜单,选择要用于此 Codelab 的项目。

  1. Google Cloud Marketplace 中启用此 Codelab 所需的 Google Maps Platform API 和 SDK。为此,请按照此视频此文档中的步骤操作。
  2. 在 Cloud Console 的凭据页面中生成 API 密钥。您可以按照此视频此文档中的步骤操作。向 Google Maps Platform 发出的所有请求都需要 API 密钥。

3. 简单地球仪

如需开始构建应用,必须先完成基础设置。这将生成地球最基本形式的“蓝色大理石”视图,如下图所示:

“显示初始设置的地球仪的图片。

添加起始页的代码

如需将地球仪添加到网站,您需要在网页中添加以下代码。这会为 Maps JavaScript API 的加载器添加一个部分,并添加一个 init 函数,用于在您将添加标记代码的网页中创建 Map 3D 元素。

请务必将您自己的密钥(在“开始设置”部分中创建)添加到网页中,否则 3D 元素将无法初始化。

<!DOCTYPE html>
<html>
   <head>
       <title>Step 1 - Simple Globe</title>
       <style>
           body {
               height: 100vh;
               margin: 0;
           }
       </style>
   </head>

   <body>
       <script>
           (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
               key: "<INSERT API KEY>",
               v: "alpha",
               // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
               // Add other bootstrap parameters as needed, using camel case.
           });
       </script>
       <script>
           let map3D = null;

           async function init() {
               const { Map3DElement, MapMode } = await google.maps.importLibrary("maps3d");

               map3D = new Map3DElement({
                   mode: MapMode.HYBRID,
               });

               document.body.append(map3D);
           }
           init();
       </script>
   </body>
</html>

完成后,您就可以开始构图,将感兴趣的地点纳入镜头中,具体操作将在下一步中介绍。

4. 第一帧视图

现在,您已经创建了一个包含地球仪视图的地图,下一步实现步骤是设置正确的起始位置。这样,用户可以立即大致了解自己所在的位置。

虽然此示例重点介绍了欧洲的 Google 办公室,但您可以将此方法应用于全球任何地理位置,从整个国家/地区到单个街区都适用。该产品的速度和灵活性可让您只需进行极少的代码更改,即可将应用从全球扩展到本地。

您将从初始取景开始,让 3D 地图看起来如下所示:

“以欧洲为中心的地球仪。

将相机对准欧洲

为了获得如图所示的显示效果,您需要正确设置显示画面的取景框,就像在空间中放置一个相机,从上往下俯视相应位置一样。

为此,您可以使用地图控件上的多个参数来设置摄像头详细信息。您可以通过下图了解参数在“真实”世界中的互动方式。具体而言,就是相机所看的中心点,以及相机与您所看位置之间的距离(范围)。您还需要设置相机视角的倾斜度(否则您将直接俯视地球)。

“显示相机参数的图片。

最后一个设置是航向,用于确定相机的方向。它以相对于正北的偏移量来衡量。这些值会作为对象应用于 3D 地图元素,以设置初始显示。您可以在使用更新后的 3D 元素构造函数的代码中看到这一点。

map3D = new Map3DElement({
    center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
    range: 5814650,
    tilt: 33,
    heading: 4.36,
    mode: MapMode.HYBRID
});

捕获相机参数

在 3D 地图中取景需要精确地放置相机,而仅通过代码实现这一点可能很难。如需简化此流程,请使用以下实用技巧:向您的网页添加一个函数,以便在您点击所需视图时捕获相机参数。这些参数将输出到控制台,以便复制到对象的相机设置中。

您可以找到稍后可能需要使用的代码,该代码已添加到此页面所示的示例中,但不会出现在后续页面的示例中,因为此 Codelab 不需要该代码。不过,如果您想通过更好的摄像头定位来制作更具沉浸感的演示,请记住该代码。

map3D.addEventListener('gmp-click', (event) => {
   console.log("camera: { center: { lat: " + map3D.center.lat + ", lng : " + map3D.center.lng + ", altitude: " + map3D.center.altitude + " }, range: " + map3D.range + ", tilt: " + map3D.tilt + " ,heading: " + map3D.heading + ", }");
   console.log("{ lat: " + event.position.lat + ", lng : " + event.position.lng + ", altitude: " + event.position.altitude + " }");
   // Stop the camera animation when the map is clicked.
   map3D.stopCameraAnimation();
});

请注意 stopCameraAnimation 函数的用法。如果网页正在放大或旋转,能够停止动画的播放非常有用,这样您就可以捕获显示屏中此时的位置。您可以使用以下代码来实现此目的。如需了解详情,请参阅 stopCameraAnimation 的文档。

点击操作的示例输出,如控制台中所示。

camera: { center: { lat: 51.39870122020001, lng : -0.08573187165829443, altitude: 51.66845062662254 }, range: 716.4743880553578, tilt: 50.5766672986501 ,heading: -1.048260134782318, }
step2.html:40 { lat: 51.398158351120536, lng : -0.08561139388593597, altitude: 51.860469133677626 }

相机文本可用作 3D 地图中各种对象的 JSON 输入,第二个输出是点击发生的实际点位置,对于创建点或用于标记定位的任何内容也很有用。

页面取景正确后,您现在可以添加标记了。请继续执行下一步操作,了解具体方法。

版块解决方案

对于此步骤,我们提供了已完成的页面作为解决方案,以验证您的实现。(如果复制,请务必使用您自己的 API 密钥)。

<!DOCTYPE html>
<html>

<head>
   <title>Step 2 - Europe View</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <script>
       (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
           key: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
<script>
       let map3D = null;

       async function init() {
           const { Map3DElement, MapMode } = await google.maps.importLibrary("maps3d");

           map3D = new Map3DElement({
                center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
                range: 5814650,
                tilt: 33,
                heading: 4.36,
                mode: MapMode.HYBRID,
            });

           map3D.addEventListener('gmp-click', (event) => {
               console.log("camera: { center: { lat: " + map3D.center.lat + ", lng : " + map3D.center.lng + ", altitude: " + map3D.center.altitude + " }, range: " + map3D.range + ", tilt: " + map3D.tilt + " ,heading: " + map3D.heading + ", }");
               console.log("{ lat: " + event.position.lat + ", lng : " + event.position.lng + ", altitude: " + event.position.altitude + " }");

               map3D.stopCameraAnimation();
           });

           document.body.append(map3D);
       }
       init();
   </script>

</body>

</html>

5. 简单标记

在本部分中,您将学习如何添加第一个标记。首先,您将了解标记的一般详细信息。

3D 地图支持创建两个不同的标记类:Marker3DElement 类和 Marker3DInteractiveElement 类,具体取决于您是否要启用标记点击。除此之外,它们在本质上是相同的,因此您将先创建 Marker3DElement,然后在后续步骤中将其“升级”为 Marker3DInteractiveElement

您可以点击此处查看此步骤的完整解决方案:

“一个地球,上面有一个标记,表示步骤已完成。

为标记添加一些高度

首先要了解的是,标记与 3D 地图中的所有其他内容一样,都是 3D 的。这意味着相应位置可以具有高度(海拔),并且该高度可以表示相对于海平面、地面、网格的某个位置,也可以设置为固定在地面上并忽略海拔位置。如需了解详情,请参阅 AltitudeMode 文档中的“Altitude 常量”部分。

您还可以使用 extruded 值设置标记是否突出显示。这将决定是否在标记下方向地面绘制一条小线,以帮助其显示与高度相关的实际位置,这对于挑选地面上的点非常有用。您可以查看 Google UK 位置的示例。这两个图形均采用了“extruded”效果,并且其位置设置为绝对高度。第一个位于 75 米处,第二个位于 125 米处。

75 米处的标记

125 米处的标记

海拔 75 米。

海拔 125 米。

隐藏或显示遮挡和碰撞标记

虽然这在我们的演示中可能不重要,因为这些位置相距甚远,但对于可能会相互重叠或可能位于建筑物后面的标记,您可以使用 collisionBehaviordrawsWhenOccluded 值来控制它们的行为。

对于碰撞行为,您可以使用以下选项:

  • REQUIRED:(默认)无论是否冲突,一律显示标记。
  • OPTIONAL_AND_HIDES_LOWER_PRIORITY 标记仅在未与其他标记重叠时才显示。如果两个同属于这种类型的标记发生重叠,则会显示 zIndex 较高的那个标记。如果二者的 zIndex 也相同,则会显示在屏幕上纵向位置较低的那个标记。
  • REQUIRED_AND_HIDES_OPTIONAL 无论是否冲突,始终显示该标记,并隐藏与该标记重叠的所有 OPTIONAL_AND_HIDES_LOWER_PRIORITY 标记或标签。

下图显示了根据定义的碰撞行为显示标记的方式差异。设置 REQUIRED 时,系统会显示所有标记,但如果您使用 REQUIRED_AND_HIDES_OPTIONAL,则在本例中,系统会显示屏幕上较低的标记(您可以根据需要调整 zIndex,使其他标记显示在顶部)。

所有标记均显示为必填

标记在其他标记后面被隐藏

必填

REQUIRED_AND_HIDES_OPTIONAL

对于遮挡,您可以选择是否在建筑物后面绘制标记。如下图所示。将 drawsWhenOccluded 设置为 true 时,系统会在标记绘制在建筑物后面时略微调暗其亮度;将其设置为 false 时,系统会在标记位于建筑物后面时隐藏标记。如需了解详情,请参阅下表:

显示地图隐藏遮挡的标记的图片

显示被遮挡的标记的地图图片

drawsWhenOccluded : false

drawsWhenOccluded : true

如前所述,如果允许绘制被遮挡的标记,则被冲突隐藏的标记将显示为灰显状态。在图片中,您可以看到一些标记被建筑物遮挡,一些标记被其他标记遮挡。

“显示多个标记和遮挡效果的图片。

如需了解详情,请参阅二维地图中的碰撞行为示例。

清除画布

现在,您可以创建第一个标记了。为确保用户专注于标记,您可以在 3D 地图中停用默认标签。

将 3D 地图元素的 mode 值设置为 SATELLITE

如需了解详情,请参阅模式

map3D = new Map3DElement({
    center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
    range: 5814650,
    tilt: 33,
    heading: 4.36,
    mode: MapMode.SATELLITE
});

这会生成以下 3D 地图:

“没有边框和文字的欧洲图片。

添加第一个标记

清理画布后,您现在可以添加第一个标记了。关键参数包括位置和标签。

如需添加标记,请设置标记的位置。您还可以添加标签(显示在标记上方),以及 Marker3DElement 文档中所述的其他元素。

如需添加标记,请在用于隐藏默认标签的行后面添加以下代码,如下所示:

const marker = new Marker3DElement({
   position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
   label: 'Google UK',
   altitudeMode: 'ABSOLUTE',
   extruded: true,
});

map3D.append(marker);

创建标记后,使用“append”方法将其添加到 3D 地图中。请注意,标记会以 3D 地图中的子元素数组的形式存储。如需修改标记,您需要通过此数组访问它。

在加载 API 时将 Marker3DElement 添加到库列表中,确保从 Maps JavaScript API 加载 Marker3DElement

const { Map3DElement, MapMode, Marker3DElement } = await google.maps.importLibrary("maps3d");

现在,页面加载后,您会看到整个欧洲,伦敦办事处上方会显示一个标记。如动画所示,您可以手动放大,查看创建的地点上方的标记。

“动画演示:手动放大 Google UK。

现在,您已加载第一个标记,下一步是让其看起来更美观。

版块解决方案

对于此步骤,我们提供了已完成的页面作为解决方案,以验证您的实现。(如果复制,请务必使用您自己的 API 密钥)。

<!DOCTYPE html>
<html>

<head>
   <title>Step 3 - Simple Marker</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <script>
       (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
           key: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       async function init() {
            const { Map3DElement, MapMode, Marker3DElement } = await google.maps.importLibrary("maps3d");

            map3D = new Map3DElement({
                center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
                range: 5814650,
                tilt: 33,
                heading: 4.36,
                mode: MapMode.SATELLITE,
            });

           const marker = new Marker3DElement({
               position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
               label: 'Google UK',
               altitudeMode: 'ABSOLUTE',
               extruded: true,
           });
           map3D.append(marker);

           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

6. SVG 标记

在此步骤中,您将更改标记,为其添加一个代表其所在国家/地区的旗帜,以使其看起来更美观。我们来看看具体该如何实现,为此您需要熟悉 PinElement

最终,您将获得如下新外观:

“带有英国国旗标记的图片”

使用 PinElement 进行基本自定义

在 JavaScript API 中,无论是 2D 地图还是 3D 地图,标记之间共享的元素之一就是 PinElement。在向 Map3DElement 添加 Marker3DElement 时,您需要将 PinElement 添加为 Marker3DElement 的子元素。

PinElement 在基本层面上具有更改普通标记的功能,可设置其边框颜色、内部点(或字形)颜色和背景颜色。您可以在显示二维标记的图片中看到这些信息。

“显示用于自定义标记图钉的选项的图片”

您还可以通过设置此元素的缩放值来设置标记大小(大于 1 表示比正常大,小于 1 表示按比例缩小)。

如果您想让图形看起来更具自定义风格,但仍保持标准的 PinElement 地图图钉外观,也可以将图形替换为图片或 SVG 文件。

PinElements 以外

在此步骤中,您将使用 svg 标志和不同的颜色更新标准 PinElement,但您还需要注意,您可以完全更改标记的外观,使其看起来甚至不像地图上的图钉。在标记中,您还可以使用模板(例如 HTMLImageElement 和 SVGElement)插入新图形。如需详细了解如何执行此操作,请参阅文档 Marker3DElement-Slots

如需了解可实现的所有功能,请查看以下示例,其中展示了使用多种不同方法设置标记样式的示例。

显示基本标记自定义功能的图片。

显示复杂标记自定义的图片。

通过 PinElement 进行基本自定义的标记,请参阅示例

通过 SVG 和图片通过模板进行复杂自定义的标记,请参阅示例

添加 PinElement

如需更改标记的外观,首先需要确保已将 PinElement 库添加到页面中。为此,请在导入 maps3d 库后添加以下代码行:

const { Map3DElement, MapMode, Marker3DElement } = await google.maps.importLibrary("maps3d");
const { PinElement } = await google.maps.importLibrary('marker');

现在,元素已加载,可以引用和创建 PinElement。查看代码,将其添加到创建标记的位置之间,并将标记附加到 3D 地图。

const marker = new Marker3DElement({
   position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
   label: 'Google UK',
   altitudeMode: 'ABSOLUTE',
   extruded: true,
});

const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

const markerPin = new PinElement({
   "background": 'white',
   "glyph": new URL(base + '/images/gb.svg'),
   "scale": 1.0,
});
marker.append(markerPin);

map3D.append(marker);

由于您不仅要加载基本图钉,因此除了设置 PinElement 及其关联的背景颜色和缩放比例之外,还需要完成许多其他工作。

首先,必须引用旗帜图标的 SVG 图片,在本例中为米字旗。您可以从 https://flagicons.lipis.dev/ 等集合中获取这些图标。

创建图标后,您可以将其放置在网站可以找到的位置,在本例中,您可以对图片的位置进行硬编码,也可以使用当前网站位置作为目录的桩,如这里使用 base 变量所示。然后,您可以将其关联到服务器上正确标志的位置,此处位于 '/images/gb.svg' 下。

这会创建一个如下所示的 PinElement:

“Marker showing Union Jack flag symbol.

因此,将标志放置在正确的位置并将代码放置在正确的位置后,您应该会看到一个如下所示的 3D 地图:

“正在放大新标记。

现在,我们的标记已经装扮完毕,还可以将其更改为可点击的标记,以便添加互动功能。这将在下一步中完成。

版块解决方案

对于此步骤,我们提供了已完成的页面作为解决方案,以验证您的实现。(如果复制,请务必使用您自己的 API 密钥)。

另外,请不要忘记,您需要获取旗帜 svg(或 png 文件,由您选择!)文件,并将其存储在网页可以找到的目录中(此处存储在“images”文件夹中)。

<!DOCTYPE html>
<html>

<head>
   <title>Step 4 - SVG Marker</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <script>
       (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
           key: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       async function init() {
           const { Map3DElement, MapMode, Marker3DElement } = await google.maps.importLibrary("maps3d");
           const { PinElement } = await google.maps.importLibrary('marker');

           map3D = new Map3DElement({
                center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
                range: 5814650,
                tilt: 33,
                heading: 4.36,
                mode: MapMode.SATELLITE,
           });

           const marker = new Marker3DElement({
               position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
               label: 'Google UK',
               altitudeMode: 'ABSOLUTE',
               extruded: true,
           });

           const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

           const markerPin = new PinElement({
               "background": 'white',
               "glyph": new URL(base + '/images/gb.svg'),
               "scale": 1.0,
           });
           marker.append(markerPin);

           map3D.append(marker);

           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

7. 交互式标记

在上一步中,我们向页面添加了一个标记,但除了看起来不错之外,它没有太大用处,您仍然需要以相同的方式与 3D 地图互动。下一步是添加在点击标记时执行操作的功能,以便标记能够响应用户互动。

如需添加此功能,您需要将 Marker3DElement 转换为 Marker3DInteractiveElement。最后,您将获得一个看起来类似的页面,但现在,点击其中的标记将弹出一条提醒,如下所示:

“显示点击该回答时显示的响应的图片。

首先更改标记类

如需向标记添加交互性,您需要确保它使用的是正确的类。所需的类是 Marker3DInteractiveElement,但由于它是 Marker3DElement 的扩展,因此除了加载新类并更改构造函数中的类名称之外,您无需执行任何操作。

const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
const { PinElement } = await google.maps.importLibrary('marker');

map3D = new Map3DElement({
    center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
    range: 5814650,
    tilt: 33,
    heading: 4.36,
    mode: MapMode.SATELLITE,
});

const marker = new Marker3DInteractiveElement({
   position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
   label: 'Google UK',
   altitudeMode: 'ABSOLUTE',
   extruded: true,
});

其次,向标记添加点击事件

接下来,为标记添加点击事件,以处理用户互动并做出响应。在代码段中,您可以看到点击事件已添加到标记。在本例中,系统会触发提醒并弹出文本,其中显示了从触发的事件的目标获取的标记标签,这让我们可以访问标签属性。在构建标记后,将以下代码添加到您的应用中。

marker.addEventListener('gmp-click', (event) => {
   alert('You clicked on : ' + event.target.label);
   event.stopPropagation();
});

请注意,stopPropagation 事件用于确保在堆栈中的任何其他点击监听器在底层对象(例如 3D 地图主画布)上触发。

现在,运行应用后,您应该会看到以下结果:

“显示点击该回答时显示的响应的图片。

现在,您可以在点击标记时执行操作,并在下一步中向页面添加一些动画。

版块解决方案

对于此步骤,我们提供了已完成的页面作为解决方案,以验证您的实现。(如果复制,请务必使用您自己的 API 密钥)。

<!DOCTYPE html>
<html>

<head>
   <title>Step 5 - Interactive Marker</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <script>
       (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
           key: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       async function init() {
           const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
           const { PinElement } = await google.maps.importLibrary('marker');

           map3D = new Map3DElement({
                center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
                range: 5814650,
                tilt: 33,
                heading: 4.36,
                mode: MapMode.SATELLITE,
           });

           const marker = new Marker3DInteractiveElement({
               position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
               label: 'Google UK',
               altitudeMode: 'ABSOLUTE',
               extruded: true,
           });

           marker.addEventListener('gmp-click', (event) => {
               alert('You clicked on : ' + event.target.label);
               event.stopPropagation();
           });

           const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

           const markerPin = new PinElement({
               "background": 'white',
               "glyph": new URL(base + '/images/gb.svg'),
               "scale": 1.0,
           });
           marker.append(markerPin);

           map3D.append(marker);

           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

8. 飞往

在此步骤中,您将使用点击标记的功能来添加动画,以便飞到其位置。您可以点击此处查看实际效果。

“动画演示:点击标记并飞到相应位置。

使用 flyCameraTo 添加动画效果

如需将其添加到页面中,您将使用 3D 地图的 flyCameraTo 方法,其中相机会在您当前所在的相机位置和您要查看的相机位置之间进行动画处理,在两者之间进行插值,并在 3D 地图中进行飞行动画处理。

使用 flyCameraTo 时,您需要指定 FlyToAnimationOptions,该对象具有两个属性:endCamera(相机在动画结束时应指向的位置)和 durationMillis(转换所需的毫秒数)。

在示例中,将相机设置为朝向标记位置的建筑物,倾斜度为 65 度,范围为 500 米,朝向正北,方位角为 0 度。将动画时间设置为 12500 毫秒 (12.5 秒)。

将页面中的当前提醒事件替换为 flyCameraTo 代码段:

marker.addEventListener('gmp-click', (event) => {
   map3D.flyCameraTo({
       endCamera: {
           center: marker.position,
           tilt: 65,
           range: 500,
           heading: 0,
       },
       durationMillis: 12500,
   });

   event.stopPropagation();
});

至此,您现在应该能够刷新页面,然后点击标记并前往 Google UK,如动画所示:

“动画演示:点击标记并飞到相应位置。

在此步骤中,您添加了一个可点击的标记,用于将相机飞到标记的位置。在下一步中,您将添加相机围绕该点飞行的能力,使其环绕该位置旋转。

版块解决方案

对于此步骤,我们提供了已完成的页面作为解决方案,以验证您的实现。(如果复制,请务必使用您自己的 API 密钥)。

<!DOCTYPE html>
<html>

<head>
   <title>Step 6 - Zoom To</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <script>
       (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
           key: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       async function init() {
           const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
           const { PinElement } = await google.maps.importLibrary('marker');

           map3D = new Map3DElement({
                center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
                range: 5814650,
                tilt: 33,
                heading: 4.36,
                mode: MapMode.SATELLITE,
           });

           const marker = new Marker3DInteractiveElement({
               position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
               label: 'Google UK',
               altitudeMode: 'ABSOLUTE',
               extruded: true,
           });

           marker.addEventListener('gmp-click', (event) => {
               map3D.flyCameraTo({
                   endCamera: {
                       center: marker.position,
                       tilt: 65,
                       range: 500,
                       heading: 0,
                   },
                   durationMillis: 12500,
               });

               event.stopPropagation();
           });

           const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

           const markerPin = new PinElement({
               "background": 'white',
               "glyph": new URL(base + '/images/gb.svg'),
               "scale": 1.0,
           });
           marker.append(markerPin);

           map3D.append(marker);

           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

9. 四处飞行

动画的最后一项是使用 flyCameraAround 方法为围绕建筑物旋转的动画添加效果。最后,您将获得一个动画,它会飞向建筑物,然后围绕建筑物飞行,如动画所示。这对于实际示例来说可能有点快,但对于演示操作的运作方式来说很有用,而且时间也不会太长,您可以调整时间,直到找到适合您的值。

“动画演示:点击某个标记后,该标记飞向某个地点并在该地点周围飞行。

我们来飞一圈吧!

flyCameraAround 方法与 flyCameraTo 函数类似,因为它接受多个选项作为输入,这些选项用于控制要绕摄的目标位置、相机参数以及绕摄所需的时间(以毫秒为单位)。最后,您还可以指定在指定时间内可以发生的旋转次数。您可以在 FlyAroundAnimationOptions 中查看所有选项

不过请等一分钟!

在动画中,您可以看到动画飞到相应位置,然后围绕该位置飞行,从而串联动画。为此,您可以使用 3D 地图 gmp-animationend 事件,确保在触发下一个动画之前,当前动画已完成。此动画应仅在停止前发生一次。

查看代码,将其插入上一部分中添加的代码后面。

marker.addEventListener('gmp-click', (event) => {
   map3D.flyCameraTo({
       endCamera: {
           center: marker.position,
           tilt: 65,
           range: 500,
           heading: 0,
       },
       durationMillis: 5000,
   });

   map3D.addEventListener('gmp-animationend', () => {
       map3D.flyCameraAround({
           camera: {
               center: marker.position,
               tilt: 65,
               range: 500,
               heading: 0,
           },
           durationMillis: 5000,
           rounds: 1
       });
   }, { once: true });

   event.stopPropagation();
});

添加了监听 gmp-animationend 事件的功能,以便它可以调用 flyCameraAround 事件。将起点设置为与飞行到方法的结束相机相同,意味着过渡会很流畅(以免在切换到新位置时造成任何突兀的移动)。同样,durationMillis 用于控制动画的执行时长。在本例中,该方法还采用另一个选项 rounds,并将其设置为 1。

这意味着相机将在 5 秒内围绕该点旋转一圈。您可以根据需要对这些值进行实验,以找到适合您的数量。

此时,动画将结束,但您不希望 gmp-animationend 事件再次通过这段代码触发,因为这将导致轨道无限期运行。为避免这种情况,请将监听器的 once 设置设为 true。这意味着,事件在完成后会被移除,从而避免无限循环。

添加此代码后,您应该能够运行解决方案,并看到动画现在会在结束时围绕标记飞舞,如动画所示:

“动画演示:飞行器围绕标记飞行。

在此步骤中,您添加了一个可点击的标记,然后相机会飞到标记位置并围绕该位置飞行。在下一步中,我们将开始添加更多点,并允许我们在这些点之间移动。

版块解决方案

对于此步骤,我们提供了已完成的页面作为解决方案,以验证您的实现。(如果复制,请务必使用您自己的 API 密钥)。

<!DOCTYPE html>
<html>

<head>
   <title>Step 7 - Zoom Around</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <script>
       (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
           key: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       const europeCamera = {
           center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
           range: 5814650,
           tilt: 33,
           heading: 4.36,
       };

       async function init() {
           const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
           const { PinElement } = await google.maps.importLibrary('marker');

           map3D = new Map3DElement({
               ...europeCamera,
               mode: MapMode.SATELLITE,
           });

           const marker = new Marker3DInteractiveElement({
               position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
               label: 'Google UK',
               altitudeMode: 'ABSOLUTE',
               extruded: true,
           });

           marker.addEventListener('gmp-click', (event) => {
               map3D.flyCameraTo({
                   endCamera: {
                       center: marker.position,
                       tilt: 65,
                       range: 500,
                       heading: 0,
                   },
                   durationMillis: 5000,
               });

               map3D.addEventListener('gmp-animationend', () => {
                   map3D.flyCameraAround({
                       camera: {
                           center: marker.position,
                           tilt: 65,
                           range: 500,
                           heading: 0,
                       },
                       durationMillis: 5000,
                       rounds: 1
                   });
               }, { once: true });

               event.stopPropagation();
           });

           const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

           const markerPin = new PinElement({
               "background": 'white',
               "glyph": new URL(base + '/images/gb.svg'),
               "scale": 1.0,
           });
           marker.append(markerPin);

           map3D.append(marker);

           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

10. Paris!

虽然伦敦是一座伟大的城市,但在页面上看起来有点孤单,因此我们先从巴黎开始添加一些新地理位置。为此,您可以使用数组来存储所有特定于位置的详细信息,然后将其用作设置标记显示参数以及飞到相机位置和围绕相机位置移动的函数和变量的输入。如前所述,为了更好地拍摄建筑物,相机取景框可能需要与标记点位置不同。

“动画演示:点击后,摄像头飞到 Google France 并在其周围移动。

位置数组

为了避免硬编码特定位置的所有详细信息(例如查看摄像头、标记点和显示选项),您可以使用一个由 JSON 对象组成的较小数组来存储这些数据。然后,在应用中创建和使用标记时,便可应用此方法。您可以在代码段中看到此示例,其中创建了一个名为 officeLocations 的变量来存储数组。

将以下代码添加到 init 函数之前。另请注意,base 变量已移出 init 函数,以便将其应用于所有办公地点。

const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

const europeCamera = {
   center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
   range: 5814650,
   tilt: 33,
   heading: 4.36,
};

const officeLocations = [
   {
       "name": "Google France",
       "camera": {
           "center": { lat: 48.877276, lng: 2.329978, altitude: 48 },
           "range": 178,
           "tilt": 57.48,
           "heading": -17,
       },
       "point": { lat: 48.8775183, lng: 2.3299791, altitude: 60 },
       "pin": {
           "background": 'white',
           "glyph": new URL(base + '/images/fr.svg'),
           "scale": 1.0,
       },
   },
   {
       "name": "Google UK",
       "camera": {
           "center": { lat: 51.5332, lng: -0.1260, altitude: 38.8 },
           "range": 500,
           "tilt": 56.21672368296945,
           "heading": -31.15763027564165,
       },
       "point": { lat: 51.5332, lng: -0.1260, altitude: 75 },
       "pin": {
           "background": 'white',
           "glyph": new URL(base + '/images/gb.svg'),
           "scale": 1.0,
       },
   }]
       const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

每个办公地点都具有以下属性:

  • name:营业地点的名称。
  • camera:用于查看要飞往和飞行周围的位置的初始视图。
  • point:放置标记的位置。
  • pin:标记图钉颜色和字形属性的详细信息

其他角度

您可能会注意到,对于英国,相机中心和标记点相同(除了海拔),而对于法国,相机中心和标记点不同。这是因为,对于法国的位置,标记点需要位于初始相机视图的不同位置,这样在飞往和围绕整个建筑物时,用户可以获得比使用标记点时更好的视图。

返回欧洲

增加多个点的一个作用是,需要能够在这些点之间移动。您可以使用下拉菜单来进行选择,但在此示例中,摄像头每次都会返回到欧洲视图,以便用户选择其他位置。

为此,初始视图需要存储在一个可用于将相机重置为整个欧洲视图的变量中。在此示例中,添加一个名为 europeCamera 的新变量来存储此值,以供日后使用。

更新 init 函数

您需要进行的第一个修改是,在创建 Map3DElement 时使用 europeCamera 对象作为输入。

您需要进行的第二项修改是将标记创建部分封装在循环中,以便使用存储在变量中的参数对其进行更新,您可以在所示代码中看到这些内容:

  • office.point:标记位置。
  • office.name:用于标记标签的办公室名称。
  • office.camera:相机的初始位置。
  • office.pin:用于显示差异的 PinElement 选项

此外,别忘了获取法国国旗的 SVG 文件或图片!

async function init() {
   const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
   const { PinElement } = await google.maps.importLibrary('marker');

   map3D = new Map3DElement({
       ...europeCamera,
       mode: MapMode.SATELLITE,
   });

   officeLocations.forEach(office => {
       const marker = new Marker3DInteractiveElement({
           position: office.point,
           label: office.name,
           altitudeMode: 'ABSOLUTE',
           extruded: true,
       });

       marker.addEventListener('gmp-click', (event) => {
           map3D.flyCameraTo({
               endCamera: office.camera,
               durationMillis: 5000,
           });

           map3D.addEventListener('gmp-animationend', () => {
               map3D.flyCameraAround({
                   camera: office.camera,
                   durationMillis: 5000,
                   rounds: 1
               });

               map3D.addEventListener('gmp-animationend', () => {
                   map3D.flyCameraTo({
                       endCamera: europeCamera,
                       durationMillis: 5000,
                   });
               }, { once: true });

           }, { once: true });

           event.stopPropagation();
       });

       const markerPin = new PinElement(office.pin);
       marker.append(markerPin);

       map3D.append(marker);
   });
   document.body.append(map3D);
}

请注意,在 flyCameraAround 动画后面添加了第二个 gmp-animationend 函数,以使用存储的 europeCamera 变量处理返回到欧洲视图的飞行动画。如动画所示:

“动画:在法国和英国的办公室之间和周围飞舞。

在此阶段,应用已扩展为包含两个位置,并且能够使用动画和位置数组在两个位置之间飞行。在下一步中,我们将将其余的办公地点添加到数组中。

版块解决方案

对于此步骤,我们提供了已完成的页面作为解决方案,以验证您的实现。(如果复制,请务必使用您自己的 API 密钥)。

另外,请不要忘记,您需要获取旗帜 SVG 文件(或您选择的 PNG 文件),并将其存储在网页可以找到的目录中(此处存储在“images”文件夹中)。

<!DOCTYPE html>
<html>

<head>
   <title>Step 8 - Paris!</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <script>
       (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
           key: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

       const europeCamera = {
           center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
           range: 5814650,
           tilt: 33,
           heading: 4.36,
       };

       const officeLocations = [
           {
               "name": "Google France",
               "camera": {
                   "center": { lat: 48.877276, lng: 2.329978, altitude: 48 },
                   "range": 178,
                   "tilt": 57.48,
                   "heading": -17,
               },
               "point": { lat: 48.8775183, lng: 2.3299791, altitude: 60 },
               "pin": {
                   "background": 'white',
                   "glyph": new URL(base + '/images/fr.svg'),
                   "scale": 1.0,
               },
           },
           {
               "name": "Google UK",
               "camera": {
                   "center": { lat: 51.5332, lng: -0.1260, altitude: 38.8 },
                   "range": 500,
                   "tilt": 56.21672368296945,
                   "heading": -31.15763027564165,
               },
               "point": { lat: 51.5332, lng: -0.1260, altitude: 75 },
               "pin": {
                   "background": 'white',
                   "glyph": new URL(base + '/images/gb.svg'),
                   "scale": 1.0,
               },
           }]

       async function init() {
           const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
           const { PinElement } = await google.maps.importLibrary('marker');

           map3D = new Map3DElement({
               ...europeCamera,
               mode: MapMode.SATELLITE,
           });

           officeLocations.forEach(office => {
               const marker = new Marker3DInteractiveElement({
                   position: office.point,
                   label: office.name,
                   altitudeMode: 'ABSOLUTE',
                   extruded: true,
               });

               marker.addEventListener('gmp-click', (event) => {
                   map3D.flyCameraTo({
                       endCamera: office.camera,
                       durationMillis: 5000,
                   });

                   map3D.addEventListener('gmp-animationend', () => {
                       map3D.flyCameraAround({
                           camera: office.camera,
                           durationMillis: 5000,
                           rounds: 1
                       });

                       map3D.addEventListener('gmp-animationend', () => {
                           map3D.flyCameraTo({
                               endCamera: europeCamera,
                               durationMillis: 5000,
                           });
                       }, { once: true });

                   }, { once: true });

                   event.stopPropagation();
               });

               const markerPin = new PinElement(office.pin);
               marker.append(markerPin);

               map3D.append(marker);
           });
           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

11. 更多地点

虽然应用现在已具备所有所需功能,但 3D 地图看起来仍然有点稀疏,因此您现在需要再添加一些地点,让地图看起来更丰富一些。通过使用数组,您可以轻松地继续填充具有自己唯一标记的新位置。最后一步是继续添加标记,直到出现以下视图。

“一张显示所有办公室的图片。

添加更多标记

Google 在欧洲的许多国家/地区都有多个办事处,我们来在地图上添加其中一些办事处。只需更新数组即可。此数据可以从 Web 服务获取,也可以从某个位置的静态文件中提供,在本例中,为简单起见,我们将其保留在同一页面中。

您可以根据需要添加任意数量的标记,系统会将这些标记提取出来,然后自动添加到视图中。记得获取正确的标志并将其存储在 images 目录(或方便的位置)中。

const officeLocations = [
   {
       name: "Google France",
       camera: {
           center: { lat: 48.877276, lng: 2.329978, altitude: 48 },
           range: 178,
           tilt: 57.48,
           heading: -17,
       },
       point: { lat: 48.8775183, lng: 2.3299791, altitude: 60 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/fr.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google UK",
       camera: {
           center: { lat: 51.5332, lng: -0.1260, altitude: 38.8 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 51.5332, lng: -0.1260, altitude: 75 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/gb.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Belgium",
       camera: {
           center: { lat: 50.83930408436509, lng: 4.38052394507952, altitude: 64.38932203802196},
           range: 466.62899893119175,
           tilt: 43.61569474716178,
           heading: 51.805907046332074,
       },
       point: { lat: 50.8392653, lng: 4.3808751, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/be.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Czechia",
       camera: {
           center: {
               lat: 50.07004093853976,
               lng: 14.402871475443956,
               altitude: 223.39574818495532
           },
           range: 522.0365799222782,
           tilt: 62.39511972890614,
           heading: -39.150149539328304,
       },
       point: { lat: 50.0703122, lng: 14.402668199999999, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/cz.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Denmark",
       details: "2, Sankt Petri Passage 5, 1165 København",
       camera: {
           center: {
               lat: 55.680359539635866,
               lng: 12.570460204526002,
               altitude: 30.447654757346044
           },
           range: 334.8786935049066,
           tilt: 55.38819319004654,
           heading: 149.63867461295067,
       },
       point: { lat: 55.6804504, lng: 12.570279099999999, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/dk.svg'),
           scale: 1.0,
       },
   },
   ,
   {
       name: "Google Greece",
       camera: {
           center: {
               lat: 38.038634694028055,
               lng: 23.802924946201266,
               altitude: 196.45884670344995
           },
           range: 343.57226336076565,
           tilt: 54.97375927639567,
           heading: -33.26775344055724,
       },
       point: { lat: 38.038619, lng: 23.8031622, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/gr.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Germany",
       camera: {
           center: {
               lat: 53.55397683312404,
               lng: 9.986350507286808,
               altitude: 44.83610870143956
           },
           range: 375.3474077822466,
           tilt: 71.35078443829818,
           heading: -160.76930098951416,
       },
       point: { lat: 53.5540227, lng: 9.9863, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/de.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Ireland",
       camera: {
           center: { lat: 53.339816899999995, lng: -6.2362644, altitude: 38.777415761228006 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 53.339816899999995, lng: -6.2362644, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/ie.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Italy",
       camera: {
           center: {
               lat: 45.486361346538224,
               lng: 9.18995496294455,
               altitude: 138.55834058400072
           },
           range: 694.9398023590038,
           tilt: 57.822470255679114,
           heading: 84.10194883488619,
       },
       point: { lat: 45.4863064, lng: 9.1894762, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/it.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Lithuania",
       camera: {
           center: {
               lat: 54.698040606567965,
               lng: 25.30965338542576,
               altitude: 111.80276944294413
           },
           range: 412.5808304977545,
           tilt: 43.50793332082195,
           heading: -29.181098269421028,
       },
       point: { lat: 54.6981204, lng: 25.3098617, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/at.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Netherlands",
       camera: {
           center: {
               lat: 52.33773837150874,
               lng: 4.871754560171063,
               altitude: 53.68063996154723
           },
           range: 473.1982259177312,
           tilt: 56.216523350388634,
           heading: 71.78252318033718,
       },
       point: { lat: 52.337801, lng: 4.872065999999999, altitude: 100 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/nl.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Norway",
       camera: {
           center: { lat: 59.90991209999999, lng: 10.726020799999999, altitude: 38.777415761228006 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 59.90991209999999, lng: 10.726020799999999, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/no.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Poland",
       camera: {
           center: { lat: 52.22844380000001, lng: 20.9851819, altitude: 38.777415761228006 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 52.22844380000001, lng: 20.9851819, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/pl.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Portugal",
       camera: {
           center: {
               lat: 38.7240122810727,
               lng: -9.150628263172639,
               altitude: 55.299662291551044
           },
           range: 337.7474313328639,
           tilt: 56.79772652682846,
           heading: 176.0722118222208,
       },
       point: { lat: 38.723915999999996, lng: -9.150629, altitude: 35 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/pt.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Romania",
       camera: {
           center: {
               lat: 44.43076650172983,
               lng: 26.109700164718586,
               altitude: 125.57895810814505
           },
           range: 364.25249956711923,
           tilt: 38.517539223834326,
           heading: -38.81294924429363,
       },
       point: { lat: 44.4309897, lng: 26.1095719, altitude: 75 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/ro.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Spain",
       camera: {
           center: {
               lat: 40.450078762608875,
               lng: -3.6930085080020856,
               altitude: 753.6446342341894
           },
           range: 845.7279793010093,
           tilt: 46.752510050599746,
           heading: 4.718779524265234,
       },
       point: { lat: 40.450294199999995, lng: -3.6927915, altitude: 175 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/es.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Sweden",
       camera: {
           center: {
               lat: 59.33313751316038,
               lng: 18.054618219238293,
               altitude: 16.728213706832868
           },
           range: 377.5210725830039,
           tilt: 63.59478230626709,
           heading: 98.53138488367703,
       },
       point: { lat: 59.3332093, lng: 18.0536386, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/se.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Switzerland",
       camera: {
           center: {
               lat: 47.365411056285275,
               lng: 8.525063594405356,
               altitude: 419.2348376754488
           },
           range: 166.74918737631742,
           tilt: 59.31431457129067,
           heading: -32.620415961949206,
       },
       point: { lat: 47.365452, lng: 8.5249253, altitude: 100 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/ch.svg'),
           scale: 1.0,
       },
   }
]

完成后,您应该会看到一个完整的页面,如图所示,用户可以点击任意位置,然后飞到该位置并围绕该位置飞行,最后退出!

“Amination flying between and around offices in Spain and Sweden.

恭喜,您已完成此 Codelab。我们将在下一部分中进行总结,并探索其他新事物!

版块解决方案

对于此步骤,我们提供了已完成的页面作为解决方案,以验证您的实现。(如果复制,请务必使用您自己的 API 密钥)。

另外,请不要忘记,您需要获取旗帜 SVG 文件(或您选择的 PNG 文件),并将其存储在网页可以找到的目录中(此处存储在“images”文件夹中)。

<!DOCTYPE html>
<html>

<head>
   <title>Step 9 - More Places!</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <script>
       (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
           key: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

       const europeCamera = {
           center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
           range: 5814650,
           tilt: 33,
           heading: 4.36,
       };

const officeLocations = [
   {
       name: "Google France",
       details: "8 Rue de Londres, 75009 Paris, France",
       camera: {
           center: { lat: 48.877276, lng: 2.329978, altitude: 48 },
           range: 178,
           tilt: 57.48,
           heading: -17,
       },
       point: { lat: 48.8775183, lng: 2.3299791, altitude: 60 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/fr.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google UK",
       details: "6 Pancras Square, London N1C 4AG, UK",
       camera: {
           center: { lat: 51.5332, lng: -0.1260, altitude: 38.8 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 51.5332, lng: -0.1260, altitude: 75 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/gb.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Belgium",
       details: "Chau. d'Etterbeek 180, 1040 Brussel",
       camera: {
           center: { lat: 50.83930408436509, lng: 4.38052394507952, altitude: 64.38932203802196},
           range: 466.62899893119175,
           tilt: 43.61569474716178,
           heading: 51.805907046332074,
       },
       point: { lat: 50.8392653, lng: 4.3808751, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/be.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Czechia",
       details: "Stroupežnického 3191/17, 150 00 Praha 5-Smíchov",
       camera: {
           center: {
               lat: 50.07004093853976,
               lng: 14.402871475443956,
               altitude: 223.39574818495532
           },
           range: 522.0365799222782,
           tilt: 62.39511972890614,
           heading: -39.150149539328304,
       },
       point: { lat: 50.0703122, lng: 14.402668199999999, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/cz.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Denmark",
       details: "2, Sankt Petri Passage 5, 1165 København",
       camera: {
           center: {
               lat: 55.680359539635866,
               lng: 12.570460204526002,
               altitude: 30.447654757346044
           },
           range: 334.8786935049066,
           tilt: 55.38819319004654,
           heading: 149.63867461295067,
       },
       point: { lat: 55.6804504, lng: 12.570279099999999, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/dk.svg'),
           scale: 1.0,
       },
   },
   ,
   {
       name: "Google Greece",
       details: "Fragkokklisias 6, Athina 151 25",
       camera: {
           center: {
               lat: 38.038634694028055,
               lng: 23.802924946201266,
               altitude: 196.45884670344995
           },
           range: 343.57226336076565,
           tilt: 54.97375927639567,
           heading: -33.26775344055724,
       },
       point: { lat: 38.038619, lng: 23.8031622, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/gr.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Germany",
       details: "ABC-Straße 19, 20354 Hamburg",
       camera: {
           center: {
               lat: 53.55397683312404,
               lng: 9.986350507286808,
               altitude: 44.83610870143956
           },
           range: 375.3474077822466,
           tilt: 71.35078443829818,
           heading: -160.76930098951416,
       },
       point: { lat: 53.5540227, lng: 9.9863, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/de.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Ireland",
       details: "Gordon House, 4 Barrow St, Grand Canal Dock, Dublin 4, D04 V4X7",
       camera: {
           center: { lat: 53.339816899999995, lng: -6.2362644, altitude: 38.777415761228006 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 53.339816899999995, lng: -6.2362644, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/ie.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Italy",
       details: "Isola Building C, Via Federico Confalonieri, 4, 20124 Milano",
       camera: {
           center: {
               lat: 45.486361346538224,
               lng: 9.18995496294455,
               altitude: 138.55834058400072
           },
           range: 694.9398023590038,
           tilt: 57.822470255679114,
           heading: 84.10194883488619,
       },
       point: { lat: 45.4863064, lng: 9.1894762, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/it.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Lithuania",
       details: "Vilnius Tech Park, Antakalnis st. 17, 2nd building, LT-10312, Vilnius",
       camera: {
           center: {
               lat: 54.698040606567965,
               lng: 25.30965338542576,
               altitude: 111.80276944294413
           },
           range: 412.5808304977545,
           tilt: 43.50793332082195,
           heading: -29.181098269421028,
       },
       point: { lat: 54.6981204, lng: 25.3098617, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/at.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Netherlands",
       details: "Claude Debussylaan 34, 1082 MD Amsterdam",
       camera: {
           center: {
               lat: 52.33773837150874,
               lng: 4.871754560171063,
               altitude: 53.68063996154723
           },
           range: 473.1982259177312,
           tilt: 56.216523350388634,
           heading: 71.78252318033718,
       },
       point: { lat: 52.337801, lng: 4.872065999999999, altitude: 100 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/nl.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Norway",
       details: "Bryggegata 6, 0250 Oslo",
       camera: {
           center: { lat: 59.90991209999999, lng: 10.726020799999999, altitude: 38.777415761228006 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 59.90991209999999, lng: 10.726020799999999, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/no.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Poland",
       details: "Rondo Daszynskiego 2, 00-843 Warsaw",
       camera: {
           center: { lat: 52.22844380000001, lng: 20.9851819, altitude: 38.777415761228006 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 52.22844380000001, lng: 20.9851819, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/pl.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Portugal",
       details: "R. Duque de Palmela 37 Piso 4, 1250-097 Lisboa",
       camera: {
           center: {
               lat: 38.7240122810727,
               lng: -9.150628263172639,
               altitude: 55.299662291551044
           },
           range: 337.7474313328639,
           tilt: 56.79772652682846,
           heading: 176.0722118222208,
       },
       point: { lat: 38.723915999999996, lng: -9.150629, altitude: 35 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/pt.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Romania",
       details: "Bulevardul Corneliu Coposu 6-8, București 030167",
       camera: {
           center: {
               lat: 44.43076650172983,
               lng: 26.109700164718586,
               altitude: 125.57895810814505
           },
           range: 364.25249956711923,
           tilt: 38.517539223834326,
           heading: -38.81294924429363,
       },
       point: { lat: 44.4309897, lng: 26.1095719, altitude: 75 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/ro.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Spain",
       details: "Torre Picasso, Pl. Pablo Ruiz Picasso, 1, Tetuán, 28020 Madrid",
       camera: {
           center: {
               lat: 40.450078762608875,
               lng: -3.6930085080020856,
               altitude: 753.6446342341894
           },
           range: 845.7279793010093,
           tilt: 46.752510050599746,
           heading: 4.718779524265234,
       },
       point: { lat: 40.450294199999995, lng: -3.6927915, altitude: 175 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/es.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Sweden",
       details: "Kungsbron 2, 111 22 Stockholm",
       camera: {
           center: {
               lat: 59.33313751316038,
               lng: 18.054618219238293,
               altitude: 16.728213706832868
           },
           range: 377.5210725830039,
           tilt: 63.59478230626709,
           heading: 98.53138488367703,
       },
       point: { lat: 59.3332093, lng: 18.0536386, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/se.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Switzerland",
       details: "Brandschenkestrasse 110, 8002 Zürich",
       camera: {
           center: {
               lat: 47.365411056285275,
               lng: 8.525063594405356,
               altitude: 419.2348376754488
           },
           range: 166.74918737631742,
           tilt: 59.31431457129067,
           heading: -32.620415961949206,
       },
       point: { lat: 47.365452, lng: 8.5249253, altitude: 100 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/ch.svg'),
           scale: 1.0,
       },
   }
]

       async function init() {
           const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
           const { PinElement } = await google.maps.importLibrary('marker');

           map3D = new Map3DElement({
               ...europeCamera,
               mode: MapMode.SATELLITE,
           });

           officeLocations.forEach(office => {
               const marker = new Marker3DInteractiveElement({
                   position: office.point,
                   label: office.name,
                   altitudeMode: 'RELATIVE_TO_GROUND',
                   extruded: true,
               });

               marker.addEventListener('gmp-click', (event) => {
                   map3D.flyCameraTo({
                       endCamera: office.camera,
                       durationMillis: 2000,
                   });

                   map3D.addEventListener('gmp-animationend', () => {
                       map3D.flyCameraAround({
                           camera: office.camera,
                           durationMillis: 2000,
                           rounds: 1
                       });

                       map3D.addEventListener('gmp-animationend', () => {
                           map3D.flyCameraTo({
                               endCamera: europeCamera,
                               durationMillis: 2000,
                           });
                       }, { once: true });

                   }, { once: true });

                   event.stopPropagation();
               });

               const markerPin = new PinElement(office.pin);
               marker.append(markerPin);

               map3D.append(marker);
           });
           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

12. 后续步骤

在此 Codelab 中,您了解了可以使用 Maps JavaScript API 中的 3D 功能执行的操作方面的基础知识。接下来,请尝试为地图添加下面的部分地图项:

  • 添加一个下拉列表,以便用户选择办公室。
  • 使用其他一些标记样式选项,让您的标记更加醒目!
  • 了解适用于 Maps JavaScript API 的其他,通过这些库可实现更多功能,例如地点库可使用每个办公室的地点 ID 显示其评分!

如需继续了解如何在网页上使用 Google Maps Platform 和 3D 的更多方式,请访问以下链接: