第十章:營區任意門 - 實作地點搜尋與平滑飛越 (FlyTo)

當你的地圖上只有 10 個營區時,使用者可以很悠哉地滑動地圖尋找。 但當你的資料庫裡有高達 1,000 個全台灣的露營地時,使用者不可能像無頭蒼蠅一樣,慢慢在地圖上放大縮小去尋找「武陵農場」到底隱藏在哪座山裡。

他們需要一個功能強大的「隨意門」:一個在網頁頂端的搜尋框。當使用者打字輸入時,下方會自動跳出建議選單 (Autocomplete),一旦點擊,地圖就會像無人機一樣,瞬間平滑飛越到該地點的上空並放大!

這是一個極具商業價值的進階功能,能讓你的系統體驗瞬間達到 Google Maps 的水準。

🎯 本章目標

  1. 實作一個帶有 Autocomplete (自動完成) 下拉選單的智慧搜尋框。
  2. 結合 React 狀態管理與 Leaflet 的 map.flyTo 動畫功能。
  3. 體驗將多個小組件 (搜尋框 + 地圖引擎) 組合連動的系統架構快感。

🔍 第一步:建立智慧搜尋框 (SearchBar)

我們不需要自己從零開始刻下拉選單的過濾邏輯與 CSS 排版,這種繁瑣的 UI 元件是交給 AI 表現的最好機會。

🔥【Vibe Prompt 實戰咒語】 我需要在 React 中做一個營區搜尋框元件 (命名為 SearchBar.tsx)。 1. 介面設計:請用 Tailwind CSS 設計得像 Google Maps 的搜尋框一樣有質感。包含陰影 (shadow-lg)、圓角、以及左側有一個放大鏡 Icon (lucide-react)。 2. 資料處理:傳入一個陣列 campsData (包含 id, name, lat, lng)。 3. 自動完成邏輯:使用者在 input 打字時,下方要出現一個絕對定位 (absolute z-50) 的下拉選單。請用 filter 過濾出名稱包含使用者輸入字串的營區。 4. 觸發事件:當使用者點擊下拉選單中的某個營區時,請觸發一個 callback prop: onCampSelect(lat, lng),並且清空輸入框與關閉下拉選單。

將這段咒語送給 AI,它會幫你把 onChange 即時過濾資料、下拉選單的 ul / li 渲染,全部寫得漂漂亮亮,而且樣式看起來非常專業。


🛫 第二步:串接地圖的飛行函數 (FlyTo)

現在,我們要把這個做好的 SearchBar 元件放到地圖頁面中,並把它的 onCampSelect 回呼函數,跟地圖的飛行功能綁定在一起。

要在 React-Leaflet 中控制地圖的視角,我們需要先拿到地圖的「實例 (Instance)」。這通常透過 <MapContainer>ref 來取得。

打開你的主頁面 (例如 MapPage.tsx),修改程式碼如下:

import { useState, useRef } from 'react';
import { MapContainer, TileLayer } from 'react-leaflet';
import SearchBar from './SearchBar';

export default function MapPage() {
  // 1. 建立一個 Ref 來抓住 Leaflet 地圖的控制權
  const mapRef = useRef(null); 

  // 2. 定義當搜尋框選中營區時,要發生的動作
  const handleCampSelect = (lat, lng) => {
    const map = mapRef.current;
    if (map) {
      // 關鍵魔法:瞬間飛往目標營區,並放大到等級 15!
      // duration: 2 代表飛行過程花費 2 秒鐘,創造平滑視覺
      map.flyTo([lat, lng], 15, { duration: 2 });
    }
  };

  return (
    <div className="relative h-screen w-full">
      {/* 搜尋框區塊:絕對定位浮在地圖最上方 */}
      <div className="absolute top-6 left-1/2 -translate-x-1/2 z-[1000] w-11/12 max-w-md">
        <SearchBar campsData={mockData} onCampSelect={handleCampSelect} />
      </div>
      
      {/* 地圖本體區塊 */}
      <MapContainer 
        ref={mapRef} // 把 mapRef 綁定上來
        center={[23.5, 121.0]} 
        zoom={8} 
        className="h-full w-full z-0"
      >
        <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
        {/* 這裡放你的 Marker... */}
      </MapContainer>
    </div>
  );
}

存檔後,測試看看。當你在搜尋框輸入「武陵」,點擊下拉選單出現的「武陵農場」,你的畫面會以極度平滑的動畫,從台灣全圖的視角,飛越山川,直接空降並放大到台中和平區的目的地! 這種絲滑的互動體驗,就是讓客戶心甘情願掏出 10 萬塊結帳的殺手級功能。


✅ 課程大總結 (Course 2 結業)

狂賀!你完成了整個 Car Camping Map (全端地圖資訊平台) 實戰專案的所有章節!

這不是一個隨便寫寫、只能放在本地自嗨的練習題,而是一個具備了企業級功能的真實商業產品:

  1. 空間資料庫整合 (Supabase API):資料不再寫死,可以從後台無限擴充。
  2. 海量資料渲染優化 (Marker Clustering):一千個點位也不卡頓。
  3. 手機版防呆與適配 (Gesture Handling & RWD):解決滑動陷阱,隨時隨地完美展示。
  4. 地理定位與智慧搜尋 (FlyTo & Autocomplete):提供 Google Maps 等級的極致使用者體驗。

只要把資料庫裡的名字與經緯度換掉,你現在隨時可以把這套系統改裝成「全台特斯拉充電樁地圖」、「台北深夜咖啡廳地圖」或是「房仲尋屋雷達」上線營利!

你已經從一個網頁切版工程師,正式升級為「能開發大型地理資訊系統 (GIS) 的架構師」。 準備好迎接下一個挑戰了嗎?前往下一個專案,我們將教你如何打造自動賺錢的 Line 機器人與後台系統!

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!