Thêm điểm đánh dấu và ảnh động vào Bản đồ 3D chân thực

1. Trước khi bắt đầu

Hướng dẫn này khám phá cách thêm và tạo kiểu cho điểm đánh dấu 3D trong ứng dụng. Bạn cũng sẽ tìm hiểu cách tạo ảnh động cho ứng dụng bằng cách bay đến và xung quanh các vị trí cụ thể.

Hướng dẫn này dựa trên các khái niệm được đề cập trong codelab đầu tiên. Hãy hoàn thành lớp học lập trình đó (nếu bạn chưa hoàn thành) để có được kiến thức cơ bản cần thiết cho ứng dụng này.

Bạn sẽ thực hiện

"Bản đồ hoàn chỉnh có điểm đánh dấu.

Ứng dụng này cung cấp thông tin tổng quan về các văn phòng chính của Google ở Châu Âu. Người dùng có thể chọn một văn phòng, bay vào và xung quanh văn phòng đó để khám phá, sau đó thu nhỏ để quay lại chế độ xem tổng quan. Những tính năng này thường thấy trong các ứng dụng du lịch và khám phá, mang đến trải nghiệm sống động hơn cho người dùng.

Trong lớp học lập trình này, bạn sẽ xây dựng một ứng dụng web 3D có các chức năng sau:

  • Tải Maps JavaScript API một cách linh động.
  • Thêm Điểm đánh dấu 3D vào bản đồ.
  • Định kiểu điểm đánh dấu bằng SVG.
  • Thêm khả năng bay đến và xung quanh các điểm đánh dấu.
  • Trích xuất các vị trí từ mã thành một mảng.

Kiến thức bạn sẽ học được

  • Cách hoạt động của điểm đánh dấu.
  • Cách tạo kiểu cho điểm đánh dấu.
  • Cách hoạt động của ảnh động với các hàm tích hợp.
  • Vị trí đặt máy ảnh khi tạo dáng so với vị trí đặt điểm để tạo khung hình đẹp hơn.
  • Các mẹo hữu ích để chụp thông số máy ảnh nhằm tạo khung hình cho các mục hiệu quả hơn.

Điều kiện tiên quyết

Bạn cần làm quen với các mục ở đây để hoàn tất Lớp học lập trình này. Nếu đã quen thuộc với việc sử dụng Nền tảng Google Maps, hãy chuyển đến Lớp học lập trình.

Các sản phẩm bắt buộc của Nền tảng Google Maps

Trong lớp học lập trình này, bạn sẽ sử dụng các sản phẩm sau đây của Nền tảng Google Maps:

  • API JavaScript cho Maps

Các yêu cầu khác đối với lớp học lập trình này

Để hoàn thành lớp học lập trình này, bạn cần có các tài khoản, dịch vụ và công cụ sau:

  • Tài khoản Google Cloud đã bật tính năng thanh toán.
  • Khoá API Google Maps Platform đã bật API JavaScript của Maps.
  • Kiến thức cơ bản về JavaScript, HTML và CSS.
  • Trình chỉnh sửa văn bản hoặc IDE mà bạn chọn để lưu và chỉnh sửa tệp để xem.
  • Một trình duyệt web để xem tệp trong khi bạn làm việc.

2. Bắt đầu thiết lập

Thiết lập Nền tảng Google Maps

Nếu bạn chưa có tài khoản Google Cloud Platform và dự án đã bật tính năng thanh toán, vui lòng xem hướng dẫn Bắt đầu sử dụng Google Maps Platform để tạo tài khoản thanh toán và dự án.

  1. Trong Cloud Console, hãy nhấp vào trình đơn thả xuống của dự án rồi chọn dự án mà bạn muốn sử dụng cho lớp học lập trình này.

  1. Bật các API và SDK của Nền tảng Google Maps cần thiết cho lớp học lập trình này trong Google Cloud Marketplace. Để làm như vậy, hãy làm theo các bước trong video này hoặc tài liệu này.
  2. Tạo khoá API trong trang Thông tin xác thực của Cloud Console. Bạn có thể làm theo các bước trong video này hoặc tài liệu này. Tất cả yêu cầu gửi đến Nền tảng Google Maps đều yêu cầu khoá API.

3. Quả địa cầu đơn giản

Để bắt đầu xây dựng ứng dụng, bạn cần thiết lập nền tảng. Thao tác này sẽ tạo ra hình ảnh "đá cẩm thạch xanh" của Trái Đất ở dạng cơ bản nhất, như trong hình:

"Hình ảnh hiển thị quả địa cầu khi được thiết lập ban đầu.

Thêm mã cho trang khởi động

Để thêm quả địa cầu vào trang web, bạn cần thêm mã sau vào trang của mình. Thao tác này sẽ thêm một phần cho trình tải cho API Maps Javascript và một hàm khởi tạo tạo phần tử Bản đồ 3D trong trang mà bạn sẽ thêm mã cho các điểm đánh dấu.

Hãy nhớ thêm khoá của riêng bạn (được tạo trong phần thiết lập) vào trang, nếu không, phần tử 3D sẽ không thể khởi chạy.

<!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>

Sau khi hoàn tất việc này, bạn có thể bắt đầu định khung vị trí yêu thích. Bạn sẽ thực hiện việc này trong bước tiếp theo.

4. Khung chế độ xem đầu tiên

Giờ đây, khi bạn đã tạo một bản đồ có chế độ xem địa cầu, bước triển khai tiếp theo là xác định đúng vị trí xuất phát. Điều này cho phép người dùng nắm được thông tin tổng quan tức thì về nơi họ đang làm việc.

Mặc dù ví dụ này tập trung vào các văn phòng của Google ở Châu Âu, nhưng bạn có thể áp dụng phương pháp này cho bất kỳ vị trí nào trên thế giới, từ toàn bộ quốc gia đến một khu vực trong thành phố. Tốc độ và tính linh hoạt của sản phẩm cho phép bạn mở rộng quy mô ứng dụng từ toàn cầu sang cục bộ với ít thay đổi về mã nhất.

Bạn sẽ bắt đầu với khung hình ban đầu để có được Bản đồ 3D như sau:

“Quả địa cầu có tâm là Châu Âu.

Hướng máy ảnh về phía Châu Âu

Để có được màn hình như minh hoạ, bạn cần căn chỉnh khung hình chính xác như thể bạn đang định vị máy ảnh trong không gian nhìn xuống vị trí đó.

Để thực hiện việc này, bạn có thể sử dụng một số tham số trên thành phần điều khiển bản đồ để đặt thông tin chi tiết về máy ảnh. Bạn có thể xem cách các tham số tương tác trong thế giới "thực" được hiển thị trong sơ đồ. Cụ thể, có điểm trung tâm mà máy ảnh đang nhìn vào và khoảng cách từ vị trí bạn đang nhìn đến điểm đó (phạm vi). Bạn cũng cần đặt góc nghiêng của phối cảnh máy ảnh (nếu không, bạn sẽ nhìn thẳng xuống Trái Đất).

&quot;Hình ảnh cho thấy các thông số của máy ảnh.

Chế độ cài đặt cuối cùng, heading (tiêu đề), xác định hướng của máy ảnh. Giá trị này được đo bằng độ lệch so với hướng bắc. Các giá trị này được áp dụng cho phần tử Bản đồ 3D dưới dạng một đối tượng để thiết lập màn hình ban đầu. Bạn có thể thấy điều này trong mã với hàm khởi tạo Phần tử 3D đã cập nhật.

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

Ghi lại thông số máy ảnh

Việc tạo khung cho chế độ xem trong bản đồ 3D đòi hỏi phải đặt máy ảnh một cách chính xác. Điều này có thể khó thực hiện chỉ bằng mã. Để đơn giản hoá quy trình này, hãy sử dụng mẹo hữu ích sau: thêm một hàm vào trang để ghi lại các tham số máy ảnh khi bạn nhấp vào chế độ xem bắt buộc. Các thông số sẽ xuất ra bảng điều khiển, sẵn sàng để sao chép vào phần cài đặt máy ảnh của đối tượng.

Bạn có thể tìm thấy mã mà bạn có thể muốn sử dụng sau này. Mã này được thêm vào mẫu của trang này, mặc dù mã này sẽ không có trong mẫu của các trang tiếp theo vì không cần thiết cho lớp học lập trình này. Tuy nhiên, bạn cần nhớ mã này nếu muốn tạo bản minh hoạ sống động hơn thông qua việc định vị máy ảnh tốt hơn.

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();
});

Lưu ý cách sử dụng hàm stopCameraAnimation. Nếu trang đang phóng to hoặc xoay vòng, bạn nên dừng ảnh động để có thể chụp vị trí trên màn hình tại thời điểm đó. Đoạn mã này cho phép bạn thực hiện việc này. Bạn có thể xem thêm thông tin chi tiết trong tài liệu về stopCameraAnimation.

Ví dụ về kết quả của lượt nhấp, như hiển thị trong bảng điều khiển.

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 }

Văn bản máy ảnh có thể được dùng làm dữ liệu đầu vào json trong nhiều đối tượng trên Bản đồ 3D, đầu ra thứ hai là vị trí điểm thực tế nơi xảy ra lượt nhấp, cũng hữu ích cho việc tạo điểm hoặc bất kỳ nội dung nào để định vị điểm đánh dấu.

Giờ đây, khi trang đã được định khung chính xác, bạn có thể thêm điểm đánh dấu. Hãy chuyển sang bước tiếp theo để tìm hiểu cách thực hiện.

Giải pháp cho phần

Đối với bước này, trang hoàn chỉnh được cung cấp dưới dạng giải pháp để xác minh quá trình triển khai của bạn. (Nếu sao chép, hãy đảm bảo bạn sử dụng khoá API của riêng mình).

<!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. Điểm đánh dấu đơn giản

Trong phần này, bạn sẽ tìm hiểu cách thêm điểm đánh dấu đầu tiên. Trước tiên, bạn sẽ tìm hiểu thông tin chung về điểm đánh dấu.

Bản đồ 3D hỗ trợ việc tạo hai lớp điểm đánh dấu khác nhau, lớp Marker3DElement và lớp Marker3DInteractiveElement. Lớp nào được chọn sẽ phụ thuộc vào việc bạn có muốn bật tính năng nhấp vào điểm đánh dấu hay không. Ngoài ra, về cơ bản, các lớp này giống nhau, vì vậy, trước tiên, bạn sẽ tạo một Marker3DElement rồi "nâng cấp" lớp này lên Marker3DInteractiveElement trong các bước sau.

Bạn có thể xem giải pháp đầy đủ cho bước này tại đây:

&quot;Quả địa cầu có điểm đánh dấu cho biết bước hoàn tất.

Thêm chiều cao cho điểm đánh dấu

Điều đầu tiên cần biết là điểm đánh dấu có dạng 3D giống như mọi thứ khác trong Bản đồ 3D. Điều đó có nghĩa là vị trí có thể có chiều cao (cao độ) và chiều cao đó có thể biểu thị dưới dạng vị trí tương ứng với mực nước biển, mặt đất, lưới hoặc được đặt để cố định trên mặt đất và bỏ qua vị trí cao độ. Bạn có thể xem thêm thông tin chi tiết trong phần Hằng số độ cao trong tài liệu về AltitudeMode.

Bạn cũng có thể đặt xem điểm đánh dấu có được đẩy ra hay không bằng cách sử dụng giá trị extruded. Điều này sẽ xác định xem điểm đánh dấu có một đường nhỏ được vẽ xuống đất để giúp hiển thị vị trí thực tế liên quan đến chiều cao hay không, rất hữu ích để chọn các điểm trên mặt đất. Bạn có thể xem ví dụ về việc này với vị trí Google ở Vương quốc Anh. Cả hai đều được đẩy ra và đặt vị trí thành Chiều cao tuyệt đối. Điểm đầu tiên là 75 mét và điểm thứ hai là 125 mét.

Điểm đánh dấu ở 75 mét

Điểm đánh dấu ở 125 mét

Độ cao 75 mét.

Độ cao 125 mét.

Ẩn hoặc hiện điểm đánh dấu có che khuất và va chạm

Mặc dù điều này có thể không quan trọng trong phần minh hoạ của chúng tôi, vì các vị trí khá xa nhau, nhưng đối với những điểm đánh dấu có thể chồng chéo lên nhau hoặc có thể nằm phía sau các toà nhà, bạn có thể kiểm soát những gì xảy ra với các điểm đánh dấu đó bằng các giá trị collisionBehavior hoặc drawsWhenOccluded.

Đối với hành vi va chạm, bạn có các lựa chọn sau:

  • REQUIRED: (mặc định) Luôn hiển thị điểm đánh dấu bất kể có va chạm hay không.
  • OPTIONAL_AND_HIDES_LOWER_PRIORITY Chỉ hiển thị điểm đánh dấu nếu điểm đánh dấu đó không chồng lên các điểm đánh dấu khác. Nếu hai điểm đánh dấu thuộc loại này trùng lặp, thì điểm đánh dấu có zIndex cao hơn sẽ xuất hiện. Nếu các tệp này có cùng zIndex, thì tệp có vị trí màn hình dọc thấp hơn sẽ xuất hiện.
  • REQUIRED_AND_HIDES_OPTIONAL Luôn hiển thị điểm đánh dấu bất kể có xung đột hay không và ẩn mọi điểm đánh dấu hoặc nhãn OPTIONAL_AND_HIDES_LOWER_PRIORITY trùng lặp với điểm đánh dấu.

Hình ảnh cho thấy sự khác biệt trong cách hiển thị điểm đánh dấu dựa trên hành vi va chạm đã xác định. Tất cả điểm đánh dấu sẽ xuất hiện khi thiết lập REQUIRED, nhưng nếu bạn sử dụng REQUIRED_AND_HIDES_OPTIONAL thì trong trường hợp này, các điểm đánh dấu thấp hơn trên màn hình sẽ xuất hiện (bạn có thể chơi với zIndex để làm cho các điểm đánh dấu khác hiển thị ở trên cùng nếu muốn).

Tất cả điểm đánh dấu đều hiển thị như bắt buộc

Điểm đánh dấu ẩn điểm đánh dấu khác

BẮT BUỘC

REQUIRED_AND_HIDES_OPTIONAL

Đối với trường hợp che khuất, bạn có thể chọn vẽ điểm đánh dấu phía sau các toà nhà hoặc không. Điều này được thể hiện trong hình sau. Khi drawsWhenOccluded được đặt thành true, các điểm đánh dấu sẽ hơi mờ khi được vẽ phía sau các toà nhà, khi được đặt thành false, các điểm đánh dấu sẽ ẩn khi nằm phía sau toà nhà. Bạn có thể xem thêm thông tin chi tiết trong bảng sau:

Hình ảnh cho thấy bản đồ ẩn các điểm đánh dấu bị che khuất

Hình ảnh cho thấy bản đồ hiển thị các điểm đánh dấu bị che khuất

drawsWhenOccluded : false

drawsWhenOccluded : true

Như đã đề cập, các điểm đánh dấu bị ẩn do va chạm sẽ hiển thị mờ nếu bạn cho phép vẽ các điểm đánh dấu bị che khuất. Trong hình ảnh, bạn có thể thấy một số điểm đánh dấu bị các toà nhà che khuất và một số điểm đánh dấu bị các điểm đánh dấu khác che khuất.

&quot;Hình ảnh cho thấy một số điểm đánh dấu và hiệu ứng che khuất.

Hãy tham khảo ví dụ về hành vi va chạm trong bản đồ 2D để biết thêm thông tin chi tiết.

Xoá canvas

Bây giờ, đã đến lúc tạo điểm đánh dấu đầu tiên. Để đảm bảo người dùng tập trung vào các điểm đánh dấu, bạn có thể tắt nhãn mặc định trong bản đồ 3D.

Đặt giá trị mode của phần tử bản đồ 3D thành SATELLITE.

Để biết thêm thông tin, hãy xem phần chế độ.

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

Kết quả là bản đồ 3D sau:

&quot;Hình ảnh Châu Âu không có đường viền và văn bản.

Thêm điểm đánh dấu đầu tiên

Với một canvas trống, bạn có thể thêm điểm đánh dấu đầu tiên. Các tham số chính bao gồm vị trí và nhãn.

Để thêm điểm đánh dấu, hãy đặt vị trí của điểm đánh dấu. Bạn cũng có thể thêm một nhãn xuất hiện phía trên điểm đánh dấu và các phần tử khác như mô tả trong tài liệu về Marker3DElement.

Để thêm điểm đánh dấu, bạn sẽ thêm mã sau đây sau dòng ẩn nhãn mặc định như minh hoạ:

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

map3D.append(marker);

Sau khi tạo điểm đánh dấu, hãy thêm điểm đánh dấu đó vào bản đồ 3D bằng phương thức nối. Hãy nhớ rằng các điểm đánh dấu được lưu trữ dưới dạng một mảng các phần tử con trong bản đồ 3D. Để sửa đổi một điểm đánh dấu, bạn cần truy cập vào điểm đánh dấu đó thông qua mảng này.

Đảm bảo Marker3DElement được tải từ API JavaScript của Maps bằng cách thêm API này vào danh sách thư viện khi tải API.

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

Giờ đây, khi trang tải xong, toàn bộ Châu Âu sẽ hiển thị với một điểm đánh dấu phía trên văn phòng ở London. Như trong ảnh động, bạn có thể phóng to theo cách thủ công để xem điểm đánh dấu trên vị trí đã tạo.

&quot;Ảnh động minh hoạ thao tác thu phóng thủ công vào Google Vương quốc Anh.

Giờ đây, khi bạn đã tải điểm đánh dấu đầu tiên, bước tiếp theo là làm cho điểm đánh dấu đó trông đẹp hơn.

Giải pháp cho phần

Đối với bước này, trang hoàn chỉnh được cung cấp dưới dạng giải pháp để xác minh quá trình triển khai của bạn. (Nếu sao chép, hãy đảm bảo bạn sử dụng khoá API của riêng mình).

<!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. Điểm đánh dấu SVG

Trong bước này, bạn sẽ làm cho điểm đánh dấu trông đẹp hơn bằng cách thay đổi điểm đánh dấu để thêm cờ vào điểm đánh dấu đó nhằm thể hiện quốc gia nơi điểm đánh dấu đó nằm. Hãy cùng xem cách thực hiện việc này và để làm được điều đó, bạn cần làm quen với PinElement.

Cuối cùng, bạn sẽ có giao diện mới như sau:

&quot;Hình ảnh có điểm đánh dấu có hình cờ Vương quốc Anh&quot;

Tuỳ chỉnh cơ bản bằng PinElement

Một trong những phần tử được chia sẻ giữa các điểm đánh dấu trong API JavaScript, cho dù đó là bản đồ 2D hay 3D, là PinElement. Trong khi thêm Marker3DElement vào Map3DElement , bạn thêm PinElement vào Marker3DElement dưới dạng phần tử con của phần tử đó.

PinElement có khả năng, ở cấp độ cơ bản, để thay đổi Marker thông thường nhằm đặt màu đường viền, màu điểm bên trong (hoặc ký tự) và màu nền. Bạn có thể thấy các thông tin này trong hình ảnh thể hiện một Điểm đánh dấu 2D.

&quot;Hình ảnh có các tuỳ chọn để tuỳ chỉnh ghim điểm đánh dấu&quot;

Bạn cũng có thể đặt kích thước điểm đánh dấu thông qua phần tử này bằng cách đặt giá trị tỷ lệ của phần tử (>1 lớn hơn bình thường và <1 nhỏ hơn theo tỷ lệ).

Bạn cũng có thể thay thế Glyph bằng một hình ảnh hoặc tệp svg nếu muốn tạo giao diện tuỳ chỉnh hơn nhưng vẫn giữ nguyên giao diện ghim bản đồ PinElement tiêu chuẩn.

Ngoài PinElements

Ở bước này, bạn sẽ cập nhật PinElement tiêu chuẩn bằng cờ svg và nhiều màu sắc khác nhau. Tuy nhiên, bạn cũng nên lưu ý rằng bạn có thể thay đổi hoàn toàn giao diện của Điểm đánh dấu để nó thậm chí không giống như Ghim trên bản đồ. Trong Marker, bạn cũng có thể chèn đồ hoạ mới thông qua việc sử dụng các mẫu, chẳng hạn như HTMLImageElement và SVGElement. Bạn có thể tìm hiểu thêm thông tin chi tiết về cách thực hiện việc này trong tài liệu Marker3DElement-Slots.

Để xem những gì có thể làm được, hãy xem các mẫu sau đây cho thấy ví dụ về cách tạo kiểu cho Điểm đánh dấu bằng một số kỹ thuật khác nhau.

Hình ảnh cho thấy cách tuỳ chỉnh điểm đánh dấu cơ bản.

Hình ảnh cho thấy cách tuỳ chỉnh điểm đánh dấu phức tạp.

Các điểm đánh dấu có tuỳ chỉnh cơ bản thông qua PinElement, hãy xem mẫu.

Điểm đánh dấu có tuỳ chỉnh phức tạp thông qua mẫu thông qua SVG và Hình ảnh, hãy xem mẫu.

Thêm PinElement

Để thay đổi giao diện của điểm đánh dấu, điều đầu tiên cần làm là đảm bảo bạn đã thêm thư viện PinElement vào trang. Bạn có thể thực hiện việc này bằng cách thêm dòng mã sau đây sau khi nhập thư viện maps3d:

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

Giờ đây, phần tử được tải, bạn có thể tham chiếu và tạo PinElement. Hãy xem mã, thêm mã đó vào giữa vị trí tạo điểm đánh dấu và thêm điểm đánh dấu vào Bản đồ 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);

Vì bạn không chỉ tải một ghim cơ bản nên cần làm một số việc ngoài việc thiết lập PinElement, với màu nền và tỷ lệ tương ứng.

Trước tiên, bạn phải tạo một tham chiếu đến hình ảnh svg cho biểu tượng cờ, trong trường hợp này là biểu tượng Cờ Liên minh. Bạn có thể lấy các biểu tượng này từ một bộ sưu tập như bộ sưu tập này tại https://flagicons.lipis.dev/.

Sau khi có biểu tượng, bạn có thể đặt biểu tượng đó ở vị trí mà trang web có thể xác định. Trong trường hợp này, bạn có thể mã hoá cứng vị trí của hình ảnh hoặc sử dụng vị trí trang web hiện tại làm phần giữ chỗ của thư mục, như minh hoạ ở đây với biến cơ sở. Sau đó, bạn có thể liên kết vị trí này trên máy chủ với cờ bên phải, ở đây là trong '/images/gb.svg'.

Thao tác này sẽ tạo một PinElement có dạng như sau:

&quot;Điểm đánh dấu cho thấy biểu tượng cờ Union Jack.

Vì vậy, sau khi đặt cờ vào đúng vị trí và đặt mã vào đúng vị trí, bạn sẽ có một Bản đồ 3D như sau:

&quot;Đang phóng to điểm đánh dấu mới.

Bây giờ, bạn đã có được điểm đánh dấu hoàn chỉnh, bạn cũng có thể thay đổi điểm đánh dấu đó để có thể nhấp vào để thêm tính tương tác. Bạn sẽ thực hiện việc này trong bước tiếp theo.

Giải pháp cho phần

Đối với bước này, trang hoàn chỉnh được cung cấp dưới dạng giải pháp để xác minh quá trình triển khai của bạn. (Nếu sao chép, hãy đảm bảo bạn sử dụng khoá API của riêng mình).

Ngoài ra, đừng quên bạn cần lấy tệp svg cờ (hoặc tệp png mà bạn chọn!) và lưu trữ tệp đó trong một thư mục mà trang của bạn có thể tìm thấy (ở đây, tệp được lưu trữ trong thư mục hình ảnh).

<!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. Điểm đánh dấu tương tác

Trong bước cuối cùng, một điểm đánh dấu đã được thêm vào trang, nhưng ngoài việc trông đẹp mắt, điểm đánh dấu này không làm được gì nhiều và bạn vẫn phải tương tác với Bản đồ 3D theo cách tương tự. Bước tiếp theo là thêm khả năng thực hiện một thao tác nào đó với điểm đánh dấu khi bạn nhấp vào điểm đánh dấu đó, cho phép điểm đánh dấu phản hồi hoạt động tương tác của người dùng.

Để thêm tính năng này, bạn cần chuyển đổi Marker3DElement thành Marker3DInteractiveElement. Cuối cùng, bạn sẽ có một trang có giao diện tương tự, nhưng khi nhấp vào điểm đánh dấu, một cảnh báo sẽ bật lên và có dạng như sau:

&quot;Hình ảnh hiển thị phản hồi khi người dùng nhấp vào hình ảnh đó.

Trước tiên, hãy thay đổi lớp điểm đánh dấu

Để thêm tính tương tác vào một điểm đánh dấu, bạn cần đảm bảo điểm đánh dấu đó đang sử dụng đúng lớp. Marker3DInteractiveElement là lớp cần thiết, nhưng vì đây là phần mở rộng của Marker3DElement, nên bạn không cần làm gì ngoài việc tải lớp mới và thay đổi tên lớp trên hàm khởi tạo.

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,
});

Tiếp theo, hãy thêm sự kiện nhấp vào điểm đánh dấu

Tiếp theo, hãy thêm một sự kiện nhấp vào điểm đánh dấu để xử lý hoạt động tương tác của người dùng và phản hồi. Trong đoạn mã, bạn có thể thấy sự kiện nhấp được thêm vào điểm đánh dấu. Trong trường hợp này, một cảnh báo sẽ được kích hoạt và bật lên văn bản hiển thị nhãn từ điểm đánh dấu, lấy từ mục tiêu của sự kiện đã kích hoạt cho phép chúng ta truy cập vào thuộc tính nhãn. Thêm mã sau vào ứng dụng ngay sau khi tạo điểm đánh dấu.

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

Lưu ý sự kiện stopPropagation được dùng để đảm bảo rằng mọi trình nghe lượt nhấp khác trong ngăn xếp đều được kích hoạt trên các đối tượng cơ bản như canvas chính của Bản đồ 3D.

Vì vậy, khi chạy ứng dụng, bạn sẽ nhận được kết quả sau:

&quot;Hình ảnh hiển thị phản hồi khi người dùng nhấp vào hình ảnh đó.

Với khả năng thực hiện một thao tác nào đó khi người dùng nhấp vào điểm đánh dấu, bạn có thể thêm một số ảnh động vào trang trong bước tiếp theo.

Giải pháp cho phần

Đối với bước này, trang hoàn chỉnh được cung cấp dưới dạng giải pháp để xác minh quá trình triển khai của bạn. (Nếu sao chép, hãy đảm bảo bạn sử dụng khoá API của riêng mình).

<!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. Chuyển đến

Trong bước này, bạn sẽ sử dụng tính năng nhấp vào điểm đánh dấu để thêm ảnh động bay đến vị trí của điểm đánh dấu đó. Bạn có thể xem ví dụ thực tế tại đây.

&quot;Ảnh động cho thấy điểm đánh dấu được nhấp và bay đến vị trí.

Tạo ảnh động bằng flyCameraTo

Để thêm tính năng này vào trang, bạn sẽ sử dụng phương thức flyCameraTo của Bản đồ 3D, trong đó máy ảnh tạo ảnh động giữa vị trí máy ảnh mà bạn đang ở đến vị trí máy ảnh mà bạn muốn xem, nội suy giữa hai vị trí này và tạo ảnh động cho chuyến bay trong Bản đồ 3D.

Khi sử dụng flyCameraTo, bạn cần chỉ định FlyToAnimationOptions có hai thuộc tính, endCamera là vị trí mà máy ảnh sẽ trỏ vào cuối ảnh động và durationMillis là thời lượng tính bằng mili giây cần thiết để thực hiện quá trình chuyển đổi.

Trong ví dụ này, hãy đặt máy ảnh để nhìn vào toà nhà là vị trí điểm đánh dấu, với độ nghiêng 65 độ, phạm vi 500 mét và hướng về phía Bắc với hướng 0 độ. Đặt thời gian ảnh động là 12500 mili giây (12,5 giây).

Thay thế sự kiện cảnh báo hiện tại trong trang bằng đoạn mã flyCameraTo:

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

   event.stopPropagation();
});

Vậy là xong, giờ bạn có thể làm mới trang rồi nhấp vào điểm đánh dấu và bay đến Google Vương quốc Anh, như trong ảnh động:

&quot;Ảnh động cho thấy điểm đánh dấu được nhấp và bay đến vị trí.

Trong bước này, bạn đã thêm một điểm đánh dấu có thể nhấp vào để đưa máy ảnh đến vị trí của điểm đánh dấu đó. Ở bước tiếp theo, bạn sẽ thêm khả năng bay xung quanh điểm để máy ảnh quay quanh vị trí đó.

Giải pháp cho phần

Đối với bước này, trang hoàn chỉnh được cung cấp dưới dạng giải pháp để xác minh quá trình triển khai của bạn. (Nếu sao chép, hãy đảm bảo bạn sử dụng khoá API của riêng mình).

<!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. Bay xung quanh

Phần tử cuối cùng của ảnh động là sử dụng phương thức flyCameraAround để tạo ảnh động cho một quỹ đạo xung quanh toà nhà. Cuối cùng, bạn sẽ có một ảnh động bay đến toà nhà rồi bay xung quanh toà nhà như trong ảnh động. Đây có thể là một ví dụ hơi nhanh cho một ví dụ thực tế, nhưng hữu ích để cho thấy cách hoạt động của thao tác, mà không quá dài, bạn có thể thử nghiệm với các thời gian cho đến khi nhận được một giá trị phù hợp với mình.

&quot;Ảnh động cho thấy một điểm đánh dấu được nhấp vào, sau đó bay đến và xung quanh một vị trí.

Hãy cùng bay xung quanh!

Phương thức flyCameraAround tương tự như hàm flyCameraTo ở chỗ phương thức này lấy một số tuỳ chọn làm dữ liệu đầu vào để kiểm soát vị trí cần quay quanh, dưới dạng tham số máy ảnh và thời gian (tính bằng mili giây) cần thiết để quay quanh. Cuối cùng, bạn cũng có thể chỉ định số lần xoay có thể xảy ra trong khoảng thời gian đã chỉ định. Bạn có thể xem tất cả các tuỳ chọn tại đây trong FlyAroundAnimationOptions

Nhưng chờ một phút!

Trong ảnh động, bạn có thể thấy ảnh động bay đến vị trí rồi bay xung quanh vị trí đó, tạo thành chuỗi ảnh động. Để thực hiện việc này, bạn sử dụng sự kiện gmp-animationend của Bản đồ 3D để đảm bảo ảnh động hiện tại đã kết thúc trước khi kích hoạt ảnh động tiếp theo. Ảnh động này chỉ xảy ra một lần trước khi dừng.

Hãy xem mã này, chèn mã này vào sau mã đã thêm ở phần trước.

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();
});

Việc thêm khả năng theo dõi sự kiện gmp-animationend cho phép sự kiện này gọi sự kiện flyCameraAround. Việc đặt điểm xuất phát giống với điểm xuất phát được dùng cho máy ảnh kết thúc của phương thức bay đến sẽ giúp quá trình chuyển đổi diễn ra suôn sẻ (để không gây ra bất kỳ chuyển động giật nào đến vị trí mới). Xin nhắc lại rằng durationMillis được đặt để kiểm soát thời lượng cần thiết để tạo ảnh động. Trong trường hợp này, phương thức này cũng có một tuỳ chọn khác là rounds và tuỳ chọn này được đặt thành 1.

Điều này có nghĩa là máy ảnh sẽ xoay quanh điểm đó một lần trong 5 giây. Bạn có thể thử nghiệm các giá trị này cho phù hợp để tìm ra con số phù hợp với mình.

Tại thời điểm này, ảnh động sẽ kết thúc, nhưng bạn không muốn sự kiện gmp-animationend kích hoạt lại bằng đoạn mã này, điều này sẽ dẫn đến quỹ đạo diễn ra vô hạn. Để tránh điều này, hãy đặt tuỳ chọn cho trình nghe với chế độ cài đặt once (một lần) bằng true. Điều này có nghĩa là sự kiện sẽ bị xoá sau khi hoàn tất, tránh vòng lặp vô hạn.

Sau khi thêm phần này, bạn có thể chạy giải pháp và thấy ảnh động bay xung quanh điểm đánh dấu ở cuối, như trong ảnh động:

&quot;Ảnh động cho thấy một con chim bay xung quanh một điểm đánh dấu.

Ở bước này, bạn đã thêm một điểm đánh dấu có thể nhấp vào, sau đó máy ảnh sẽ bay đến và xung quanh vị trí điểm đánh dấu. Trong giai đoạn tiếp theo, đã đến lúc bắt đầu thêm các điểm khác và cho phép chúng ta di chuyển giữa các điểm đó.

Giải pháp cho phần

Đối với bước này, trang hoàn chỉnh được cung cấp dưới dạng giải pháp để xác minh quá trình triển khai của bạn. (Nếu sao chép, hãy đảm bảo bạn sử dụng khoá API của riêng mình).

<!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!

Mặc dù London là một thành phố tuyệt vời, nhưng có vẻ như thành phố này hơi cô đơn trên trang. Vì vậy, hãy bắt đầu thêm một số vị trí mới, bắt đầu bằng Paris. Để thực hiện việc này, bạn có thể sử dụng một mảng để lưu giữ tất cả thông tin chi tiết cụ thể về vị trí, sau đó sử dụng thông tin đó làm dữ liệu đầu vào cho các hàm và biến đặt tham số hiển thị Điểm đánh dấu cũng như bay đến và xung quanh vị trí của máy ảnh. Như đã đề cập, vị trí này có thể khác với vị trí điểm đánh dấu để chụp ảnh một toà nhà đẹp hơn.

&quot;Ảnh động cho thấy một lượt nhấp và bay đến và xung quanh Google Pháp.

Mảng vị trí

Để không phải mã hoá cứng tất cả thông tin chi tiết về một vị trí cụ thể, chẳng hạn như máy ảnh xem, điểm đánh dấu và các tuỳ chọn hiển thị, bạn có thể sử dụng một mảng nhỏ các đối tượng JSON để lưu trữ dữ liệu này. Sau đó, bạn có thể áp dụng khi tạo và sử dụng các điểm đánh dấu trong ứng dụng. Bạn có thể xem ví dụ này trong đoạn mã, tạo một biến có tên là officeLocations để lưu trữ mảng.

Thêm mã sau vào ngay trước hàm khởi tạo. Ngoài ra, hãy lưu ý rằng biến cơ sở đã được di chuyển ra ngoài hàm khởi tạo để có thể áp dụng cho tất cả các vị trí văn phòng.

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("/"));

Mỗi vị trí văn phòng có các thuộc tính sau:

  • name : tên của vị trí.
  • camera : chế độ xem ban đầu để xem vị trí bay đến và xung quanh.
  • point : vị trí đặt điểm đánh dấu.
  • pin : thông tin chi tiết về màu sắc và thuộc tính ký tự của ghim điểm đánh dấu

Một góc khác

Bạn có thể nhận thấy ở đây rằng đối với Vương quốc Anh, tâm máy ảnh và điểm đánh dấu giống nhau (ngoài độ cao), trong khi đối với Pháp, máy ảnh và điểm đánh dấu khác nhau. Lý do là đối với vị trí ở Pháp, điểm đánh dấu cần ở một vị trí khác với chế độ xem ban đầu của máy ảnh. Điều này giúp người dùng có được tầm nhìn tốt hơn về toàn bộ toà nhà khi bay đến và xung quanh so với tầm nhìn có thể có nếu sử dụng điểm đánh dấu.

Quay lại Châu Âu

Một chức năng của việc có nhiều điểm hơn là thêm một yêu cầu để có thể di chuyển giữa các điểm đó. Bạn có thể sử dụng trình đơn thả xuống để cho phép lựa chọn, nhưng trong ví dụ này, máy ảnh sẽ quay lại chế độ xem Châu Âu mỗi khi cho phép người dùng chọn một vị trí khác.

Để thực hiện việc này, chế độ xem ban đầu cần được lưu trữ trong một biến có thể dùng để đặt lại máy ảnh về toàn bộ chế độ xem Châu Âu. Trong ví dụ này, hãy thêm một biến mới có tên là europeCamera để lưu trữ giá trị này để sử dụng sau.

Cập nhật hàm khởi tạo

Nội dung chỉnh sửa đầu tiên bạn cần thực hiện là sử dụng đối tượng europeCamera làm dữ liệu đầu vào khi tạo Map3DElement.

Nội dung chỉnh sửa thứ hai mà bạn cần thực hiện là gói phần tạo điểm đánh dấu trong vòng lặp để cập nhật phần đó bằng các tham số được lưu trữ trong biến. Bạn có thể xem các tham số này trong mã sau:

  • office.point : vị trí điểm đánh dấu.
  • office.name : tên văn phòng được dùng cho nhãn của điểm đánh dấu.
  • office.camera : vị trí ban đầu của máy ảnh.
  • office.pin : các tuỳ chọn PinElement cho sự khác biệt về hiển thị

Ngoài ra, đừng quên tải tệp hoặc hình ảnh svg cho cờ Pháp!

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);
}

Lưu ý rằng một hàm gmp-animationend thứ hai được thêm vào sau ảnh động flyCameraAround để xử lý việc quay lại chế độ xem Châu Âu, sử dụng biến europeCamera đã lưu trữ. Như trong ảnh động:

&quot;Ảnh động bay giữa và xung quanh các văn phòng ở Pháp và Vương quốc Anh.

Ở giai đoạn này, ứng dụng đã được mở rộng để có hai vị trí và khả năng bay giữa các vị trí đó bằng ảnh động và một mảng vị trí. Ở bước tiếp theo, các vị trí văn phòng còn lại sẽ được thêm vào mảng.

Giải pháp cho phần

Đối với bước này, trang hoàn chỉnh được cung cấp dưới dạng giải pháp để xác minh quá trình triển khai của bạn. (Nếu sao chép, hãy đảm bảo bạn sử dụng khoá API của riêng mình).

Ngoài ra, đừng quên bạn cần lấy tệp svg cờ (hoặc tệp png mà bạn chọn) và lưu trữ tệp đó trong một thư mục mà trang của bạn có thể tìm thấy (ở đây, tệp được lưu trữ trong thư mục hình ảnh).

<!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. Địa điểm khác

Mặc dù ứng dụng hiện đã có tất cả các tính năng cần thiết, nhưng Bản đồ 3D vẫn có vẻ hơi thưa thớt. Vì vậy, bạn sẽ thêm một vài vị trí khác để bản đồ trở nên sinh động hơn. Thông qua việc sử dụng một mảng, bạn có thể dễ dàng điền sẵn các vị trí mới, với các điểm đánh dấu riêng biệt. Bước cuối cùng là tiếp tục thêm điểm đánh dấu cho đến khi có chế độ xem sau.

&quot;Hình ảnh cho thấy tất cả các văn phòng.

Thêm điểm đánh dấu khác.

Google có một số văn phòng tại nhiều quốc gia ở Châu Âu. Hãy thêm một số văn phòng đó vào bản đồ. Bạn chỉ cần cập nhật mảng. Nội dung này có thể được lấy từ một dịch vụ web hoặc phân phát từ một tệp tĩnh ở đâu đó, trong trường hợp của chúng ta, để đơn giản, nội dung này sẽ được giữ trong cùng một trang.

Bạn có thể thêm bao nhiêu điểm đánh dấu tuỳ thích. Trang sẽ nhận các điểm đánh dấu này rồi tự động thêm vào thành phần hiển thị. Hãy nhớ lấy cờ chính xác và lưu trữ chúng trong thư mục hình ảnh (hoặc bất cứ nơi nào thuận tiện).

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,
       },
   }
]

Sau khi bạn thực hiện việc này, bạn sẽ thấy một trang hoàn chỉnh như hình ảnh hiển thị, cho phép người dùng nhấp vào vị trí bất kỳ và bay đến và xung quanh vị trí đó, sau đó quay lại!

&quot;Ảnh động bay giữa và xung quanh các văn phòng ở Tây Ban Nha và Thuỵ Điển.

Xin chúc mừng bạn đã hoàn thành lớp học lập trình này! Hãy cùng chúng tôi kết thúc trong phần tiếp theo và tìm hiểu những điều mới mẻ khác để thử!

Giải pháp cho phần

Đối với bước này, trang hoàn chỉnh được cung cấp dưới dạng giải pháp để xác minh quá trình triển khai của bạn. (Nếu sao chép, hãy đảm bảo bạn sử dụng khoá API của riêng mình).

Ngoài ra, đừng quên bạn cần lấy tệp svg cờ (hoặc tệp png mà bạn chọn) và lưu trữ tệp đó trong một thư mục mà trang của bạn có thể tìm thấy (ở đây, tệp được lưu trữ trong thư mục hình ảnh).

<!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. Các bước tiếp theo

Trong lớp học lập trình này, bạn đã tìm hiểu những điều cơ bản về những việc bạn có thể làm với API JavaScript 3D trong Maps. Tiếp theo, hãy thử thêm một số tính năng sau vào bản đồ:

  • Thêm danh sách thả xuống để cho phép chọn văn phòng.
  • Hãy sử dụng một số tuỳ chọn tạo kiểu điểm đánh dấu khác để tạo thêm hiệu ứng!
  • Hãy khám phá các thư viện bổ sung có sẵn cho Maps JavaScript API để bật các tính năng bổ sung, chẳng hạn như Địa điểm để hiển thị điểm xếp hạng của từng văn phòng bằng cách sử dụng Mã địa điểm!

Để tiếp tục tìm hiểu thêm về các cách bạn có thể làm việc với Nền tảng Google Maps và 3D trên web, hãy xem các đường liên kết sau: