JavaScript >> Javascript 文檔 >  >> Tags >> Next.js

如何在 Next.js 中使用 Google Maps 渲染帶有標記的地圖

如何使用 Next.js 在 React 組件內渲染帶有標記的 Google 地圖,並根據標記邊界為該地圖設置動畫。

開始使用

對於本教程,我們將使用 CheatCode Next.js 樣板作為我們工作的起點。首先,讓我們克隆一個副本:

終端

git clone https://github.com/cheatcode/nextjs-boilerplate

接下來,我們需要安裝樣板的依賴項:

終端

cd nextjs-boilerplate && npm install

最後,啟動樣板:

終端

npm run dev

有了這個,我們就可以開始了。

通過 CDN 添加谷歌地圖

在我們實現我們的地圖之前,我們需要訪問 Google Maps JavaScript API。為了獲得訪問權限,我們將使用 API 的官方 Google CDN 鏈接:

/pages/_document.js

import Document, { Html, Head, Main, NextScript } from "next/document";
import { ServerStyleSheet } from "styled-components";
import settings from "../settings";

export default class extends Document {
  static async getInitialProps(ctx) { ... }

  render() {
    const { styles } = this.props;

    return (
      <Html lang="en">
        <Head>
          <meta httpEquiv="Content-Type" content="text/html; charset=utf-8" />
          <meta name="application-name" content="App" />
          ...
          <script
            src={`https://maps.googleapis.com/maps/api/js?key=${settings?.googleMaps?.apiKey}&callback=initMap&libraries=&v=weekly`}
            async
          ></script>
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

上面,在 /pages/_document.js <Head></Head> 中包含在樣板文件中的文件 標籤,我們已經粘貼在 <script></script> Google 建議在網頁中包含 Google Maps JavaScript API 的標記。

因為這個文件很大,我們在 <Head></Head> 中壓縮了一些其他的標籤 帶有 ... 的標記 .您要放置自己的 <script></script> 的位置 標籤就在結束 </Head> 之前 標記。

值得注意的是,在這裡,我們更改了 src 我們從谷歌獲得的標籤上的屬性允許我們使用字符串插值,以便我們可以通過我們的設置文件傳遞我們的谷歌地圖 API 密鑰。在我們使用的樣板文件中,/settings/index.js 文件負責自動加載相應的/settings/settings-<env>.js的內容 <env> 部分等於 process.env.NODE_ENV 的當前值 ,或者,應用程序運行的當前環境(對於本教程,developmentsettings-development.js )。

如果您還沒有 Google Maps API 密鑰,請先在此處了解如何創建密鑰,然後再繼續。

回到我們的 /pages/_document.js 文件,我們可以導入 settings 來自 /settings/index.js 並引用我們的 settings-<env>.js 中的值 文件。在這裡,我們希望該文件包含一個具有 googleMaps 的對象 屬性和嵌套的 apiKey 值,像這樣:

/settings/settings-development.js

const settings = {
  googleMaps: {
    apiKey: "Paste Your API Key Here",
  },
  graphql: {
    uri: "http://localhost:5001/api/graphql",
  },
  ...
};

export default settings;

有了所有這些,現在,當我們加載我們的應用程序時,我們將擁有一個全局 google 可用的值將具有 .maps 我們將用於與庫交互的對象。

設置全局地圖樣式

加載了 Google Maps API 後,接下來,我們要創建我們的應用程序。在我們開始之前,為了我們的演示,我們想為我們的應用添加一些全局 CSS 樣式,以便在應用中全屏顯示我們的地圖:

/pages/_app.js

...
import { createGlobalStyle } from "styled-components";
...

const GlobalStyle = createGlobalStyle`
  :root {
    ...
  }

  ${pong} /* CSS for /lib/pong.js alerts. */

  body > #__next > .container {
    padding-top: 20px;
    padding-bottom: 20px;
  }

  body.is-map > #__next > .navbar {
    display: none;
  }

  body.is-map > #__next > .container {
    width: 100%;
    max-width: 100%;
    padding: 0 !important;
  }

  ...
`;

class App extends React.Component {
  state = {
    loading: true,
  };

  async componentDidMount() { ... }

  render() { ... }
}

App.propTypes = {
  Component: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired,
  pageProps: PropTypes.object.isRequired,
};

export default App;

在傳遞給 createGlobalStyle 的字符串中 (由反引號 `` 表示 ),我們正在添加兩個 CSS 規則,預期將一個類應用於我們的 <body></body> 標籤 is-map

body.is-map > #__next > .navbar {
  display: none;
}

body.is-map > #__next > .container {
  width: 100%;
  max-width: 100%;
  padding: 0 !important;
}

這裡的第一條規則是默認選擇樣板中包含的導航欄元素,如果 <body></body> 將其隱藏在屏幕上 標籤有 .is-map 班級。第二條規則——同樣針對 .is-map 類—定位 <div className="container"></div>render() 中進一步包裹頁面主要內容的元素 文件中的功能。此處的樣式強制該容器填充頁面的整個寬度並刪除其默認填充(確保地圖的左側和右側沒有間隙)。

創建我們的地圖

現在我們準備好設置我們的地圖了。因為我們在之前克隆的樣板文件中使用 Next.js,所以我們將依賴使用 /pages 的框架的路由器 目錄為我們的應用程序創建路由。對於我們的演示,我們將在 http://localhost:5000/map 處渲染我們的地圖 ,所以我們要創建一個名為 map 的新文件夾 在 /pages 下 :

/pages/map/index.js

import React from "react";
import StyledMap from "./index.css";

class Map extends React.Component {
  state = {};

  componentDidMount() {
    document.body.classList.add("is-map");
  }

  componentWillUnmount() {
    document.body.classList.remove("is-map");
  }

  render() {
    return (
      <StyledMap>
        <div id="google-map" />
      </StyledMap>
    );
  }
}

Map.propTypes = {
  // prop: PropTypes.string.isRequired,
};

export default Map;

在這裡,我們正在創建一個基於類的 React 組件——與使用函數組件模式相比,在 React 中實現 Google Maps 的方法要簡單得多。在 render() 下 方法,我們渲染一個組件 <StyledMap></StyledMap> 環繞一個空的 <div></div> 使用 id google-map (我們將在其中渲染地圖)。

componentDidMount() 函數,請注意我們正在設置 is-map <body></body> 上的類 就像我們之前在 componentWillUnmount() 中暗示的那樣 函數(當我們離開 /map 時調用 頁面),我們確保刪除 is-map 類,因為這是我們希望使用基於該類名稱應用樣式的唯一頁面。

真快,讓我們打開那個 StyledMap 我們從 ./index.css 導入的組件 靠近我們文件的頂部:

/pages/map/index.css.js

import styled from "styled-components";

export default styled.div`
  #google-map {
    width: 100%;
    height: 100vh;
  }
`;

很簡單。在這裡,我們使用 styled-components 包含在 Next.js 樣板中的庫,我們用於創建一個 React 組件,該組件將自動應用一些 CSS。在這裡,我們調用 styled.div 庫中包含的函數並將其傳遞給一個字符串(由 `` 表示 反引號)我們想要應用於返回 <div></div> 的 React 組件的 CSS 標記。

如果語法看起來很奇怪,styled.div`` 只是 styled.div(``) 的簡寫 (如果我們傳遞給函數的唯一參數是字符串,JavaScript 允許我們省略括號)。

對於我們的樣式,我們只是告訴 <div></div> 我們將在其中註入我們的 Google 地圖以填充屏幕的整個寬度和高度。

/pages/map/index.js

import React from "react";
import StyledMap from "./index.css";

class Map extends React.Component {
  state = {
    defaultCenter: {
      lat: 36.1774465,
      lng: -86.7042552,
    },
  };

  componentDidMount() {
    document.body.classList.add("is-map");
    this.handleAttachGoogleMap();
  }

  componentWillUnmount() { ... }

  handleAttachGoogleMap = () => {
    const { defaultCenter } = this.state;
    this.map = new google.maps.Map(document.getElementById("google-map"), {
      center: defaultCenter,
      zoom: 10,
    });
  };

  render() {
    return (
      <StyledMap>
        <div id="google-map" />
      </StyledMap>
    );
  }
}

Map.propTypes = {
  // prop: PropTypes.string.isRequired,
};

export default Map;

接下來,在我們的 componentDidMount() ,我們添加了對新函數 handleAttachGoogleMap() 的調用 我們在其中添加了重要部分:對 new google.maps.Map() 的調用 傳遞對 document.getElementById('google-map') 的調用 作為第一個參數,然後是一個 JavaScript 對象,其中包含我們地圖的一些設置。

這就是說“選擇 <div id="google-map" /> render() 中的元素 函數並在該位置渲染谷歌地圖。”對於選項,我們設置 center 屬性(加載時地圖的中心位置)到我們在 state 中設置的一些坐標 defaultCenter 下的值 .請注意,Google 希望我們通過帶有 lat 的 JavaScript 對象將坐標作為緯度和經度對傳遞 和 lng 作為包含這些值的屬性。

對於 zoom 我們將其設置為級別 10 (縮放值越高,我們越接近街道水平,縮放值越低,我們被縮小得越遠)。最後我們賦值調用new google.maps.Map()的結果 到 this.map .這有助於我們完成的是使我們的整個組件都可以訪問我們的 Google Maps 實例。接下來,當我們考慮向地圖添加標記時,這將派上用場。

向我們的地圖添加標記

現在我們可以訪問 Google Maps 實例,我們可以在地圖上添加一些標記。為了加快速度,我們將添加一個 markers 數組 到默認的 state 值在我們的組件頂部附近,在我們的 defaultCenter 附近有一些地方 (您可以更改這些以適應您自己的地圖的需要):

/pages/map/index.js

import React from "react";
import StyledMap from "./index.css";

class Map extends React.Component {
  state = {
    defaultCenter: {
      lat: 36.1774465,
      lng: -86.7042552,
    },
    markers: [
      {
        lat: 36.157055,
        lng: -86.7696144,
      },
      {
        lat: 36.1521981,
        lng: -86.7801724,
      },
      {
        lat: 36.1577547,
        lng: -86.7785841,
      },
      {
        lat: 36.1400674,
        lng: -86.8382887,
      },
      {
        lat: 36.1059131,
        lng: -86.7906082,
      },
    ],
  };

  componentDidMount() { ... }

  componentWillUnmount() { ... }

  handleAttachGoogleMap = () => {
    const { defaultCenter } = this.state;
    this.map = new google.maps.Map(...);

    setTimeout(() => {
      this.handleDrawMarkers();
    }, 2000);
  };

  handleDrawMarkers = () => {
    const { markers } = this.state;
    markers.forEach((marker) => {
      new google.maps.Marker({
        position: marker,
        map: this.map,
      });
    });
  };

  render() { ... }
}

Map.propTypes = {
  // prop: PropTypes.string.isRequired,
};

export default Map;

handleAttachGoogleMap 內部 ,在我們創建了地圖實例之後,現在,我們正在添加對 this.handleDrawMarkers() 的調用 ,我們正在添加的函數,我們將在其中渲染地圖的標記。值得注意的是,為了讓我們的演示更加精緻,我們包裝了一個 setTimeout() 兩秒鐘說“加載地圖,然後在兩秒鐘後繪製標記。”這使得加載體驗對用戶來說在視覺上更有趣(儘管它不是必需的,所以請隨意刪除它)。

handleDrawMarkers() 內部 ,我們使用 JavaScript 解構來“提取” markers 我們添加到 state 的值 (同樣,只是一組緯度/經度對象)。使用 JavScript .forEach() markers 上的方法 數組,我們遍歷列表並為每個列表調用 new google.maps.Markers() .對於該函數,我們傳遞一個描述 position 的選項對象 對於我們的標記(緯度/經度對)和 map 我們想將標記添加到(我們存儲在 this.map 的現有 Google Map 實例 )。

雖然看起來可能不多,但當我們加載頁面時,我們應該會看到我們的地圖呈現出來,並且在兩秒延遲後,我們的標記會出現。

不過,我們還沒有完成。最後,我們將使用 Google 地圖 bounds 進行完善 清理用戶體驗的功能。

使用標記作為地圖邊界來設置中心和縮放

我們現在需要做的所有工作都將在 handleDrawMarkers() 中 功能:

/pages/maps/index.js

handleDrawMarkers = () => {
  const { markers } = this.state;
  const bounds = new google.maps.LatLngBounds();

  markers.forEach((marker) => {
    new google.maps.Marker({
      position: marker,
      map: this.map,
    });

    bounds.extend(marker);
  });

  this.map.fitBounds(bounds);
  this.map.panToBounds(bounds);
};

只關注那個函數,現在,我們要使用 .LatLngBounds() google.maps 中的方法 圖書館幫助我們在地圖上的標記周圍設置邊界。為此,我們在 .forEach() 上方添加了一行 ,創建 google.maps.LatLngBounds() 的實例 ,將其存儲在變量 const bounds 中 .

接下來,在我們的 markers.forEach() 內部 ,創建標記後,我們添加對 bounds.extend() 的調用 , 傳入我們的 marker (我們的緯度/經度對)。這個函數“推出”了我們在 bounds 中初始化的邊界 包括我們當前循環的標記(想想這就像在你的櫃檯上將披薩麵團推成一個圓圈,披薩的中心就是我們的標記所在的位置)。

在我們的 .forEach() 下 循環,我們接下來調用 this.map 上的兩個函數 實例:.fitBounds() 它採用 bounds 我們已經建立並將地圖“收縮”到該邊界(放大)和 .panToBounds() ,將地圖的中心移動到我們剛剛繪製的邊界的中心。

有了這個,現在,當我們的地圖加載時,我們會看到一個漂亮的動畫,因為我們的標記被添加到地圖中。

總結

在本教程中,我們學習瞭如何將 Google 地圖添加到 Next.js 應用程序並在 React.js 組件中呈現地圖,包括標記和基於這些標記邊界的動畫縮放效果。


Tutorial JavaScript 教程
  1. JavaScript 條件簡單指南(If、Else If、Else 和 Switch 語句)

  2. 從參數中的對像中刪除屬性與從對像中刪除屬性

  3. 你的替代解決方案是什麼?挑戰#58

  4. React.js 和 Mysql 中的 Crud 操作

  5. 創建(可愛的)React 標籤!

  6. 使用 Express 將文件上傳到 MongoDB GridFS

  7. Azure 上的 GraphQL:第 7 部分 - 服務器端身份驗證

  1. 如何在您的 Web 應用程序中使用 jQuery 拆分器 UI 組件 - 第 2 部分

  2. 應用程序而不是庫

  3. 以有效的方式複​​制 Javascript 對象

  4. 如何從 Highcharts 中刪除按鈕

  5. 如何判斷字符串是否同時包含單引號 (') 和雙引號 ()?

  6. 獲取最小正數 JavaScript |數組示例代碼

  7. 反向循環關聯數組

  1. 節點 v17.0.1 錯誤

  2. 開始使用 Angular

  3. 如何快速學習 JavaScript:六個簡單的思維技巧

  4. 從企鵝到普吉特海灣:使用 Observable Plot 進行快速數據探索