資料視覺敘事

路線地圖卡

導航 App 風格的路線地圖卡:格線底圖與確定性街道、路線逐段描繪、導航點沿線前進、街名依序淡入,附 ETA 讀數。

這是 WebberUI Pro 元件

下方的安裝指令需要授權才能執行,未授權時會回 401。

怎麼安裝 Pro 元件 →查看方案 →

外送追蹤、叫車、物流進度這類「東西正在路上」的畫面,用一張卡就能講清楚。路線頂點用 0–1 相對座標描述,不需要任何真實地圖資料或圖磚:底圖是兩級 CSS 格線加上由 seed 產生的確定性街道、公園與水域;路線先以描繪動畫畫出,導航點再沿線前進,經過的街名依序淡入,角落的玻璃讀數卡以逐位滾動顯示剩餘分鐘與距離。進度可以交給元件自動播放,也可以用 progress 完全受控,接上你自己的即時位置資料。

把這個效果裝進你的專案

在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。

npx webberui@latest add "https://webberui.com/r/route-map-card.json?t=<安裝 token>"

還沒有專案,或安裝遇到問題?看快速開始 →

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

0.45
2.4
0.6
7
編輯中的呼叫預設屬性
<RouteMapCard/>

完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。

安裝

npx webberui@latest add "https://webberui.com/r/route-map-card.json?t=<安裝 token>"

或在 components.json 設定 registries 後,改用 @webberui/route-map-card 安裝。

使用

import {
  RouteMapCard,
  type RouteMapPoint,
} from "@/components/ui/route-map-card";

const route: RouteMapPoint[] = [
  { x: 0.14, y: 0.8, label: "老張牛肉麵" },
  { x: 0.16, y: 0.46, label: "民生東路" },
  { x: 0.46, y: 0.46, label: "復興北路" },
  { x: 0.5, y: 0.22, label: "南京東路" },
  { x: 0.78, y: 0.24, label: "巷口" },
  { x: 0.84, y: 0.56, label: "你的位置" },
];

// 自動播放:捲入視窗後先描繪路線,再以 duration × travelFactor 走完
<RouteMapCard points={route} totalMinutes={12} totalKm={3.6} routeName="外送路線" />;

// 受控:把你的即時進度(0–1)接進來
<RouteMapCard points={route} progress={0.42} totalMinutes={12} />;

Props

Prop型別預設值說明
pointsRouteMapPoint[]—路線頂點,依序連線,至少兩點;座標為 0–1 相對值
roadsRouteMapRoad[]—額外的裝飾道路,畫在確定性街道之上、路線之下
seednumber7底圖街道、公園與水域的亂數種子;同一個 seed 永遠產生同一張圖
progressnumber—受控進度(0–1),直接決定導航點位置與已走路段;提供後不再自動播放
defaultProgressnumber0非受控模式的起始進度
onProgressChange(progress: number) => void—非受控自動播放時每一幀回報進度
onArrive() => void—非受控自動播放走到終點時回呼
trigger"mount" | "inView" | "manual""inView"非受控時何時開始:掛載後、捲入視窗(約 30% 可見)或手動
playingboolean—trigger="manual" 時控制播放/暫停;暫停後再播放從目前位置繼續,走完則從頭重播
durationnumber2.4路線描繪時長(秒),同時寫入 --wb-rmc-duration
travelFactornumber3導航點前進時長倍數:前進時長 = duration × travelFactor,線性
glownumber0.6路線下層發光強度(0–1)
totalMinutesnumber18全程預估分鐘數;剩餘分鐘 = round(totalMinutes × (1 − progress))
totalKmnumber—全程公里數;提供時 ETA 讀數一併顯示剩餘距離
smoothbooleanfalsetrue 時以 Catmull-Rom 平滑曲線連接頂點,否則為圓角折線
cornerRadiusnumber12圓角折線每個轉角的圓角半徑(viewBox 單位,約等於 px)
markerShape"dot" | "arrow""arrow"導航點外形;箭頭會朝向行進方向
aspectstring"4/3"地圖區長寬比,例如 "16/9"、"1"
routeColorstring—路線顏色;未提供時使用 --wb-rmc-route(預設 #3b82f6)
routeNamestring"路線"螢幕閱讀器描述開頭的路線名稱
etaCorner"top-left" | "top-right" | "bottom-left" | "bottom-right""bottom-right"ETA 讀數卡位置,避開你的路線
headerReactNode—卡片頂部內容,例如外送員資訊
classNamestring—透傳到最外層容器

RouteMapPoint

欄位型別預設值說明
xnumber—水平位置(0 = 左緣,1 = 右緣)
ynumber—垂直位置(0 = 上緣,1 = 下緣)
labelstring—街名或地點名稱;導航點抵達時淡入,起點與終點固定顯示

RouteMapRoad

欄位型別預設值說明
dstring—原始 SVG path 資料,座標系為 viewBox(寬固定 400,高 = 400 ÷ aspect);與 points 擇一
points[number, number][]—以 0–1 相對座標描述的折線頂點
widthnumber10線寬(viewBox 單位)

另外具名匯出 RouteMapCardProps、RouteMapPoint、RouteMapRoad、RouteMapTrigger、RouteMapMarkerShape 與 RouteMapEtaCorner 型別。

細節

  • 座標系:SVG viewBox 寬固定 400、高依 aspect 換算,地圖容器用同一個長寬比,所以 0–1 座標、SVG 與 HTML 標籤三者完全對齊
  • 路徑:預設為圓角折線,每個轉角以二次貝茲做圓角,半徑不超過相鄰兩段的一半;smooth 改用 Catmull-Rom 曲線(換算成三次貝茲)通過每個頂點
  • 導航點定位:元件在 JS 端把路徑取樣成折線並累計弧長,依進度內插出位置與行進角度——效果等同 getPointAtLength,但純計算、SSR 首渲就正確,也沒有 CSS offset-path 與縮放後 viewBox 座標系不一致的問題
  • 描繪:路線以 pathLength="1" 搭配 stroke-dashoffset 的 CSS transition 描繪,時長讀 --wb-rmc-duration、曲線讀 --wb-rmc-ease(預設 ease-in-out);下層是同路線較粗、高斯模糊的發光底,透明度等於 glow
  • 已走路段:上層同一條路徑以 stroke-dasharray 只露出前 progress 段,經過的轉角圓點同步填色
  • 街名標籤:每個頂點在全程的累計長度比例決定到達時機,到達時以 200ms 淡入並上移 4px
  • 確定性底圖:以 seed 驅動 mulberry32 小型亂數產生 10–14 條街道、兩塊公園與一片水域,不呼叫 Math.random,伺服器與瀏覽器輸出相同
  • ETA 讀數:剩餘分鐘逐位滾動(每位數一欄 0–9),剩餘距離為 totalKm × (1 − progress) 取一位小數;走完顯示「已抵達」

可及性

  • 地圖 SVG 為 role="img",aria-label 依 props 動態生成,例如「外送路線,從 老張牛肉麵 到 你的位置,預計 12 分鐘抵達」
  • ETA 區附 aria-live="polite" 的 sr-only 文字,只在每減少 1 分鐘時改變,不會每幀打斷螢幕閱讀器;滾動數字本身為 aria-hidden
  • 底圖、路線與街名標籤都是裝飾性內容(aria-hidden),完整資訊已由 aria-label 提供
  • 使用者系統開啟「減少動態效果」時,路線直接完整顯示、不播描繪動畫;導航點直接放在 progress 位置(非受控時放在終點),停止外圈脈動,所有街名標籤一次顯示,數字也不再滾動