Jeff的隨手筆記

學習當一個前端工程師

0%

Google API串接

會打這一篇主要還是因為想把做Side Project時查找的資料做一個整理,方便之後如果要在串接一次Google API時可以方便查閱。

那我們就開始吧!

申請Google API 金鑰

為了使用 Google 提供的服務,我們首先必須前往 Google Cloud 申請 API 金鑰。

以下是申請 API 金鑰的步驟:

  1. 首先,建立一個新的專案。

  2. 選擇您剛建立的專案。

  3. 在左側導覽欄中,找到 “憑證” 連結並點擊它。

  4. 在憑證頁面上,點擊 “建立憑證”。

  5. 從選單中選擇 “API 金鑰”。

這樣我們就獲得了 API 金鑰。若需進一步設定 API 金鑰的使用限制,您可以參考以下文章:**Google 地圖 API 金鑰使用限制設定**

選擇Google API

我們可以在 “API 和服務” 頁面中啟用所需的 API 和服務。。

在這個頁面上,我們可以將需要的 API 添加到我們的專案中。以下是一些必須選擇的 API:

  • Maps JavaScript API(用於顯示地圖本身): 使用 Maps JavaScript API,您可以在網站或應用程式中嵌入互動式 Google 地圖,自定義地圖樣式,添加標記、路線、交通信息等。
  • Places API(用於地點搜索): 使用 Places API,您可以在應用程式中實現功能,如附近地點的搜索、自動完成地點建議、評價和評論的檢索等。
  • Geocoding API(將地址或地點名稱轉換為地理坐標): Geocoding 是將地址或地點名稱轉換為地理坐標(緯度和經度)的過程。它可用於在地圖上標記特定位置或進行地理位置分析。

當然,根據我們的專案需求,還有其他 API可以選擇。

在 React渲染 Google 地圖

使用的套件

在這次專案中,我們使用了 react-google-maps/api 套件。

您可以使用以下命令安裝此套件:**npm i @react-google-maps/api**

這時候我們就可以直接導入我們所需要的Hook了:

1
import { GoogleMap, MarkerF, useJsApiLoader } from "@react-google-maps/api";

我們導入了3個Hook,下面將要來解說這3個分別是幹嘛的:

MarkerF hook

MarkerF 是一個用於在 Google 地圖上標記特定位置的 React 元件。可以使用它來添加多個標記,每個標記代表一個地點或位置。

有在網路上查過資料的朋友可能會有疑問?為什麼網路上很多資料都是使用Marker 為什麼現在你是使用MarkerF呢?

我一開始是使用 **Marker** ,但是在實作過程中,我發現在我的頁面上卻無法顯示畫面。

在經歷一番波折後,我在stack overflow 上找到答案:

The problem that you’re having is something introduced by React 18 related to Strict Mode. If you remove Strict Mode from your app, the markers should appear. That also explains why Smokey Dawson’s app worked in production — I’m guessing they removed Strict Mode.

for react 18+ you have to import MarkerF instead of Marker and use in it as a tag(of course)

簡單來說,由於 React 18 的 Strict Mode,我們在使用 Marker 時可能會遇到問題。為了解決這個問題,我們改用了 **MarkerF**。

useJsApiLoader hook

useJsApiLoader() 是專為 Google Maps JavaScript API 設計的 Hook。它用於載入和管理 Google Map相關的 JavaScript 資源。這個 Hook 對於需要在應用程式中使用 Google Map的情況非常有用。它提供了特定於 Google Map的配置選項和輔助函數,以更輕鬆地集成 Google Map。

除了 **useJsApiLoader()**,還有一個名為 useLoadScript() 的 Hook,它由 React Query 提供,用於動態載入資源。我們可以使用 useLoadScript() 來載入各種不同類型的資源,不僅僅是只能用在 Google Maps JavaScript API。

如果以簡單的小專案來說,用哪一個都沒有關係。

GoogleMap hook

這是一個 React 元件,它代表了 Google 地圖。通過在應用程序中使用這個元件,你可以將 Google 地圖集成到你的網頁中。

GoogleMap 提供了預設的 props:

  • Zoom:設定地圖的初始縮放級別
  • center:設定地圖的預設中心
  • mapContainerClassName:指定 GoogleMap 元件的高度和寬度的 CSS 類別名稱

要記住!如果我們不傳遞這些預設屬性,那麼 Map 元件將會崩潰,因為它沒有必要的資料。

定義地圖的選項

當我們渲染出地圖後會看到很多的預設功能,例如顯示道路地圖還是衛星地圖等等,這邊我們都可以自行設定我們渲染出來的地圖要有哪先功能,就以我的專案來說我使用了以下的功能:

1
2
3
4
5
6
7
8
9
10
// 定義地圖的選項
const options = useMemo(
() => ({
disableDefaultUI: false,
clickableIcons: false,
zoomControl: true,
mapTypeControl: false,
}),
[]
);

這別題一件事情:在這邊使用useMemo主要的目的是為了性能優化。

我們知道在 React 中計算某個值,並且這個值在渲染過程中不經常變化時,使用 useMemo 可以避免在每次渲染時都重新計算這個值。而在這裡**options** 是一個靜態的地圖選項物件,它的值不會在component 渲染過程中改變。因此,使用 useMemo 可以確保這個值只在初始渲染時計算一次,並在後續的渲染中重複使用,從而節省性能。

回到要解釋的內容:

  1. disableDefaultUI: 禁用地圖的默認介面元素,例如縮放控制、地圖類型切換器等。在這它被設置為 **false**,表示不禁用默認介面元素。
  2. clickableIcons: 是否允許點擊地圖上的圖示(如標記)來執行操作。在這它被設置為 **false**,表示不允許點擊圖示。
  3. zoomControl: 是否顯示縮放控制元件,允許用戶手動縮放地圖。在這被設置為 **true**,表示顯示縮放控制。
  4. mapTypeControl: 是否顯示地圖類型控制元件,用戶可以在不同地圖類型之間切換(如道路地圖、衛星地圖等)。在這它被設置為 **false**,表示不顯示地圖類型控制。

這些選項只是 Google Maps JavaScript API 提供的一部分,還有許多其他選項可供選擇,具體選擇哪些選項取決於專案需求。

定位使用者所在地

現在地圖準備好了,我們要來讓地圖的中心出現在我們的所在地,這時候我們就需要利用到Geolocation API

這是一個瀏覽器內建的 API,身處在 navigator 這個 object 當中。在MDN所述,可以透過 navigator.geolocation 當中的 getCurrentPosition() 取得當前位置等資訊。

語法如下:

1
navigator.geolocation.getCurrentPosition(success, error, options)

參數解釋:

  • success
    成功取得位置訊息時的callback function
  • error (可選)
    無法取得位置訊息時的callback function
  • option (可選)
    可以放置一個PositionOptions Object

在我自己的專案我是要它一進到這個頁面就要去抓取這個資訊,因此我使用了useEffect 這個hook,而他的 dependencies我讓它保持是空的,讓它只會在component創建時執行一次:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 拿取當前使用者位子
useEffect(() => {
navigator.geolocation.getCurrentPosition(
(position) => {
setUserLocation({
lat: position.coords.latitude,
lng: position.coords.longitude,
});
},
() => {
console.log("無法顯示或拒絕定位");
}
);
}, []);

當然也會遇到使用者不同意瀏覽器取得他的資訊,因此我們也必須要設定一個預設的初始值:

1
2
3
4
// 設定預設中心點(台北101)
const defaultCenter = { lat: 25.033671, lng: 121.564427 };

const [userLocation, setUserLocation] = useState(defaultCenter); //存放使用者座標

並在一開始就把預設的值存在state裡面,這樣當我們無法抓取到使用這的位置時地圖中心還是會出現在我們預設的地方。

移動畫面圖標會始終在畫面的中心

現在我們希望在移動地圖時,將地圖中心點的座標同步到我們的應用程式中,以實現畫面上的圖標始終位於地圖中心。

要實現這一功能,我們需要使用 onLoad 事件。在 Google 地圖組件中,這個事件提供了一個方便的方式,在地圖初始化後執行自定義的操作,確保地圖以預期的方式運作。

1
2
3
<GoogleMap
onLoad={onLoad}
>

因此我們先來設定我們要在地圖初始化後要做什麼動作。

首先,我們定義了一個 screenCenterRef 變數,它是一個 useRef 的參考,用於存儲螢幕中心點的相關信息。然後,我們使用 onLoad 回調函數來處理地圖載入後的操作,將螢幕中心點的資訊存儲在 screenCenterRef 中並更新 screenCenter 狀態。

1
2
3
4
5
const screenCenterRef = useRef();
const onLoad = useCallback((screenCenter) => {
screenCenterRef.current = screenCenter;
setScreenCenter(screenCenter);
}, []);

其實這時候我們就完成了這項功能,但是當我們打開console時,就會發現一個很嚴重的效能浪費,

我們無論何時拖動地圖,都立即執行資訊抓取操作,這個是我不想要的。

為了解決這個問題,我們使用了 setTimeout 函數,設置一個計時器,僅在停止地圖拖曳 500 毫秒後才執行後續的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const [timer, setTimer] = useState(null); //存放setTimeout的計時器
const centerChangHandler = () => {
if (timer) {
clearTimeout(timer);
}

const newTimer = setTimeout(() => {
if (screenCenter) {
const newCurrentCenter = screenCenter.getCenter();
setCurrentPosition({
lat: newCurrentCenter.lat(),
lng: newCurrentCenter.lng(),
});
}
}, 500);
setTimer(newTimer);
};

centerChangeHandler 函數中,我們首先檢查是否存在之前設置的計時器 **timer**,如果存在,則使用 clearTimeout 函數來取消計時器。接著,我們設置一個新的計時器,它將在延遲 500 毫秒後執行指定的操作。這個操作是抓取當前螢幕中心點的座標並更新應用程式的狀態,以確保畫面上的圖標位置與地圖中心點保持一致。

結束

附上這個component回傳的code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
return (
<div className={style.container}>
<GoogleMap
zoom={16}
center={userLocation}
options={options}
mapContainerClassName={style.googleMap}
onLoad={onLoad}
onCenterChanged={centerChangHandler}
>
<MarkerF position={userLocation} icon={currentIcon} />
</GoogleMap>
</div>
);
}

以上就是一個簡單的Google map api的串接練習,提供給大家參考。

後面可以依照需求增加更多的功能,這次就先介紹到這裡了!