🧼 3D 列印機維護指南:熱端、噴嘴、皮帶、軸承、滑軌的保養週期與技巧
不管是 Bambu、ELEGOO、Creality 還是任何 CoreXY/FDM 印表機,只要維護得好,都能保持高速又穩定。 本篇整理 完整維護清單,包含噴嘴、熱端、擠出機、皮帶、滑軌、軸承、風扇、電源等所有必須保養的部件。 透過這篇,你可以直接建立屬於自己的「列印機保養週期制度」。
📌 一、噴嘴維護(Nozzle Maintenance)
✓ 建議週期:每 1~2 kg 耗材
- 清除碳化塑料
- 使用 0.4 清潔針疏通殘料
- 必要時執行 Cold Pull(冷拔)
# Cold Pull 步驟摘要
1. 加熱到材料列印溫度
2. 放置 5 秒後降溫至 90°C(PLA)
3. 一次拉出整條材料
若使用碳纖(CF)材料:建議改用 Hardened Steel 噴嘴。
📌 二、熱端維護(Hotend)
✓ 建議週期:每 3~6 kg 耗材
- 檢查熱斷(Heatbreak)是否積碳
- 清理導熱膏(適用於全金屬熱端)
- 檢查溫度偵測器是否鬆脫
- 檢查加熱棒是否固定良好
📌 三、皮帶張力(Belt Tension)
✓ 建議週期:每 1 個月檢查一次
皮帶太鬆 → 層移、邊緣振動 皮帶太緊 → 馬達負載增加、噪音變大
- CoreXY 需要保持左右皮帶張力一致
- Ender 類床移動機需檢查 Y 軸張力
現代機(如 Bambu、Centauri Carbon)可使用自動張力檢測。
📌 四、滑軌、線軌(Linear Rail)與軸承保養
✓ 建議週期:每 2~3 個月
- 清潔灰塵(避免導致卡滯)
- 補充機械用潤滑油(如 Super Lube)
- 檢查是否有「卡點」、「阻力突增」
線軌比 V-Slot 準確,但需要定期維護才會滑順。
📌 五、擠出機(Extruder)保養
✓ 建議週期:每 3~5 kg 耗材
- 檢查齒輪是否磨損
- 清除齒輪上卡住的塑料粉塵
- 確保彈簧壓力正常(直驅特別重要)
📌 六、風扇(Fans)清潔
✓ 建議週期:每 1~3 個月
- 冷卻風扇(Part Cooling Fan)
- 熱端風扇(Hotend Fan)
- 主機板風扇(Electronics Fan)
若風扇轉速變低 → 將直接導致噴嘴過熱或冷卻不足。
📌 七、熱床清潔(Print Bed Maintenance)
✓ 建議週期:每次長列印前後
- PLA:酒精清潔
- PETG:避免直接酒精清潔(會變黏),改用肥皂水
- ABS/ASA:可使用膠水層避免太強吸附
📌 八、電子與機構檢查(每 6 個月)
- 檢查電源接頭是否鬆動
- 檢查主機板散熱
- 檢查風扇是否卡灰塵
- 確認所有轉軸是否正常運作
📌 九、建議保養週期表(可收藏)
| 部件 | 建議週期 |
|---|---|
| 噴嘴 | 1–2 kg 耗材 |
| 熱端 | 3–6 kg 耗材 |
| 皮帶張力 | 1 個月 |
| 線軌/軸承 | 2–3 個月 |
| 風扇 | 1–3 個月 |
| 熱床清潔 | 每次列印前後 |
| 電子檢查 | 6 個月 |
📘 結語
維護是讓 3D 列印機「越用越順」的關鍵。 只要建立一套固定保養週期,你的列印機能長期保持高速、穩定、低噪音與高品質輸出。 本篇也適合列印出來貼在你的工作區,當作固定巡檢表!
📚 3D 列印系列文章
- 🧱 3D 列印完整入門指南:運作原理、應用情境與設備選購一次搞懂
- 🖨️ 消費級 3D 列印機選購指南:FDM / SLA 比較、核心規格解析
- 📦 3D 列印模型來源大全:Printables、Thingiverse、MakerWorld、Cults 平台比較與下載技巧
- ⚙️ ELEGOO 與 Bambu Lab 比較:速度、精度、AMS、多材料全面解析
- 🏷️ 常見消費級品牌比較:ELEGOO、Creality、Anycubic、Bambu Lab 的定位與選擇策略
- ⚡ 超高速 3D 列印機比較:Centauri Carbon、Bambu P1/P2、K1 Max 完整分析
- 🧵 3D 列印材料百科:PLA、ABS、PETG、TPU、Nylon、CF 系列完整解析
- 🔥 列印溫度、速度與冷卻設定指南:提升成功率與表面品質的完整技術筆記
- 📏 3D 列印校正大全:Flow、EM、Z-Offset、Retraction、溫度塔完整教學
- 🔧 = 1100) { var hoverZone = document.getElementById('sidebar-hover-zone'); var hoverDelay = null; function startHoverOpen() { clearTimeout(hoverDelay); if (!isSidebarOpen()) openSidebar(); } function scheduleHoverClose() { clearTimeout(hoverDelay); hoverDelay = setTimeout(function(){ if (isSidebarOpen()) closeSidebar(); }, 300); } [fab, hoverZone, sidebar].forEach(function(el) { if (!el) return; el.addEventListener('mouseenter', startHoverOpen); el.addEventListener('mouseleave', scheduleHoverClose); }); } try { if (localStorage.getItem('wwf_sb2') === '1') { openSidebar(); } else { syncFab(); } } catch(e) { syncFab(); } document.addEventListener('click', function(e) { if (window.innerWidth >= 1100) return; if (!isSidebarOpen()) return; if (sidebar && !sidebar.contains(e.target) && fab && !fab.contains(e.target)) closeSidebar(); }); /* 3. READING PROGRESS BAR */ var progressBar = document.getElementById('reading-progress'); var tocFloatBar = document.getElementById('toc-float-bar'); if (progressBar) { function updateProgress() { var article = document.querySelector('.full-post-body') || document.getElementById('content'); if (!article) return; var rect = article.getBoundingClientRect(); var total = rect.height - window.innerHeight; var scrolled = Math.max(0, -rect.top); var pct = total > 0 ? Math.min(100, (scrolled / total) * 100) : 0; progressBar.style.width = pct + '%'; if (tocFloatBar) tocFloatBar.style.height = pct + '%'; var pctEl = document.getElementById('progress-pct'); if (pctEl) pctEl.textContent = Math.round(pct); var pctEl = document.getElementById('progress-pct'); if (pctEl) pctEl.textContent = Math.round(pct); } if (document.body.classList.contains('item')) { window.addEventListener('scroll', updateProgress, { passive: true }); updateProgress(); } } /* 4. BACK TO TOP */ var bttBtn = document.getElementById('back-to-top'); if (bttBtn) { window.addEventListener('scroll', function() { bttBtn.classList.toggle('visible', window.scrollY > 400); }, { passive: true }); bttBtn.addEventListener('click', function(e) { e.preventDefault(); window.scrollTo({ top: 0, behavior: 'smooth' }); }); } /* 5. SEARCH OVERLAY */ var searchOverlay = document.getElementById('search-overlay'); var searchToggle = document.getElementById('search-toggle'); var searchInput = document.getElementById('search-input'); var lastFocusBeforeSearch = null; function openSearch() { if (!searchOverlay) return; lastFocusBeforeSearch = document.activeElement; searchOverlay.classList.add('open'); if (searchInput) setTimeout(function(){ searchInput.focus(); }, 50); } function closeSearch() { if (!searchOverlay) return; searchOverlay.classList.remove('open'); if (lastFocusBeforeSearch) lastFocusBeforeSearch.focus({ preventScroll: true }); } if (searchToggle) searchToggle.addEventListener('click', function(e){ e.preventDefault(); openSearch(); }); if (searchOverlay) searchOverlay.addEventListener('click', function(e){ if (e.target === searchOverlay) closeSearch(); }); if (searchInput) { searchInput.addEventListener('keydown', function(e) { if (e.key === 'Enter' && this.value.trim()) { window.location.href = window.location.protocol + '//' + window.location.hostname + '/search?q=' + encodeURIComponent(this.value.trim()); } }); } document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { closeSearch(); closeLightbox(); } if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault(); openSearch(); } }); /* 6. POPULAR STRIP TICKER */ (function loadTicker() { var tickerEl = document.getElementById('ticker-inner'); if (!tickerEl) return; var origin = window.location.protocol + '//' + window.location.hostname; window._wwfTicker = function(data) { try { var entries = (data && data.feed && data.feed.entry) ? data.feed.entry : []; if (!entries.length) { tickerEl.textContent = ''; return; } var html = ''; var sep = ' ✦ '; entries.forEach(function(entry) { var title = entry.title ? entry.title.$t : ''; var url = ''; if (entry.link) { for (var j = 0; j < entry.link.length; j++) { if (entry.link[j].rel === 'alternate') { url = entry.link[j].href; break; } } } if (title && url) html += '' + title + '' + sep; }); if (!html) { tickerEl.textContent = ''; return; } tickerEl.innerHTML = html + html; tickerEl.style.opacity = '1'; tickerEl.style.fontStyle = 'normal'; // 依內容長度動態調整速度:每個字元 ~0.18s,最少 50s var charCount = tickerEl.textContent.replace(/\s+/g,'').length / 2; var dur = Math.max(50, Math.round(charCount * 0.18)); tickerEl.style.animationDuration = dur + 's'; } catch(err) {} delete window._wwfTicker; }; var s = document.createElement('script'); s.async = true; s.src = origin + '/feeds/posts/summary?alt=json&max-results=20&orderby=published&callback=_wwfTicker'; s.onerror = function() { if (tickerEl) tickerEl.textContent = ''; }; document.head.appendChild(s); })(); /* 7. LABEL TOOLBAR */ (function buildLabelToolbar() { var pillsEl = document.getElementById('ltb-pills'); if (!pillsEl) return; var activeLabelRaw = ''; var labelMatch = window.location.pathname.match(/\/search\/label\/(.+)/); if (labelMatch) { try { activeLabelRaw = decodeURIComponent(labelMatch[1]); } catch(e) { activeLabelRaw = labelMatch[1]; } } var CACHE_KEY = 'wwf_top_labels_v6'; var CACHE_TTL = 4 * 60 * 60 * 1000; function renderPills(labels) { if (!labels || !labels.length) { pillsEl.innerHTML = ''; return; } var origin = window.location.protocol + '//' + window.location.hostname; var medals = ['①','②','③','④','⑤','⑥','⑦','⑧','⑨','⑩']; var html = ''; labels.forEach(function(item, idx) { var labelUrl = origin + '/search/label/' + encodeURIComponent(item.name); var isActive = activeLabelRaw && (activeLabelRaw.toLowerCase() === item.name.toLowerCase()); html += '' + '' + (medals[idx] || (idx+1)+'.') + '' + item.name + '' + item.count + ''; }); pillsEl.innerHTML = html; pillsEl.querySelectorAll('.ltb-pill').forEach(function(el, i) { el.style.opacity = '0'; el.style.transform = 'translateY(6px)'; setTimeout(function() { el.style.transition = 'opacity .35s ease, transform .35s ease'; el.style.opacity = '1'; el.style.transform = 'translateY(0)'; }, i * 60 + 20); }); } // 清除所有舊版快取 try { ['wwf_top_labels_v3','wwf_top_labels_v4','wwf_top_labels_v5'].forEach(function(k){ localStorage.removeItem(k); }); } catch(e) {} try { var cached = JSON.parse(localStorage.getItem(CACHE_KEY) || 'null'); if (cached && cached.ts && (Date.now() - cached.ts < CACHE_TTL) && cached.labels) { renderPills(cached.labels); return; } } catch(e) {} var origin = window.location.protocol + '//' + window.location.hostname; var allCounts = {}; var fetched = 0; var batchSize = 500; var totalPosts = 0; // 從第一批的 totalResults 取得 function processBatch(data) { try { var feed = data && data.feed; var entries = (feed && feed.entry) ? feed.entry : []; // 讀 totalResults(第一批才有) if (fetched === 0 && feed) { var tr = feed['openSearch$totalResults']; if (tr && tr['$t']) totalPosts = parseInt(tr['$t'], 10) || 0; } entries.forEach(function(entry) { if (!entry.category) return; entry.category.forEach(function(cat) { if (cat.term) allCounts[cat.term] = (allCounts[cat.term] || 0) + 1; }); }); fetched += entries.length; // totalPosts 已知:比較 fetched;未知:靠 entries.length var hasMore = totalPosts > 0 ? fetched < totalPosts : entries.length === batchSize; if (hasMore) { fetchBatch(fetched + 1); } else { finalize(); } } catch(err) { finalize(); } } function finalize() { var sorted = Object.keys(allCounts) .map(function(k) { return { name: k, count: allCounts[k] }; }) .sort(function(a, b) { return b.count - a.count; }) .slice(0, 10); renderPills(sorted); try { localStorage.setItem(CACHE_KEY, JSON.stringify({ ts: Date.now(), labels: sorted })); } catch(e) {} } function fetchBatch(startIdx) { var cb = '_wwfLabels' + startIdx; window[cb] = function(data) { delete window[cb]; processBatch(data); }; var s = document.createElement('script'); s.async = true; // 不加 fields 限制,讓 totalResults 可以回傳 s.src = origin + '/feeds/posts/summary?alt=json&max-results=' + batchSize + '&start-index=' + startIdx + '&callback=' + cb; s.onerror = function() { finalize(); }; document.head.appendChild(s); } window._wwfLabels = function(data) { delete window._wwfLabels; processBatch(data); }; var s = document.createElement('script'); s.async = true; s.src = origin + '/feeds/posts/summary?alt=json&max-results=' + batchSize + '&start-index=1&callback=_wwfLabels'; s.onerror = function() { finalize(); }; document.head.appendChild(s); })(); /* 8. READING TIME */ var readTimeEl = document.getElementById('post-read-time'); var postBody = document.querySelector('.full-post-body'); if (readTimeEl && postBody) { var chars = (postBody.innerText || postBody.textContent || '').replace(/\s/g,'').length; var mins = Math.max(1, Math.round(chars / 500)); readTimeEl.innerHTML = ' 約 ' + mins + ' 分鐘'; } /* 8b. DROP CAP — only on pure-text first paragraph */ (function applyDropCap() { var body = document.querySelector('.full-post-body'); if (!body) return; var firstP = body.querySelector('p'); if (!firstP) return; var txt = (firstP.textContent || '').trim(); if (/^[\u4e00-\u9fff\u3400-\u4dbfa-zA-Z]/.test(txt)) { firstP.classList.add('dropcap'); } })(); /* 9. TABLE OF CONTENTS */ (function buildTOC() { var tocList = document.getElementById('toc-list'); var tocToggle = document.getElementById('toc-toggle'); var tocFloat = document.getElementById('toc-float'); var tocFloatList = document.getElementById('toc-float-list'); var body = document.querySelector('.full-post-body'); if (!body) return; var headings = body.querySelectorAll('h2, h3'); if (headings.length < 2) { var tc = document.getElementById('toc-container'); if (tc) tc.style.display = 'none'; if (tocFloat) tocFloat.style.display = 'none'; return; } var inlineHtml = '', floatHtml = ''; var tocNum = 0; headings.forEach(function(h, idx) { var id = 'toc-heading-' + idx; h.id = id; var lvl = h.tagName.toLowerCase(); var raw = h.textContent.trim(); var emojiRe = /^([\u{1F000}-\u{1FFFF}\u{2600}-\u{27BF}\u{1F900}-\u{1F9FF}\u{1FA00}-\u{1FA9F}]+)/u; var emojiMatch = raw.match(emojiRe); var icon = emojiMatch ? emojiMatch[1] : ''; var txt = icon ? raw.slice(icon.length).trim() : raw; if (lvl === 'h2') { tocNum++; } var numStr = lvl === 'h2' ? String(tocNum).padStart(2, '0') : '\u00a0\u00a0'; inlineHtml += '
- ' + '' + numStr + '' + (icon ? '' : '') + '' + txt + ' '; floatHtml += '
- ' + (icon ? icon + '\u2009' : '') + txt + ' '; }); if (tocList) tocList.innerHTML = inlineHtml; if (tocFloatList) tocFloatList.innerHTML = floatHtml; if (tocToggle && tocList) { tocToggle.addEventListener('click', function() { var collapsed = tocList.classList.toggle('collapsed'); tocToggle.classList.toggle('collapsed', collapsed); tocToggle.setAttribute('aria-expanded', collapsed ? 'false' : 'true'); }); } if (tocFloat) { var articleEl = document.querySelector('.post-full'); var navH = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--nav-h')) || 54; function updateTocFloat() { if (!articleEl) return; var rect = articleEl.getBoundingClientRect(); // 顯示:文章頂部已捲過 header,且文章底部還在視窗內 var shouldShow = rect.top < navH + 100 && rect.bottom > window.innerHeight * 0.3; tocFloat.classList.toggle('visible', shouldShow); } window.addEventListener('scroll', updateTocFloat, { passive: true }); window.addEventListener('resize', updateTocFloat, { passive: true }); updateTocFloat(); } function makeActiveObserver(listEl) { if (!listEl) return; var links = listEl.querySelectorAll('a'); var isInline = (listEl === tocList); var obs = new IntersectionObserver(function(entries) { entries.forEach(function(entry) { if (entry.isIntersecting) { links.forEach(function(a) { a.classList.remove('active'); if (isInline && a.closest('li')) a.closest('li').classList.remove('toc-active'); }); var active = listEl.querySelector('a[href="#' + entry.target.id + '"]'); if (active) { active.classList.add('active'); if (isInline && active.closest('li')) active.closest('li').classList.add('toc-active'); } var fill = document.getElementById('toc-read-fill'); var pctEl2 = document.getElementById('toc-float-pct'); if (fill || pctEl2) { var allH = Array.from(headings); var cur = allH.indexOf(entry.target); if (cur >= 0) { var pct2 = Math.round((cur / Math.max(allH.length - 1, 1)) * 100); if (fill) fill.style.width = pct2 + '%'; if (pctEl2) pctEl2.textContent = pct2 + '%'; } } } }); }, { rootMargin: '-20% 0px -75% 0px' }); headings.forEach(function(h) { obs.observe(h); }); links.forEach(function(a) { a.addEventListener('click', function(e) { e.preventDefault(); var target = document.getElementById(this.getAttribute('href').slice(1)); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); target.focus({ preventScroll: true }); } }); }); } makeActiveObserver(tocList); makeActiveObserver(tocFloatList); })(); /* 10. LIGHTBOX */ var lightbox = document.getElementById('lightbox'); var lightboxImg = document.getElementById('lightbox-img'); var lightboxClose = document.getElementById('lightbox-close'); var lightboxCap = document.getElementById('lightbox-caption'); var lightboxPrev = document.getElementById('lightbox-prev'); var lightboxNext = document.getElementById('lightbox-next'); var lastFocusBeforeLightbox = null; var galleryImgs = []; var galleryIdx = 0; if (postBody) galleryImgs = Array.from(postBody.querySelectorAll('img')); function openLightbox(src, alt, idx) { if (!lightbox || !lightboxImg) return; lastFocusBeforeLightbox = document.activeElement; lightboxImg.src = src; lightboxImg.alt = alt || ''; if (lightboxCap) lightboxCap.textContent = alt || ''; galleryIdx = typeof idx === 'number' ? idx : 0; if (lightboxPrev) lightboxPrev.style.display = galleryImgs.length > 1 ? '' : 'none'; if (lightboxNext) lightboxNext.style.display = galleryImgs.length > 1 ? '' : 'none'; lightbox.classList.add('open'); document.body.style.overflow = 'hidden'; if (lightboxClose) lightboxClose.focus(); } function closeLightbox() { if (!lightbox) return; lightbox.classList.remove('open'); document.body.style.overflow = ''; if (lastFocusBeforeLightbox) lastFocusBeforeLightbox.focus({ preventScroll: true }); } function navLightbox(dir) { if (!galleryImgs.length) return; galleryIdx = (galleryIdx + dir + galleryImgs.length) % galleryImgs.length; var img = galleryImgs[galleryIdx]; if (lightboxImg) { lightboxImg.style.opacity = '0'; setTimeout(function(){ lightboxImg.src = img.src; lightboxImg.alt = img.alt || ''; lightboxImg.style.opacity = '1'; if (lightboxCap) lightboxCap.textContent = img.alt || ''; }, 150); } } if (lightboxClose) lightboxClose.addEventListener('click', closeLightbox); if (lightbox) lightbox.addEventListener('click', function(e){ if (e.target === lightbox) closeLightbox(); }); if (lightboxPrev) lightboxPrev.addEventListener('click', function(e){ e.stopPropagation(); navLightbox(-1); }); if (lightboxNext) lightboxNext.addEventListener('click', function(e){ e.stopPropagation(); navLightbox(1); }); if (postBody) { galleryImgs.forEach(function(img, idx) { img.style.cursor = 'zoom-in'; img.setAttribute('tabindex','0'); img.setAttribute('role','button'); img.addEventListener('click', function() { openLightbox(this.src, this.alt || '', idx); }); img.addEventListener('keydown', function(e){ if (e.key === 'Enter' || e.key === ' '){ e.preventDefault(); openLightbox(this.src, this.alt || '', idx); } }); }); } /* 11. SCROLL-REVEAL */ if ('IntersectionObserver' in window) { var cards = document.querySelectorAll('.tl-card'); if (cards.length) { cards.forEach(function(c) { c.style.opacity = '0'; c.style.transform = 'translateY(24px)'; }); var revObs = new IntersectionObserver(function(entries) { entries.forEach(function(entry) { if (entry.isIntersecting) { var el = entry.target; el.style.transition = 'opacity .5s ease, transform .5s ease'; el.style.opacity = '1'; el.style.transform = 'translateY(0)'; revObs.unobserve(el); } }); }, { threshold: 0.08 }); cards.forEach(function(c, i) { setTimeout(function(){ revObs.observe(c); }, i * 40); }); } } /* 12. HERO PARALLAX */ var heroImg = document.getElementById('hero-img'); if (heroImg) { window.addEventListener('scroll', function() { heroImg.style.transform = 'translateY(' + (window.scrollY * 0.25) + 'px)'; }, { passive: true }); } /* 13. CODE BLOCK ENHANCEMENTS — header bar design */ if (postBody) { postBody.querySelectorAll('pre').forEach(function(pre) { var code = pre.querySelector('code'); var rawCls = ((code ? code.className : pre.className) || ''); var clsMatch = rawCls.match(/language-([a-z0-9+#-]+)/i); // Header bar var header = document.createElement('div'); header.className = 'code-header'; var langLabel = document.createElement('span'); langLabel.className = 'code-lang-label'; langLabel.setAttribute('aria-hidden', 'true'); langLabel.textContent = clsMatch ? clsMatch[1] : 'shell'; var copyBtn = document.createElement('button'); copyBtn.className = 'code-copy-btn'; copyBtn.type = 'button'; copyBtn.textContent = '複製'; copyBtn.setAttribute('aria-label', '複製程式碼'); copyBtn.addEventListener('click', function() { var textToCopy = code ? code.innerText : pre.innerText; navigator.clipboard.writeText(textToCopy || '').then(function() { copyBtn.innerHTML = '✓ 已複製'; copyBtn.classList.add('copied'); setTimeout(function() { copyBtn.textContent = '複製'; copyBtn.classList.remove('copied'); }, 2000); }).catch(function() { copyBtn.textContent = '失敗'; setTimeout(function(){ copyBtn.textContent = '複製'; }, 2000); }); }); header.appendChild(langLabel); header.appendChild(copyBtn); // Wrap: code-block-wrap > header + pre var wrap = document.createElement('div'); wrap.className = 'code-block-wrap'; pre.parentNode.insertBefore(wrap, pre); wrap.appendChild(header); wrap.appendChild(pre); pre.style.margin = '0'; pre.style.borderTop = 'none'; pre.style.borderRadius = '0 0 9px 0'; }); } /* 14. HEADING ANCHORS */ if (postBody) { postBody.querySelectorAll('h2, h3, h4').forEach(function(h) { if (!h.id) { var id = h.textContent.trim().toLowerCase().replace(/\s+/g,'-').replace(/[^\w\u4e00-\u9fff\-]/g,'').replace(/^-+|-+$/g,''); if (id) h.id = id; } if (h.id) { var a = document.createElement('a'); a.href = '#' + h.id; a.className = 'heading-anchor'; a.setAttribute('aria-label','連結至此段落'); a.innerHTML = ''; h.appendChild(a); } }); } /* 15. EXTERNAL LINKS */ if (postBody) { postBody.querySelectorAll('a[href]').forEach(function(a) { var href = a.getAttribute('href') || ''; if (href.startsWith('http') && !href.includes(window.location.hostname)) { var rel = (a.getAttribute('rel') || ''); if (!rel.includes('noopener')) a.setAttribute('rel', (rel + ' noopener noreferrer').trim()); a.setAttribute('target','_blank'); } }); } /* 16. FONT SIZE CONTROLLER */ (function initFontSizeCtrl() { var SIZES = [14,15,15.5,16,16.5,17,18,19,20]; var LS_KEY = 'wwf_font_size_v1'; var DEFAULT_IDX = 4; var curIdx = DEFAULT_IDX; var fscUp = document.getElementById('fsc-up'); var fscDown = document.getElementById('fsc-down'); var fscReset = document.getElementById('fsc-reset'); function applySize(idx) { curIdx = Math.max(0, Math.min(SIZES.length - 1, idx)); var pb = document.querySelector('.full-post-body'); if (pb) pb.style.fontSize = SIZES[curIdx] + 'px'; try { localStorage.setItem(LS_KEY, curIdx); } catch(e) {} if (fscDown) fscDown.disabled = curIdx <= 0; if (fscUp) fscUp.disabled = curIdx >= SIZES.length - 1; } try { var s = parseInt(localStorage.getItem(LS_KEY), 10); if (!isNaN(s)) curIdx = s; } catch(e) {} applySize(curIdx); if (fscUp) fscUp.addEventListener('click', function(){ applySize(curIdx + 1); }); if (fscDown) fscDown.addEventListener('click', function(){ applySize(curIdx - 1); }); if (fscReset) fscReset.addEventListener('click', function(){ applySize(DEFAULT_IDX); }); })(); /* 17. COPY URL */ var copyUrlBtn = document.getElementById('copy-url-btn'); if (copyUrlBtn) { copyUrlBtn.addEventListener('click', function() { navigator.clipboard.writeText(window.location.href).then(function() { copyUrlBtn.classList.add('copied'); copyUrlBtn.innerHTML = '已複製!'; setTimeout(function(){ copyUrlBtn.classList.remove('copied'); copyUrlBtn.innerHTML = '複製連結'; }, 2400); }); }); } /* 18a. VIEW TOGGLE */ function setView(mode) { var grid = document.getElementById('cards-grid'); var btnCard = document.getElementById('btn-card'); var btnList = document.getElementById('btn-list'); if (!grid) return; if (mode === 'list') { grid.classList.add('list-view'); if (btnCard) btnCard.classList.remove('active'); if (btnList) btnList.classList.add('active'); } else { grid.classList.remove('list-view'); if (btnCard) btnCard.classList.add('active'); if (btnList) btnList.classList.remove('active'); } try { localStorage.setItem('wwf_view_mode', mode); } catch(e) {} } // 綁定切換按鈕 — 用 addEventListener 避開 Blogger XML " 轉義問題 (function bindViewToggle() { var bar = document.getElementById('view-toggle-bar'); if (!bar) return; bar.addEventListener('click', function(e) { var btn = e.target.closest('[data-mode]'); if (!btn) return; setView(btn.getAttribute('data-mode')); }); // 還原上次選擇 try { var saved = localStorage.getItem('wwf_view_mode'); if (saved) setView(saved); } catch(e) {} })(); /* 18b. INFINITE SCROLL — 自動載入下一頁 */ (function initInfiniteScroll() { // 只在首頁/標籤頁啟用 if (document.body.classList.contains('item') || document.body.classList.contains('static_page')) return; var loading = false; var noMore = false; var grid = document.getElementById('cards-grid'); var pager = document.getElementById('blog-pager'); if (!grid || !pager) return; // 取得「較舊文章」連結 function getOlderUrl() { var a = document.getElementById('blog-pager-older-link'); if (!a) return null; var link = a.querySelector('a'); return link ? link.href : null; } // 隱藏分頁列(改為自動載入) pager.style.display = 'none'; // 載入指示器 var spinner = document.createElement('div'); spinner.id = 'inf-spinner'; spinner.style.cssText = 'grid-column:1/-1;text-align:center;padding:32px 0;font-size:12px;color:var(--ink-3);letter-spacing:.08em;display:none;'; spinner.innerHTML = '載入更多…'; grid.parentNode.insertBefore(spinner, pager); // 「沒有更多」提示 var endMsg = document.createElement('div'); endMsg.style.cssText = 'grid-column:1/-1;text-align:center;padding:24px 0;font-size:11px;color:var(--ink-3);letter-spacing:.1em;display:none;text-transform:uppercase;'; endMsg.textContent = '· 已顯示全部文章 ·'; grid.parentNode.insertBefore(endMsg, pager); function loadMore() { var url = getOlderUrl(); if (!url || loading || noMore) return; loading = true; spinner.style.display = 'block'; fetch(url) .then(function(r) { return r.text(); }) .then(function(html) { var parser = new DOMParser(); var doc = parser.parseFromString(html, 'text/html'); // 抓新頁的卡片 var newCards = doc.querySelectorAll('.tl-card'); newCards.forEach(function(card, i) { card.style.opacity = '0'; card.style.transform = 'translateY(20px)'; grid.appendChild(card); // 入場動畫 setTimeout(function() { card.style.transition = 'opacity .4s ease, transform .4s ease'; card.style.opacity = '1'; card.style.transform = 'translateY(0)'; }, i * 40 + 50); }); // 更新分頁連結(從新頁面取得) var newPager = doc.getElementById('blog-pager'); if (newPager) { pager.innerHTML = newPager.innerHTML; } // 判斷是否還有更多 var nextUrl = getOlderUrl(); if (!nextUrl || newCards.length === 0) { noMore = true; endMsg.style.display = 'block'; } }) .catch(function() { // fetch 失敗(CORS 等),fallback:顯示原本的分頁按鈕 pager.style.display = ''; noMore = true; }) .finally(function() { loading = false; spinner.style.display = 'none'; }); } // 監聽捲動 window.addEventListener('scroll', function() { if (noMore || loading) return; var scrollBottom = window.scrollY + window.innerHeight; var pageHeight = document.documentElement.scrollHeight; if (scrollBottom >= pageHeight - 400) { loadMore(); } }, { passive: true }); })(); /* 18. ACTIVE NAV */ var curPath = window.location.pathname; document.querySelectorAll('#main-nav a.nav-item').forEach(function(a) { var href = a.getAttribute('href') || ''; try { if (href && curPath === new URL(href, window.location.href).pathname) a.classList.add('active'); } catch(e) {} }); if (!document.querySelector('#main-nav a.nav-item.active')) { var first = document.querySelector('#main-nav a.nav-item'); if (first) first.classList.add('active'); } /* 19. READING PROGRESS BADGE in meta */ (function initReadProgress() { var badge = document.getElementById('post-read-progress'); if (!badge) return; // hide on non-article pages if (!document.body.classList.contains('item')) { badge.style.display = 'none'; return; } badge.style.display = 'inline-flex'; })(); })(); //]]>