D3.js 範例學習-阿爾罕布拉宮

Jacy Chu
5 min readAug 2, 2021

--

從範例學習 mini map 之 zoom, brush 功能

📋 目錄

  1. 範例介紹
  2. [實作] 繪製 main map & mini map
  3. [實作] 新增 brush & zoom 功能
  4. 小結

範例介紹

在撰寫 動手實作 D3.js 練習-運動場 這篇,發現一個很好的 brush & zoom 範例!( 由 Seemant Kulleen 撰寫 ✒️ )

此篇文章將基於原著的程式碼,細節部分加以說明,並進行部分客製化。
目標:製作阿爾罕布拉宮 🏰 main map & mini map,並提供 brush & zoom 功能。

若尚未具備基礎 D3.js 概念,建議先閱讀這幾篇:
🚪 D3.js 資料視覺化工具(上)
🚪 D3.js 資料視覺化工具(中)
🚪 D3.js 資料視覺化工具(下)

準備好就開始吧 🚀

[實作] 繪製 main map & mini map

先準備好 index.html,程式碼如下:

頁面上有兩個獨立的 SVG 元素: main mini
其中主要的 SVG 會嵌套在 aperture 的 SVG 中,是為了提供放大鏡功能。
檔案佈局為:
(小圖) div.vis -> div#mini -> svg
(大圖) div.vis -> div#main -> svg.aperture -> svg#largemap

上方的 viewBox(min-x, min-y, width, height),搭配 preserveAspectRatio = "xMinYMin meet",SVG 內部的元素就會自動填滿容器的寬。
有關 viewBox 以及 preserveAspectRatio 可以參考這篇 📃
也可以參考這支影片說明 🎥

在開始實作 main mini 功能之前,先加入要放大檢視的圖片。
Unsplash 網站下載 圖片素材,將圖片放入資料夾中,並將其重新命名為view.jpg
本實作使用下方阿爾罕布拉宮這張

index.js 檔案加入下方程式碼:

接著,index.html 檔案中的 style 區塊加入下方程式碼:

此非完整的 index.html 檔案

使用 Flexbox 處理排版,並將 main map 設定為 mini map 的四倍大 🔍
若不清楚 Flexbox 使用方式,可以參考這支教學影片 (Learn Flexbox in 15 Minutes)

目前畫面如上所示, 👈 左方為 mini map ,右方為 main map 👉

[實作] 新增 brush & zoom 功能

有了 mini map 與 main map,要來新增 brush & zoom 功能。

step 1 取參數,並將對應方法加到 SVG 上
index.js 加上程式碼如下方:

此非完整的 index.js 檔案

首先,將最初設定的 viewBox 存起來。

brush.extent() 函數用於將可刷範圍設置為指定的點數組[[ x0 , y0 ], [ x1 , y1 ]],其中 [ x0 , y0 ] 是左上角[ x1 , y1 ] 是右下角並返回畫筆。

.scaleExtent([]):縮放最大值與最小值,預設為(1, 無限大)

最後把功能加到對應的 SVG 上 🎯

step 2 加上 brushed function
index.js 加上程式碼如下方:

此非完整的 index.js 檔案

一開始若發現呼叫的是 zoom 動作,即忽略 brushed function
d3.event.selection以抓移動 brush 的值
接著算出要 scale 的 縮放因子 k

在內部,元素的變換存為 element.__zoom
不使用 zoom 提供的回呼函式,而是直接手動取修改 __zoom

step 3 加上 zoomed function

此非完整的 index.js 檔案

若是在小圖上進行縮放,反映到大圖上做縮放動作
若發現觸發的是 brush 動作,即忽略 zoomed function

接著取 d3.event.transform 取回是一個 object ,其中包含 x y 以及 k(縮放因子)

縮放動作完成要更新原本的 viewBox 並反映到小圖上

完成了 🎉 🎉 🎉

小結

具備 D3.js 基礎後,藉由網路資源找尋不錯的範例,抽絲剝繭做學習,過程中更認識進階的 zoom & brush 功能。將原始的 SVG 圖檔置換成不同檔案型式(jpg),部分客製化為喜歡的作品。附上此練習的 GitHub 檔案連結 🔗 ,專案資料夾名稱:d3-minimap-Alhambra。有任何指點或建議都可以在下方留言~🙏

🏰 Thanks for reading!

--

--