資料視覺敘事
路線地圖卡
導航 App 風格的路線地圖卡:格線底圖與確定性街道、路線逐段描繪、導航點沿線前進、街名依序淡入,附 ETA 讀數。
外送追蹤、叫車、物流進度這類「東西正在路上」的畫面,用一張卡就能講清楚。路線頂點用 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 | 型別 | 預設值 | 說明 |
|---|---|---|---|
points | RouteMapPoint[] | — | 路線頂點,依序連線,至少兩點;座標為 0–1 相對值 |
roads | RouteMapRoad[] | — | 額外的裝飾道路,畫在確定性街道之上、路線之下 |
seed | number | 7 | 底圖街道、公園與水域的亂數種子;同一個 seed 永遠產生同一張圖 |
progress | number | — | 受控進度(0–1),直接決定導航點位置與已走路段;提供後不再自動播放 |
defaultProgress | number | 0 | 非受控模式的起始進度 |
onProgressChange | (progress: number) => void | — | 非受控自動播放時每一幀回報進度 |
onArrive | () => void | — | 非受控自動播放走到終點時回呼 |
trigger | "mount" | "inView" | "manual" | "inView" | 非受控時何時開始:掛載後、捲入視窗(約 30% 可見)或手動 |
playing | boolean | — | trigger="manual" 時控制播放/暫停;暫停後再播放從目前位置繼續,走完則從頭重播 |
duration | number | 2.4 | 路線描繪時長(秒),同時寫入 --wb-rmc-duration |
travelFactor | number | 3 | 導航點前進時長倍數:前進時長 = duration × travelFactor,線性 |
glow | number | 0.6 | 路線下層發光強度(0–1) |
totalMinutes | number | 18 | 全程預估分鐘數;剩餘分鐘 = round(totalMinutes × (1 − progress)) |
totalKm | number | — | 全程公里數;提供時 ETA 讀數一併顯示剩餘距離 |
smooth | boolean | false | true 時以 Catmull-Rom 平滑曲線連接頂點,否則為圓角折線 |
cornerRadius | number | 12 | 圓角折線每個轉角的圓角半徑(viewBox 單位,約等於 px) |
markerShape | "dot" | "arrow" | "arrow" | 導航點外形;箭頭會朝向行進方向 |
aspect | string | "4/3" | 地圖區長寬比,例如 "16/9"、"1" |
routeColor | string | — | 路線顏色;未提供時使用 --wb-rmc-route(預設 #3b82f6) |
routeName | string | "路線" | 螢幕閱讀器描述開頭的路線名稱 |
etaCorner | "top-left" | "top-right" | "bottom-left" | "bottom-right" | "bottom-right" | ETA 讀數卡位置,避開你的路線 |
header | ReactNode | — | 卡片頂部內容,例如外送員資訊 |
className | string | — | 透傳到最外層容器 |
RouteMapPoint
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
x | number | — | 水平位置(0 = 左緣,1 = 右緣) |
y | number | — | 垂直位置(0 = 上緣,1 = 下緣) |
label | string | — | 街名或地點名稱;導航點抵達時淡入,起點與終點固定顯示 |
RouteMapRoad
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
d | string | — | 原始 SVG path 資料,座標系為 viewBox(寬固定 400,高 = 400 ÷ aspect);與 points 擇一 |
points | [number, number][] | — | 以 0–1 相對座標描述的折線頂點 |
width | number | 10 | 線寬(viewBox 單位) |
另外具名匯出 RouteMapCardProps、RouteMapPoint、RouteMapRoad、RouteMapTrigger、RouteMapMarkerShape 與 RouteMapEtaCorner 型別。
細節
- 座標系:SVG viewBox 寬固定 400、高依
aspect換算,地圖容器用同一個長寬比,所以 0–1 座標、SVG 與 HTML 標籤三者完全對齊 - 路徑:預設為圓角折線,每個轉角以二次貝茲做圓角,半徑不超過相鄰兩段的一半;
smooth改用 Catmull-Rom 曲線(換算成三次貝茲)通過每個頂點 - 導航點定位:元件在 JS 端把路徑取樣成折線並累計弧長,依進度內插出位置與行進角度——效果等同
getPointAtLength,但純計算、SSR 首渲就正確,也沒有 CSSoffset-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位置(非受控時放在終點),停止外圈脈動,所有街名標籤一次顯示,數字也不再滾動