1. 事前準備
本教學課程將探討如何在應用程式中新增 3D 標記並設定樣式。您也會瞭解如何飛往特定地點,並在該地周圍飛行,為應用程式製作動畫。
本教學課程的設計是以第一個codelab中涵蓋的概念為基礎。如果您尚未完成該程式碼研究室,請先完成該程式碼研究室,取得這個應用程式所需的基本知識。
執行步驟
這項應用程式提供歐洲主要 Google 辦公室的總覽。使用者可以選取辦公室,並在辦公室內部和周圍四處飛行探索,然後縮小畫面,返回一般檢視畫面。這些功能通常出現在旅遊和探索應用程式中,可為使用者提供更身歷其境的體驗。
在本程式碼研究室中,您將建構 3D 網頁應用程式,執行以下操作:
- 動態載入 Maps JavaScript API。
- 在地圖中加入 3D 標記。
- 使用 SVG 設定標記的樣式。
- 新增飛往標記和在標記周圍飛行的功能。
- 將程式碼中的地點抽象化為陣列。
課程內容
- 標記的運作方式。
- 如何設定標記樣式。
- 動畫如何搭配內建函式運作。
- 擺放攝影機的位置與點選位置,以便取得更好的構圖。
- 擷取相機參數的實用技巧,可協助您更妥善地構圖。
必要條件
您必須熟悉這裡的項目,才能完成本程式碼研究室。如果您已熟悉 Google 地圖平台的使用方式,請直接前往程式碼研究室。
必要的 Google 地圖平台產品
在本程式碼研究室中,您將使用下列 Google 地圖平台產品:
- Maps JavaScript API
本程式碼研究室的其他需求
如要完成本程式碼研究室,您需要下列帳戶、服務和工具:
- 已啟用帳單功能的 Google Cloud 帳戶。
- 已啟用 Maps JavaScript API 的 Google 地圖平台 API 金鑰。
- 具備 JavaScript、HTML 和 CSS 的基本知識。
- 您選擇的文字編輯器或 IDE,用於儲存及查看編輯過的檔案。
- 網路瀏覽器,可用於在工作時查看檔案。
2. 做好準備
設定 Google 地圖平台
如果您尚未建立 Google Cloud Platform 帳戶,以及已啟用帳單功能的專案,請參閱「開始使用 Google 地圖平台」指南,建立帳單帳戶和專案。
- 在 Cloud 控制台中,點選專案下拉式選單,然後選取要用於本程式碼研究室的專案。
- 在 Google Cloud Marketplace 中啟用本程式碼研究室所需的 Google 地圖平台 API 和 SDK。如要進行這項操作,請按照這部影片或這份說明文件中的步驟操作。
- 在 Cloud 控制台的「憑證」頁面中產生 API 金鑰。您可以按照這部影片或這份說明文件中的步驟操作。所有 Google 地圖平台要求都需要 API 金鑰。
3. 簡易地球儀
如要開始建構應用程式,您必須先完成基礎設定。這會產生「藍色地球」的地球影像,如下圖所示:
新增範例頁面的程式碼
如要將地球儀加入網站,請在網頁中加入下列程式碼。這會為 Maps JavaScript API 的載入器新增一節,以及一個初始化函式,用於在您新增標記程式碼的頁面中建立 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 地圖中設定視框需要精確的相機位置,而這項操作很難只透過程式碼完成。為了簡化這個程序,請使用這個實用的技巧:在頁面中加入函式,在您點選所需檢視畫面時擷取相機參數。系統會將參數輸出至控制台,方便您複製至物件的相機設定。
您可以找到稍後可能會用到的程式碼,我們已將該程式碼加入本頁的範例中 (但不會加入後續頁面的範例,因為程式碼研究室不需要該程式碼)。如果您想透過更佳的攝影機位置,製作更身歷體驗的示範,請記住這點。
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 英國為例,兩者都經過擠出處理,且位置設為絕對高度。第一個在 75 公尺,第二個在 125 公尺。
海拔高度 75 公尺。 | 海拔高度 125 公尺。 |
隱藏或顯示遮蔽和碰撞標記
雖然在我們的示範中,由於位置相距甚遠,因此不太重要,但如果標記可能彼此重疊,或可能落在建築物後方,您可以使用 collisionBehavior 或 drawsWhenOccluded 值,控制標記的顯示方式。
您可以使用下列選項設定衝突行為:
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 時,標記會在建築物後方時隱藏。詳情請參閱下表:
|
|
如前所述,如果允許繪製遮蔽的標記,遭到遮蔽的標記會以較暗的顏色顯示。在圖片中,您可以看到部分標記被建築物遮住,部分標記則被其他標記遮住。
詳情請參閱 2D 地圖中的碰撞行為範例。
清除畫布
接下來,我們要建立第一個標記。為確保使用者能專注於標記,您可以停用 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);
建立標記後,請使用附加方法將標記新增至 3D 地圖。請注意,標記會儲存為 3D 地圖中的子元素陣列。如要修改標記,您必須透過這個陣列存取標記。
載入 API 時,請將 Marker3DElement
新增至程式庫清單,確保系統會從 Maps JavaScript API 載入 Marker3DElement
。
const { Map3DElement, MapMode, Marker3DElement } = await google.maps.importLibrary("maps3d");
現在,當網頁載入時,整個歐洲地區都會顯示在倫敦辦公室上方,並標示出地點。如動畫所示,您可以手動放大,查看建立位置上的標記。
您已載入第一個標記,接下來要讓標記看起來更美觀。
區塊解決方案
在這個步驟中,我們會提供完成的網頁,讓您驗證實作方式。(如果複製,請務必使用自己的 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 進行基本自訂
無論是 2D 或 3D 地圖,在 JavaScript API 中,標記之間共用的元素之一就是 PinElement。在將 Marker3DElement 新增至 Map3DElement 時,您會將 Marker3DElement 新增至 PinElement,做為該元素的子項。
PinElement 包含基本層級的功能,可變更一般標記,設定邊框顏色、內部點 (或字符) 顏色和背景顏色。您可以在顯示 2D 標記的圖片中看到這些資訊。
您也可以透過元素設定標記大小,方法是設定其比例值 (大於 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/ 等集合中取得這些圖示。
取得圖示後,您可以將圖示放在網站可找到的位置,在這種情況下,您可以將圖片位置硬式編碼,或使用目前的網站位置做為目錄的存根,如同這裡的基礎變數。接著,您可以將這個圖片連結至伺服器上的正確標記位置,如圖所示,位於 '/images/gb.svg' 底下。
這會建立 PinElement,如下所示:
因此,只要將標記放在正確位置,並將程式碼放在正確位置,您應該會看到如下所示的 3D 地圖:
現在標記已完成設定,您也可以將其設為可點選,以便新增互動功能。您將在下一個步驟中執行這項操作。
區塊解決方案
在這個步驟中,我們會提供完成的網頁,讓您驗證實作方式。(如果複製,請務必使用自己的 API 金鑰)。
另外,請記得取得旗幟 svg (或您選擇的 png 檔案),並將其儲存在網頁可找到的目錄中 (這裡是儲存在圖片資料夾中)。
<!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. 巴黎!
雖然倫敦是個很棒的城市,但在頁面上看起來有點單調,因此我們要開始新增一些新地點,先從巴黎開始。為此,您可以使用陣列來儲存所有位置特定詳細資料,然後將其用作函式和變數的輸入內容,藉此設定標記顯示參數,以及飛往及飛越攝影機位置的路徑。如前所述,為了讓相機拍攝到更理想的建築物影像,你可能需要將標記點位置設在與建築物不同的位置。
位置陣列
為了避免硬式編碼特定位置的所有詳細資料 (例如觀看攝影機、標記點和顯示選項),您可以使用小型 JSON 物件陣列來儲存這些資料。之後,您可以在應用程式中建立及使用標記時套用這項設定。您可以在程式碼片段中查看這個範例,建立名為 officeLocations
的變數來儲存陣列。
請在初始化函式前方加入下列程式碼。請注意,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 : 位置名稱。
- 相機:初始檢視畫面,用於查看要飛往的位置和周圍環境。
- 點:標記放置的位置。
- pin:標記圖釘的顏色和字符屬性詳細資料
不同角度
你可能會注意到,在英國,相機中心和標記點相同 (除了高度),但在法國,相機和點不同。這是因為法國的標記必須位於與初始相機檢視畫面不同的位置,這樣在飛往和飛越整棟建築物時,就能獲得比使用標記點更清晰的畫面。
返回歐洲
增加點數的其中一個功能,就是新增在點數之間移動的必要條件。您可以使用下拉式選單讓使用者進行選取,但在本例中,相機每次都會回到歐洲地圖,讓使用者選取其他位置。
為此,初始檢視畫面必須儲存在可用於將相機重設為整個歐洲區域的變數中。在本範例中,新增名為 europeCamera
的新變數,以便儲存此值供日後使用。
更新初始化函式
您需要先在建立 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 檔案) 檔案,並將檔案儲存在網頁可找到的目錄中 (這裡是儲存在圖片資料夾中)。
<!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 在歐洲許多國家/地區設有辦公室,因此我們會將其中一些加入地圖。只要更新陣列即可。這可以來自網頁服務,也可以來自某處的靜態檔案,在本例中,為了簡單起見,我們會將其保留在同一頁面中。
您可以視需要新增任意數量的標記,系統會自動將這些標記加入檢視畫面。請記得取得正確的標記,並將其儲存在圖片目錄 (或任何方便的位置) 中。
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,
},
}
]
完成後,您應該會看到完整的頁面,如下圖所示,使用者可以點選任何位置,並飛往該位置並環繞該位置,然後返回!
恭喜您完成程式碼研究室課程,讓我們在下一節總結,並找出其他新內容來嘗試!
區塊解決方案
在這個步驟中,我們會提供完成的網頁,讓您驗證實作方式。(如果複製,請務必使用自己的 API 金鑰)。
另外,請記得取得旗標 SVG (或您選擇的 PNG 檔案) 檔案,並將檔案儲存在網頁可找到的目錄中 (這裡是儲存在圖片資料夾中)。
<!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. 後續步驟
在本程式碼研究室中,您已瞭解如何在 Maps JavaScript API 中使用 3D 功能。接著,請嘗試在地圖中加入下列部分功能:
- 新增下拉式清單,讓使用者選取辦公室。
- 使用其他標記樣式選項,讓圖示更醒目!
- 請查看 Maps JavaScript API 提供的其他程式庫,啟用其他功能,例如使用 Places 的 ID 顯示每個辦公室的評分!
如要進一步瞭解如何使用 Google 地圖平台和 3D 網路服務,請參閱下列連結: