diff --git a/themes/Ying/assets/css/main.css b/themes/Ying/assets/css/main.css index 3d72bd6f..de415ad8 100644 --- a/themes/Ying/assets/css/main.css +++ b/themes/Ying/assets/css/main.css @@ -2989,18 +2989,24 @@ input[type=submit] { max-width: 100%; margin: 1.2rem auto; line-height: 0; - opacity: 0; - transform: translateY(14px); + opacity: 1; + transform: none; transition: opacity 0.6s cubic-bezier(0.22, 0.61, 0.36, 1), transform 0.6s cubic-bezier(0.22, 0.61, 0.36, 1); content-visibility: auto; contain-intrinsic-size: auto 400px; } +.post-content .sen-image-frame.reveal-pending:not(.is-visible), +.entry-content .sen-image-frame.reveal-pending:not(.is-visible) { + opacity: 0; + transform: translateY(14px); +} + .post-content .sen-image-frame.is-visible, .entry-content .sen-image-frame.is-visible { opacity: 1; - transform: translateY(0); + transform: none; } .post-content .sen-image-frame>img, diff --git a/themes/Ying/assets/js/main.js b/themes/Ying/assets/js/main.js index c1adb3ff..e7e0023c 100644 --- a/themes/Ying/assets/js/main.js +++ b/themes/Ying/assets/js/main.js @@ -91,6 +91,10 @@ function initImageFrameReveal() { return; } + frames.forEach(function(frame) { + frame.classList.add('reveal-pending'); + }); + if (senFrameObserver) { senFrameObserver.disconnect(); } diff --git a/themes/Ying/assets/js/modules/artalk.js b/themes/Ying/assets/js/modules/artalk.js index 710ca0ce..2ea5a0ed 100644 --- a/themes/Ying/assets/js/modules/artalk.js +++ b/themes/Ying/assets/js/modules/artalk.js @@ -1,19 +1,40 @@ // Global Artalk Initialization Function (Supports PJAX) +window.loadScriptOnce = window.loadScriptOnce || function(src, globalName, callback) { + if (globalName && window[globalName]) { + callback(); + return; + } + + var existing = document.querySelector('script[data-dynamic-src="' + src + '"]'); + if (existing) { + existing.addEventListener('load', callback, { once: true }); + return; + } + + var script = document.createElement('script'); + script.src = src; + script.defer = true; + script.dataset.dynamicSrc = src; + script.onload = callback; + document.head.appendChild(script); +}; + window.initArtalk = function() { if (!document.querySelector("#Comments")) return; - + // Check if configuration exists if (!window.artalkConfig) { console.warn('window.artalkConfig is not defined. Skipping Artalk init.'); return; } - // Check if Artalk library is loaded if (typeof Artalk === 'undefined') { - console.warn('Artalk library is not loaded yet. Waiting for bundle...'); + var artalkUrl = window.optionalAssetUrls && window.optionalAssetUrls.artalk; + if (!artalkUrl) return; + window.loadScriptOnce(artalkUrl, 'Artalk', window.initArtalk); return; } - + try { // Logic for caching Artalk configuration to speed up rendering // 1. Manual Refresh (F5): Request from server and save to localStorage @@ -195,19 +216,11 @@ window.initArtalk = function() { // Force hide "No Comment" if AI comment exists if (window.artalkConfig.aiComment) { - var checkNoComment = setInterval(function() { - var noComment = document.querySelector('.atk-list-no-comment'); - var aiComment = document.querySelector('.atk-ai-comment'); - if (aiComment && noComment && noComment.style.display !== 'none') { - noComment.style.display = 'none'; - } - if (!document.querySelector('#Comments')) { - clearInterval(checkNoComment); - } - }, 500); - - // Clear interval after 10 seconds to save resources - setTimeout(function() { clearInterval(checkNoComment); }, 10000); + var noComment = document.querySelector('.atk-list-no-comment'); + var aiComment = document.querySelector('.atk-ai-comment'); + if (aiComment && noComment) { + noComment.style.display = 'none'; + } } }); diff --git a/themes/Ying/assets/js/modules/floating-tools.js b/themes/Ying/assets/js/modules/floating-tools.js index 06a10c81..72812905 100644 --- a/themes/Ying/assets/js/modules/floating-tools.js +++ b/themes/Ying/assets/js/modules/floating-tools.js @@ -1,3 +1,19 @@ +window.refreshFloatingTools = function() { + if (typeof window.initFloatingTools !== 'function') return; + var delays = [0, 60, 180]; + delays.forEach(function(delay) { + setTimeout(function() { + requestAnimationFrame(function() { + try { + window.initFloatingTools(); + } catch (e) { + console.error('initFloatingTools error:', e); + } + }); + }, delay); + }); +}; + window.initFloatingTools = function() { try { // 0. Global Toolbar Visibility (Homepage Check) @@ -7,29 +23,21 @@ window.initFloatingTools = function() { // 1. Check for Homepage-specific element (.recent-posts) // 2. Ensure NOT on a Post page (.post-content) // 3. Fallback: Check pathname (only if DOM check is ambiguous) - var hasRecentPosts = document.querySelector('.recent-posts'); - var hasPostContent = document.querySelector('.post-content'); - - var isHomePage = false; - if (hasRecentPosts && !hasPostContent) { - isHomePage = true; - } else if (!hasRecentPosts && !hasPostContent) { - // Could be Archive or Custom Page. Check Path. - var path = window.location.pathname; - if (path === '/' || path === '/index.html' || path.startsWith('/page/')) { - isHomePage = true; - } - } + var pjaxContainer = document.getElementById('pjax-container'); + var hasRecentPosts = !!(pjaxContainer && pjaxContainer.querySelector('.recent-posts')); + var hasArticle = !!(pjaxContainer && (pjaxContainer.querySelector('#post-writing') || pjaxContainer.querySelector('#Comments'))); + var path = window.location.pathname; + var isHomePage = !hasArticle && (hasRecentPosts || path === '/' || path === '/index.html' || path.startsWith('/page/')); if (container) { if (isHomePage) { container.style.display = 'none'; - // Do not return here, let other logic run in case we need to reset state - // But for performance, we can skip binding if hidden? - // No, better to bind so if it becomes visible later (unlikely without PJAX) it works. - // Actually, with PJAX, this function runs again. So we can just hide it. } else { - container.style.display = ''; // Restore display + container.style.display = 'flex'; + container.classList.remove('hidden'); + container.style.visibility = 'visible'; + container.style.opacity = '1'; + container.style.pointerEvents = 'auto'; } } @@ -55,18 +63,19 @@ window.initFloatingTools = function() { } if (window.IntersectionObserver) { - var observer = new IntersectionObserver(function(entries) { + if (window.backToTopObserver) { + window.backToTopObserver.disconnect(); + } + window.backToTopObserver = new IntersectionObserver(function(entries) { entries.forEach(function(entry) { if (!entry.isIntersecting) { - // Sentinel is out of view (scrolled down past 300px) backToTopButton.classList.add('show'); } else { - // Sentinel is in view (near top) backToTopButton.classList.remove('show'); } }); }); - observer.observe(sentinel); + window.backToTopObserver.observe(sentinel); } else { // Fallback for very old browsers var ticking = false; @@ -207,18 +216,32 @@ window.initFloatingTools = function() { window.lightboxObserver.disconnect(); } - if (container) { + if (container && !isHomePage) { + var lightboxToolsHidden = null; var syncLightboxState = function() { var lightbox = document.querySelector('.view-image'); + var lightboxOpen = false; if (lightbox) { + var rect = lightbox.getBoundingClientRect(); + var style = window.getComputedStyle(lightbox); + lightboxOpen = style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0' && rect.width > 0 && rect.height > 0; + } + if (lightboxToolsHidden === lightboxOpen) return; + lightboxToolsHidden = lightboxOpen; + + if (lightboxOpen) { container.classList.add('hidden'); } else { container.classList.remove('hidden'); + container.style.display = 'flex'; + container.style.visibility = 'visible'; + container.style.opacity = '1'; + container.style.pointerEvents = 'auto'; } }; window.lightboxObserver = new MutationObserver(function() { - syncLightboxState(); + requestAnimationFrame(syncLightboxState); }); window.lightboxObserver.observe(document.body, { childList: true, subtree: true }); diff --git a/themes/Ying/assets/js/modules/mypjax.js b/themes/Ying/assets/js/modules/mypjax.js index 8c3b63b7..4791ff5c 100644 --- a/themes/Ying/assets/js/modules/mypjax.js +++ b/themes/Ying/assets/js/modules/mypjax.js @@ -87,6 +87,10 @@ document.addEventListener("pjax:success", function () { if (window.ViewImage) { ViewImage.init('.post-content img'); } + + if (typeof window.refreshFloatingTools === 'function') { + window.refreshFloatingTools(); + } }); // pjax加载完成 @@ -141,10 +145,14 @@ document.addEventListener("pjax:complete", function () { } // 重载 Search 和 Floating Tools (Moved from main.js) - if (typeof window.initFloatingTools === 'function') { - try { - window.initFloatingTools(); - } catch(e) { console.error('initFloatingTools error:', e); } + if (typeof window.refreshFloatingTools === 'function') { + window.refreshFloatingTools(); + } else if (typeof window.initFloatingTools === 'function') { + requestAnimationFrame(function() { + try { + window.initFloatingTools(); + } catch(e) { console.error('initFloatingTools error:', e); } + }); } if (typeof window.initSearch === 'function') { try { diff --git a/themes/Ying/assets/js/modules/utils.js b/themes/Ying/assets/js/modules/utils.js index 81756dbe..0f77b14b 100644 --- a/themes/Ying/assets/js/modules/utils.js +++ b/themes/Ying/assets/js/modules/utils.js @@ -1,25 +1,43 @@ -// Sheet reveal — new theme slides in, covering old theme like pulling a curtain function themeSweep(newTheme, callback) { - var isH = Math.random() < 0.5; + var veil = document.createElement('div'); + var gradient = newTheme === 'dark' + ? 'linear-gradient(135deg,rgba(2,6,23,0.18),rgba(15,23,42,0.55) 45%,rgba(30,41,59,0.36))' + : 'linear-gradient(135deg,rgba(255,247,237,0.28),rgba(252,250,245,0.62) 45%,rgba(254,243,199,0.32))'; - var blank = newTheme === 'dark' ? '#111827' : '#fcfaf5'; - var sheet = document.createElement('div'); - sheet.style.cssText = - 'position:fixed;inset:0;z-index:9999;' + - 'background:' + blank + ';' + - 'clip-path:inset(0 0 0 0);' + - 'transition:clip-path 1.2s cubic-bezier(0.4,0,0.2,1);' + - 'will-change:clip-path;'; - document.body.appendChild(sheet); + veil.style.cssText = + 'position:fixed;inset:0;z-index:9999;pointer-events:none;' + + 'background:' + gradient + ';' + + 'opacity:0;transition:opacity 0.42s cubic-bezier(0.22,0.61,0.36,1);' + + 'will-change:opacity;contain:paint;'; + document.body.appendChild(veil); - sheet.offsetWidth; - callback(); + var cleaned = false; + function cleanup() { + if (cleaned) return; + cleaned = true; + if (veil.parentNode) veil.parentNode.removeChild(veil); + } - sheet.style.clipPath = isH ? 'inset(0 0 0 100%)' : 'inset(100% 0 0 0)'; + var applied = false; + function applyTheme() { + if (applied) return; + applied = true; + callback(); + veil.style.opacity = '0'; + } - sheet.addEventListener('transitionend', function() { - if (sheet.parentNode) sheet.parentNode.removeChild(sheet); + veil.offsetWidth; + veil.style.opacity = '1'; + + setTimeout(applyTheme, 300); + veil.addEventListener('transitionend', function(e) { + if (!applied || e.propertyName !== 'opacity') return; + cleanup(); }); + setTimeout(function() { + applyTheme(); + cleanup(); + }, 950); } // Toast Message Helper diff --git a/themes/Ying/layouts/_default/_markup/render-image.html b/themes/Ying/layouts/_default/_markup/render-image.html index 2f1d43f1..356e2ef7 100644 --- a/themes/Ying/layouts/_default/_markup/render-image.html +++ b/themes/Ying/layouts/_default/_markup/render-image.html @@ -14,6 +14,6 @@ {{- $src = $src | relURL -}} {{- end -}} {{- end -}} - - {{ $alt }} + + {{ $alt }} \ No newline at end of file diff --git a/themes/Ying/layouts/partials/artalk.html b/themes/Ying/layouts/partials/artalk.html index e7518a64..445309ad 100644 --- a/themes/Ying/layouts/partials/artalk.html +++ b/themes/Ying/layouts/partials/artalk.html @@ -1,5 +1,5 @@ - - +{{ $artalkCss := resources.Get "css/libs/Artalk.css" | resources.Minify | resources.Fingerprint }} + {{- end -}} diff --git a/themes/Ying/layouts/partials/heatmap.html b/themes/Ying/layouts/partials/heatmap.html index 658ef05e..864d04ca 100644 --- a/themes/Ying/layouts/partials/heatmap.html +++ b/themes/Ying/layouts/partials/heatmap.html @@ -179,11 +179,8 @@ // Initial render if (window.echarts) { renderHeatmap(); - } else { - // Retry a few times if script is still loading - setTimeout(renderHeatmap, 500); - setTimeout(renderHeatmap, 1500); - setTimeout(renderHeatmap, 3000); + } else if (window.optionalAssetUrls && window.optionalAssetUrls.echarts && window.loadScriptOnce) { + window.loadScriptOnce(window.optionalAssetUrls.echarts, 'echarts', renderHeatmap); } // Re-render on theme change