iT邦幫忙

2025 iThome 鐵人賽

DAY 17
0

Day 17: 頁面優化與視覺體驗提升 - 從空白問題到完美布局的進化之路

🎯 今日目標

在 GASO 專案的第 17 天,我們專注於解決頁面布局問題,提升視覺體驗。主要目標包括:

  • 固定 setup 圖標位置,不隨滾動移動
  • 新增生動的載入動畫,提升用戶體驗
  • 解決頁面下方大量空白問題
  • 優化 Graphviz 地圖的顯示效果
  • 提升整體用戶體驗

🔍 問題診斷

1. Setup 圖標位置問題

原本的 setup 圖標會隨著頁面滾動而移動,影響用戶體驗。

2. 缺乏載入動畫

頁面載入時沒有視覺反饋,用戶不知道系統正在處理。

3. 頁面空白問題

在測試過程中發現頁面下方存在大量空白,經過分析發現問題根源:

// 問題:強制使用螢幕高度
const calculatedHeight = Math.max(minPageHeight, window.innerHeight);

這會導致即使內容不需要那麼高,頁面也會被強制拉伸到螢幕高度。

2. Graphviz 尺寸過大

檢查發現 #zoomInner 元素尺寸異常:

  • 原始尺寸:6447px × 3925px
  • 縮放後:2514px × 1531px(scale 0.39)

這個巨大的尺寸是造成頁面空白的主要原因。

🛠️ 解決方案

1. 固定 Setup 圖標位置

.sidebar-toggle {
  position: fixed; /* 改為 fixed 定位 */
  top: 20px;
  right: 20px;
  /* ... 其他樣式保持不變 ... */
}

將 setup 圖標的定位從 absolute 改為 fixed,確保它始終固定在螢幕右上角,不會隨頁面滾動而移動。

2. 新增載入動畫

CSS 動畫樣式

.loading-container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #f4f1e8;
  background-image:
    radial-gradient(circle at 20% 50%, rgba(139, 69, 19, 0.1) 0%, transparent 50%),
    radial-gradient(circle at 80% 20%, rgba(160, 82, 45, 0.1) 0%, transparent 50%),
    radial-gradient(circle at 40% 80%, rgba(101, 67, 33, 0.1) 0%, transparent 50%);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  z-index: 9999;
  transition: opacity 0.5s ease-out;
}

.loading-spinner {
  width: 80px;
  height: 80px;
  border: 6px solid #8b4513;
  border-top: 6px solid #a0522d;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 30px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.loading-text {
  font-family: 'Times New Roman', serif;
  font-size: 24px;
  font-weight: bold;
  color: #8b4513;
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
  margin-bottom: 20px;
  animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.7; }
}

HTML 結構

<body>
  <!-- 載入動畫 -->
  <div class="loading-container" id="loadingContainer">
    <div class="loading-spinner"></div>
    <div class="loading-text">正在載入學習地圖</div>
    <div class="loading-dots">
      <div class="loading-dot"></div>
      <div class="loading-dot"></div>
      <div class="loading-dot"></div>
    </div>
    <div class="loading-progress">
      <div class="loading-progress-bar"></div>
    </div>
    <div class="loading-subtitle">探索 Google Apps Script 的無限可能</div>
  </div>
  <!-- ... 其他內容 ... -->
</body>

JavaScript 控制

function hideLoadingAnimation() {
  const loadingContainer = document.getElementById('loadingContainer');
  if (loadingContainer) {
    loadingContainer.classList.add('hidden');
    setTimeout(() => {
      loadingContainer.style.display = 'none';
    }, 500);
  }
}

function showLoadingAnimation() {
  const loadingContainer = document.getElementById('loadingContainer');
  if (loadingContainer) {
    loadingContainer.style.display = 'flex';
    loadingContainer.classList.remove('hidden');
  }
}

// 在載入開始時顯示動畫
showLoadingAnimation();

google.script.run
  .withSuccessHandler(function (data) {
    // ... 處理數據 ...
    renderGraph().then(() => {
      hideLoadingAnimation();
      setTimeout(() => {
        removeExcessWhitespace();
      }, 100);
    });
  })
  .withFailureHandler(function (err) {
    // ... 錯誤處理 ...
    hideLoadingAnimation();
  })
  .getGraphData();

3. 智能頁面高度調整

// 修復前:強制使用螢幕高度
const calculatedHeight = Math.max(minPageHeight, window.innerHeight);

// 修復後:精確計算所需高度
const calculatedHeight = minPageHeight;
document.body.style.height = calculatedHeight + 'px';

2. 元素尺寸限制

// 限制最大尺寸,避免過大的元素
const maxReasonableWidth = window.innerWidth * 3;
const maxReasonableHeight = window.innerHeight * 3;

const finalWidth = Math.min(origW, maxReasonableWidth);
const finalHeight = Math.min(origH, maxReasonableHeight);

3. 智能大地圖處理

// 智能處理大地圖
const isLargeGraph = graphWidth > window.innerWidth * 2 || graphHeight > window.innerHeight * 2;

if (isLargeGraph) {
  // 大地圖:限制頁面高度,讓用戶可以滾動查看
  const maxPageHeight = window.innerHeight * 1.5;
  minPageHeight = Math.min(requiredHeight, maxPageHeight);
  
  // 啟用滾動
  document.body.style.overflowX = 'auto';
  document.body.style.overflowY = 'auto';
} else {
  // 小地圖:正常處理
  minPageHeight = headerHeight + graphHeight + graphPadding + bodyPadding + 20;
  document.body.style.overflowX = 'hidden';
  document.body.style.overflowY = 'auto';
}

🎨 視覺優化

1. 節點緊密度調整

code.js 中調整 Graphviz 參數:

let dot = `
  digraph G {
    graph [overlap=false, nodesep=0.3, ranksep=0.3];
    node [fontsize=16, style=filled, fillcolor="#8b4513", color=none, fontcolor=white];
  `;
  • nodesep=0.3:控制同一層級節點之間的水平間距
  • ranksep=0.3:控制不同層級節點之間的垂直間距

2. 邊線樣式優化

// 將預設邊線樣式改為直線
splines: localStorage.getItem("gv_splines") || "line",

3. 背景圖大小調整

#zoomInner::before { 
  content: ''; 
  position: absolute; 
  top: -100px; 
  left: -100px; 
  right: -100px; 
  bottom: -100px; 
  background-image: url("...");
  background-repeat: no-repeat; 
  background-position: center center; 
  background-size: cover; 
  opacity: 0.3; 
  z-index: -1; 
}

讓背景圖比 Graphviz 地圖大 200px(上下左右各 100px)。

📊 技術實現細節

1. 動態頁面大小調整函數

function adjustPageSizeToGraph(graphWidth, graphHeight) {
  const header = document.querySelector('.header');
  const headerHeight = header ? header.offsetHeight + 40 : 0;
  const graphPadding = 40;
  const bodyPadding = 40;
  
  // 智能處理大地圖
  const isLargeGraph = graphWidth > window.innerWidth * 2 || graphHeight > window.innerHeight * 2;
  let minPageHeight;
  
  if (isLargeGraph) {
    const maxPageHeight = window.innerHeight * 1.5;
    const requiredHeight = headerHeight + graphHeight + graphPadding + bodyPadding + 20;
    minPageHeight = Math.min(requiredHeight, maxPageHeight);
    
    document.body.style.overflowX = 'auto';
    document.body.style.overflowY = 'auto';
  } else {
    minPageHeight = headerHeight + graphHeight + graphPadding + bodyPadding + 20;
    document.body.style.overflowX = 'hidden';
    document.body.style.overflowY = 'auto';
  }
  
  document.body.style.minHeight = minPageHeight + 'px';
  document.body.style.height = 'auto';
}

2. 空白移除函數

function removeExcessWhitespace() {
  const graph = document.getElementById('graph');
  const header = document.querySelector('.header');
  
  if (!graph || !header) return;
  
  const headerHeight = header.offsetHeight + 40;
  const graphHeight = graph.offsetHeight;
  const bodyPadding = 40;
  const requiredHeight = headerHeight + graphHeight + bodyPadding + 20;
  
  document.body.style.minHeight = requiredHeight + 'px';
  document.body.style.height = requiredHeight + 'px';
  
  const currentScrollHeight = document.body.scrollHeight;
  if (currentScrollHeight > requiredHeight + 100) {
    document.body.style.height = (requiredHeight - 50) + 'px';
  }
}

🎯 優化效果

1. 用戶體驗優化

  • 固定圖標:setup 圖標固定在螢幕右上角,不隨滾動移動
  • 載入動畫:生動的載入動畫提供視覺反饋,提升用戶體驗
  • 無多餘空白:頁面高度精確匹配內容
  • 智能適應:根據地圖大小動態調整
  • 響應式設計:在不同螢幕尺寸下都能正常顯示

2. 視覺體驗提升

  • 節點更緊密:節點間距縮小,整體更緊湊
  • 直線邊預設:邊線樣式預設為直線,更簡潔
  • 背景圖優化:背景圖比地圖大 200px,提供更好的視覺緩衝

3. 性能優化

  • 減少 DOM 空間:避免不必要的頁面空間佔用
  • 智能滾動:只在需要時啟用滾動功能
  • 記憶用戶設定:用戶的選擇會被記住

🔧 技術要點總結

1. 頁面高度計算

// 精確計算所需頁面高度
const minPageHeight = headerHeight + graphHeight + graphPadding + bodyPadding + 20;
document.body.style.height = minPageHeight + 'px';

2. 元素尺寸限制

// 限制過大元素
const maxReasonableWidth = window.innerWidth * 3;
const maxReasonableHeight = window.innerHeight * 3;
const finalWidth = Math.min(origW, maxReasonableWidth);
const finalHeight = Math.min(origH, maxReasonableHeight);

3. 智能布局處理

// 根據地圖大小智能處理
const isLargeGraph = graphWidth > window.innerWidth * 2 || graphHeight > window.innerHeight * 2;
if (isLargeGraph) {
  // 大地圖:限制高度,啟用滾動
} else {
  // 小地圖:正常顯示
}

🚀 未來展望

今天的優化為 GASO 專案帶來了顯著的視覺體驗提升:

  1. 提升了用戶體驗:固定圖標位置,新增載入動畫
  2. 解決了空白問題:頁面布局更加精確
  3. 提升了視覺效果:節點更緊密,邊線更簡潔
  4. 優化了整體體驗:智能適應不同大小的地圖

這些改進讓 GASO 成為一個更加專業和用戶友好的學習地圖工具。在接下來的開發中,我們可以繼續專注於功能擴展和用戶體驗的進一步優化。


Day 17 完成! 🎉

今天的優化讓 GASO 的頁面布局更加完美,視覺體驗更加出色。從解決空白問題到優化節點排列,每一步都讓這個學習地圖工具變得更加專業和易用。

明日預告:我們將繼續探索更多功能,讓 GASO 變得更加強大!


如果想要看一些我鐵人賽之外的 Google Apps Script 分享,
也歡迎追蹤我的 ThreadsFacebook


上一篇
Day 16: 從零到一:GASO 專案的完整部署指南
系列文
亨利羊帶你 Google Apps Script 從入門到精通:放棄長篇大論的教學吧,你需要的只是一些精心設計的 prompt!17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言