Jak vykreslit mapu pomocí značek pomocí Map Google v Next.js

Jak vykreslit mapu Google se značkami uvnitř komponenty React pomocí Next.js a animovat tuto mapu na základě hranice značky.

Začínáme

Pro tento tutoriál použijeme CheatCode Next.js Boilerplate jako výchozí bod pro naši práci. Nejprve naklonujme kopii:

Terminál

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

Dále musíme nainstalovat závislosti pro základní verzi:

Terminál

cd nextjs-boilerplate && npm install

Nakonec spusťte základní desku:

Terminál

npm run dev

Díky tomu jsme připraveni začít.

Přidání Map Google přes CDN

Než implementujeme naši mapu, budeme potřebovat přístup k Google Maps JavaScript API. K získání přístupu použijeme oficiální odkaz Google CDN pro API:

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

Výše v /pages/_document.js soubor, který je součástí standardu, v <Head></Head> tag, jsme vložili do <script></script> značku, kterou společnost Google doporučuje pro zahrnutí rozhraní JavaScript API Map Google na webovou stránku.

Protože je tento soubor poměrně velký, zhustili jsme některé další značky v <Head></Head> tag s ... . Místo, kam chcete umístit svůj vlastní <script></script> značka je těsně před koncovou </Head> tag.

Zde je třeba poznamenat, že jsme změnili src atribut na značce, kterou získáme od společnosti Google, abychom mohli použít interpolaci řetězců, abychom mohli předat klíč API Map Google prostřednictvím našeho souboru nastavení. Ve standardu, který používáme, /settings/index.js soubor je zodpovědný za automatické načtení obsahu příslušného /settings/settings-<env>.js kde <env> část je rovna aktuální hodnotě process.env.NODE_ENV , nebo aktuální prostředí, ve kterém aplikace běží (pro tento výukový program development nebo settings-development.js ).

Pokud ještě nemáte klíč API Map Google, přečtěte si, jak jej vytvořit, než budete pokračovat.

Zpět v našem /pages/_document.js soubor, můžeme importovat settings od /settings/index.js a odkazujte na hodnoty v našem settings-<env>.js soubor. Zde očekáváme, že tento soubor bude obsahovat objekt s googleMaps vlastnost a vnořený apiKey hodnotu, například takto:

/settings/settings-development.js

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

export default settings;

Se všemi těmito sadami nyní, když načteme naši aplikaci, budeme mít globální google dostupná hodnota, která bude mít .maps objekt na něm, který budeme používat k interakci s knihovnou.

Nastavení globálních stylů map

Poté, co je načteno rozhraní Google Maps API, chceme dále vytvořit naši aplikaci. Ještě před tím, než to uděláme, chceme pro naši ukázku přidat do naší aplikace nějaký globální styl CSS, který zobrazí naši mapu na celé obrazovce v aplikaci:

/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;

V řetězci předaném na createGlobalStyle (označeno zadním zaškrtnutím `` ), přidáváme dvě pravidla CSS, která předpokládají, že třída bude aplikována na naše <body></body> tag is-map :

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

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

Prvním pravidlem je výběr prvku navbar obsaženého ve výchozím nastavení a jeho skrytí z obrazovky, pokud <body></body> tag má .is-map třída. Druhé pravidlo – také cílení na .is-map class — vyhledá <div className="container"></div> prvek, který zalamuje hlavní obsah stránky níže do render() funkce v souboru. Zde uvedené styly přinutí kontejner vyplnit celou šířku stránky a odstraní jeho výchozí výplň (zajistí, aby na levé a pravé straně mapy nebyla žádná mezera).

Vytváření naší mapy

Nyní jsme připraveni nastavit naši mapu. Protože používáme Next.js ve standardu, který jsme klonovali dříve, budeme se spoléhat na router tohoto rámce, který používá /pages adresář pro vytváření tras pro naši aplikaci. Pro naši ukázku vykreslíme mapu na http://localhost:5000/map , takže chceme vytvořit novou složku s názvem map pod /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;

Zde vytváříme komponentu React založenou na třídách – mnohem jednodušší způsob, jak implementovat Mapy Google v Reactu vs. pomocí vzoru funkčních komponent. Dole v render() způsob, vykreslíme komponentu <StyledMap></StyledMap> kolem prázdného <div></div> s id z google-map (kde vykreslíme naši mapu).

V componentDidMount() Všimněte si, že nastavujeme is-map třídy na <body></body> tag, jak jsme naznačili dříve a v componentWillUnmount() funkce (volána, když se vzdálíme od /map stránku), musíme odstranit is-map class, protože toto je jediná stránka, kde chceme, aby byly použity styly, které aplikujeme na základě názvu třídy.

Rychle, pojďme otevřít StyledMap komponentu, kterou importujeme z ./index.css v horní části našeho souboru:

/pages/map/index.css.js

import styled from "styled-components";

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

Velmi jednoduché. Zde používáme styled-components knihovna, která je součástí Next.js Boilerplate, kterou používáme k vytvoření komponenty React, na kterou se automaticky použije nějaké CSS. Zde voláme na styled.div funkci obsaženou v knihovně a předejte jí řetězec (označený `` backticks here) CSS, které chceme použít na komponentu React, která vrací <div></div> tag.

V případě, že tato syntaxe vypadá divně, styled.div`` je jen zkratka pro styled.div(``) (JavaScript nám umožňuje vynechat závorky, pokud je jediným argumentem, který funkci předáváme, řetězec).

U našich stylů říkáme pouze <div></div> kam vložíme naši mapu Google tak, aby vyplnila celou šířku a výšku obrazovky.

/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;

Dále v našem componentDidMount() , přidali jsme volání nové funkce handleAttachGoogleMap() kam jsme přidali důležitou část:volání na new google.maps.Map() předání volání na document.getElementById('google-map') jako první argument a poté objekt JavaScriptu s některými nastaveními pro naši mapu.

To říká "vyberte <div id="google-map" /> prvek dole v našem render() a vykreslit mapu Google na tomto místě." Pro možnosti jsme nastavili center vlastnost (kde bude střed mapy při načítání umístěn) na některé souřadnice, které jsme nastavili v state hodnotu pod defaultCenter . Všimněte si, že Google očekává, že souřadnice předáme jako páry zeměpisné šířky a délky prostřednictvím objektů JavaScript s kódem lat a lng jako vlastnosti obsahující tyto hodnoty.

Pro zoom nastavíme to na úroveň 10 (čím vyšší je hodnota přiblížení, tím více se přibližujeme k úrovni ulice, čím nižší je hodnota přiblížení, tím více jsme oddálení). Nakonec přiřadíme výsledek volání new google.maps.Map() na this.map . To nám pomáhá dosáhnout toho, že naši instanci Map Google zpřístupníme celé naší komponentě. To se nám bude hodit příště, když se podíváme na přidávání značek do naší mapy.

Přidávání značek do naší mapy

Nyní, když máme přístup k instanci Map Google, můžeme do mapy přidat nějaké značky. Abychom to urychlili, přidáme pole markers na výchozí state hodnota v horní části naší komponenty s některými místy poblíž našeho defaultCenter (můžete je změnit tak, aby vyhovovaly potřebám vaší vlastní mapy):

/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;

Uvnitř handleAttachGoogleMap , poté, co jsme vytvořili naši instanci mapy, nyní přidáváme volání this.handleDrawMarkers() , funkci, kterou přidáváme, kde budeme vykreslovat značky pro naši mapu. Aby bylo naše demo ještě dokonalejší, zabalili jsme setTimeout() na dvě sekundy říct „načtěte mapu a po dvou sekundách nakreslete značky“. Díky tomu je načítání pro uživatele vizuálně zajímavější (ačkoli to není vyžadováno, takže to klidně odeberte).

Uvnitř handleDrawMarkers() , používáme destrukci JavaScriptu k „odtržení“ markers hodnotu, kterou jsme přidali do state (opět jen pole objektů zeměpisné šířky/délky). Pomocí JavaScriptu .forEach() metodou na našem markers pole, procházíme seznamem a pro každý z nich zavoláme new google.maps.Markers() . Této funkci předáme objekt options popisující position pro naši značku (pár zeměpisná šířka/délka) a map chceme přidat značku (naší existující instanci Google Map, kterou jsme uložili na this.map ).

I když to možná nevypadá, když načteme naši stránku, měli bychom vidět naši mapu vykreslenou a po dvousekundové prodlevě se objeví naše značky.

Ještě jsme ale úplně neskončili. Abychom to uzavřeli, vyladíme věci pomocí Google Maps bounds funkce pro vyčištění uživatelského prostředí.

Použití značek jako hranic mapy k nastavení středu a přiblížení

Veškerá práce, kterou nyní musíme udělat, bude v handleDrawMarkers() funkce:

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

Nyní se soustředíme pouze na tuto funkci a chceme použít .LatLngBounds() metoda v google.maps knihovna, která nám pomůže nastavit hranici kolem našich značek na mapě. Za tímto účelem jsme přidali řádek nad naše .forEach() , čímž se vytvoří instance google.maps.LatLngBounds() , jeho uložením do proměnné const bounds .

Dále uvnitř našeho markers.forEach() , poté, co vytvoříme značku, přidáme volání bounds.extend() , předáním našeho marker (naše dvojice zeměpisná šířka/délka). Tato funkce "vytlačí" hranici, kterou jsme inicializovali v bounds zahrnout značku, přes kterou právě procházíme (představte si to jako vytlačování těsta na pizzu do kruhu na pultu, kde je střed pizzy tam, kde jsou umístěny naše značky).

Pod naším .forEach() smyčky, dále zavoláme dvě funkce na našem this.map instance:.fitBounds() který přebírá bounds vytvořili jsme a „zabalili“ mapu k této hranici (přiblížení) a .panToBounds() , přesune střed mapy tak, aby byl středem hranice, kterou jsme právě nakreslili.

Díky tomu nyní, když se naše mapa načte, uvidíme pěknou animaci, jak se naše značky přidávají do mapy.

Zabalení

V tomto tutoriálu jsme se naučili, jak přidat Mapy Google do aplikace Next.js a vykreslit mapu v komponentě React.js, doplněnou značkami a animovaným efektem přiblížení založeným na hranicích těchto značek.