在 GASO 專案的第 17 天,我們專注於解決頁面布局問題,提升視覺體驗。主要目標包括:
原本的 setup 圖標會隨著頁面滾動而移動,影響用戶體驗。
頁面載入時沒有視覺反饋,用戶不知道系統正在處理。
在測試過程中發現頁面下方存在大量空白,經過分析發現問題根源:
// 問題:強制使用螢幕高度
const calculatedHeight = Math.max(minPageHeight, window.innerHeight);
這會導致即使內容不需要那麼高,頁面也會被強制拉伸到螢幕高度。
檢查發現 #zoomInner
元素尺寸異常:
這個巨大的尺寸是造成頁面空白的主要原因。
.sidebar-toggle {
position: fixed; /* 改為 fixed 定位 */
top: 20px;
right: 20px;
/* ... 其他樣式保持不變 ... */
}
將 setup 圖標的定位從 absolute
改為 fixed
,確保它始終固定在螢幕右上角,不會隨頁面滾動而移動。
.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; }
}
<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>
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();
// 修復前:強制使用螢幕高度
const calculatedHeight = Math.max(minPageHeight, window.innerHeight);
// 修復後:精確計算所需高度
const calculatedHeight = minPageHeight;
document.body.style.height = calculatedHeight + 'px';
// 限制最大尺寸,避免過大的元素
const maxReasonableWidth = window.innerWidth * 3;
const maxReasonableHeight = window.innerHeight * 3;
const finalWidth = Math.min(origW, maxReasonableWidth);
const finalHeight = Math.min(origH, maxReasonableHeight);
// 智能處理大地圖
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';
}
在 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
:控制不同層級節點之間的垂直間距// 將預設邊線樣式改為直線
splines: localStorage.getItem("gv_splines") || "line",
#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)。
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';
}
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';
}
}
// 精確計算所需頁面高度
const minPageHeight = headerHeight + graphHeight + graphPadding + bodyPadding + 20;
document.body.style.height = minPageHeight + 'px';
// 限制過大元素
const maxReasonableWidth = window.innerWidth * 3;
const maxReasonableHeight = window.innerHeight * 3;
const finalWidth = Math.min(origW, maxReasonableWidth);
const finalHeight = Math.min(origH, maxReasonableHeight);
// 根據地圖大小智能處理
const isLargeGraph = graphWidth > window.innerWidth * 2 || graphHeight > window.innerHeight * 2;
if (isLargeGraph) {
// 大地圖:限制高度,啟用滾動
} else {
// 小地圖:正常顯示
}
今天的優化為 GASO 專案帶來了顯著的視覺體驗提升:
這些改進讓 GASO 成為一個更加專業和用戶友好的學習地圖工具。在接下來的開發中,我們可以繼續專注於功能擴展和用戶體驗的進一步優化。
Day 17 完成! 🎉
今天的優化讓 GASO 的頁面布局更加完美,視覺體驗更加出色。從解決空白問題到優化節點排列,每一步都讓這個學習地圖工具變得更加專業和易用。
明日預告:我們將繼續探索更多功能,讓 GASO 變得更加強大!
如果想要看一些我鐵人賽之外的 Google Apps Script 分享,
也歡迎追蹤我的 Threads 和 Facebook