diff --git a/themes/Ying/assets/css/main.css b/themes/Ying/assets/css/main.css index 0db1f704..3d72bd6f 100644 --- a/themes/Ying/assets/css/main.css +++ b/themes/Ying/assets/css/main.css @@ -45,32 +45,6 @@ html { scroll-behavior: smooth; } -/* ── Theme switch transition ── */ -::view-transition-old(root), -::view-transition-new(root) { - animation-duration: 0.45s; - animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} - -::view-transition-old(root) { - animation-name: theme-fade-out; -} - -::view-transition-new(root) { - animation-name: theme-fade-in; - animation-delay: 0.04s; -} - -@keyframes theme-fade-out { - from { opacity: 1; } - to { opacity: 0; } -} - -@keyframes theme-fade-in { - from { opacity: 0; } - to { opacity: 1; } -} - [data-theme="dark"] body { color: var(--text-content-dark-color); background-color: var(--background-dark-color); @@ -4533,6 +4507,8 @@ img[atk-emoticon] { /* --- Global Theme Transition --- */ body, .main, +body, +.main, .header, .footer, .post-content, diff --git a/themes/Ying/assets/js/modules/floating-tools.js b/themes/Ying/assets/js/modules/floating-tools.js index e6604022..06a10c81 100644 --- a/themes/Ying/assets/js/modules/floating-tools.js +++ b/themes/Ying/assets/js/modules/floating-tools.js @@ -123,20 +123,33 @@ window.initFloatingTools = function() { }; updateIcon(); - themeToggle.addEventListener('click', function() { + themeToggle.addEventListener('click', function(e) { var currentTheme = document.documentElement.getAttribute('data-theme'); var newTheme = currentTheme === 'dark' ? 'light' : 'dark'; - document.documentElement.setAttribute('data-theme', newTheme); - localStorage.setItem('theme', newTheme); - updateIcon(); - - // Trigger Artalk theme update - if (window.artalk && typeof window.artalk.setDarkMode === 'function') { - window.artalk.setDarkMode(newTheme === 'dark'); - } - if (window.showToast) { - window.showToast('已切换至 ' + (newTheme === 'dark' ? '深色' : '浅色') + '模式', 'success'); + if (typeof themeSweep === 'function') { + var apply = function() { + document.documentElement.setAttribute('data-theme', newTheme); + localStorage.setItem('theme', newTheme); + updateIcon(); + if (window.artalk && typeof window.artalk.setDarkMode === 'function') { + window.artalk.setDarkMode(newTheme === 'dark'); + } + if (window.showToast) { + window.showToast('已切换至 ' + (newTheme === 'dark' ? '深色' : '浅色') + '模式', 'success'); + } + }; + themeSweep(newTheme, apply); + } else { + document.documentElement.setAttribute('data-theme', newTheme); + localStorage.setItem('theme', newTheme); + updateIcon(); + if (window.artalk && typeof window.artalk.setDarkMode === 'function') { + window.artalk.setDarkMode(newTheme === 'dark'); + } + if (window.showToast) { + window.showToast('已切换至 ' + (newTheme === 'dark' ? '深色' : '浅色') + '模式', 'success'); + } } }); } diff --git a/themes/Ying/assets/js/modules/utils.js b/themes/Ying/assets/js/modules/utils.js index 17b96803..be88bb60 100644 --- a/themes/Ying/assets/js/modules/utils.js +++ b/themes/Ying/assets/js/modules/utils.js @@ -1,11 +1,50 @@ -// 判断是否为移动端 -window.isMobile = function() { - if (window.navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i)) { - return true; // 移动端 - } else { - return false; // PC端 - } -}; +// Scanner — page is blanked, beam paints the new theme back line by line +function themeSweep(newTheme, callback) { + var isH = Math.random() < 0.5; + var w = window.innerWidth, h = window.innerHeight; + + // Blank overlay: covers entire page with new-theme's dominant color + var blank = newTheme === 'dark' ? '#111827' : '#fcfaf5'; + var sheet = document.createElement('div'); + sheet.style.cssText = + 'position:fixed;inset:0;z-index:9999;' + + 'background:' + blank + ';' + + (isH + ? 'clip-path:inset(0 0 0 0);transition:clip-path 1.2s cubic-bezier(0.4,0,0.2,1);will-change:clip-path;' + : '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); + + // Thin scan beam at the leading edge + var beam = document.createElement('div'); + beam.style.cssText = + 'position:fixed;z-index:10000;pointer-events:none;' + + 'background:rgba(255,255,255,0.7);' + + 'box-shadow:0 0 4px 1px rgba(255,255,255,0.35),0 0 12px 2px rgba(255,255,255,0.12);' + + 'transition:transform 1.2s cubic-bezier(0.4,0,0.2,1);will-change:transform;' + + (isH + ? 'top:0;right:0;width:1px;height:100%;transform:translateX(0);' + : 'bottom:0;left:0;height:1px;width:100%;transform:translateY(0);'); + + document.body.appendChild(beam); + sheet.offsetWidth; + + // Switch theme immediately — sheet covers it, beam reveals + callback(); + + if (isH) { + sheet.style.clipPath = 'inset(0 0 0 100%)'; + beam.style.transform = 'translateX(' + (-w) + 'px)'; + } else { + sheet.style.clipPath = 'inset(100% 0 0 0)'; + beam.style.transform = 'translateY(' + (-h) + 'px)'; + } + + beam.addEventListener('transitionend', function(e) { + if (e.propertyName !== 'transform') return; + if (sheet.parentNode) sheet.parentNode.removeChild(sheet); + if (beam.parentNode) beam.parentNode.removeChild(beam); + }); +} // Toast Message Helper window.showToast = function(text, type) { @@ -21,13 +60,12 @@ window.showToast = function(text, type) { // Theme Toggle Logic window.initThemeToggle = function() { - const avatar = document.getElementById('theme-toggle-avatar'); + var avatar = document.getElementById('theme-toggle-avatar'); if (!avatar) return; - - // Remove existing listeners to avoid duplicates (if any) - const newAvatar = avatar.cloneNode(true); + + var newAvatar = avatar.cloneNode(true); avatar.parentNode.replaceChild(newAvatar, avatar); - + newAvatar.addEventListener('click', function(e) { e.preventDefault(); @@ -38,49 +76,34 @@ window.initThemeToggle = function() { var apply = function() { html.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); - window.showToast('已切换至 ' + (newTheme === 'dark' ? '深色' : '浅色') + '模式', 'success'); - if (window.artalk && typeof window.artalk.setDarkMode === 'function') { window.artalk.setDarkMode(newTheme === 'dark'); } - window.dispatchEvent(new Event('resize')); }; - var isMobile = window.isMobile && window.isMobile(); - - if (document.startViewTransition && !isMobile) { - document.startViewTransition(function() { apply(); }); - } else { - apply(); - } + themeSweep(newTheme, apply); }); }; // Code Folding Logic window.initCodeFold = function() { - const codeBlocks = document.querySelectorAll('.highlight'); - codeBlocks.forEach(block => { - // Check height (approx 300px) + var codeBlocks = document.querySelectorAll('.highlight'); + codeBlocks.forEach(function(block) { if (block.offsetHeight > 360) { - // Check if already processed if (block.parentNode.classList.contains('code-fold-container')) return; - - // Wrap - const wrapper = document.createElement('div'); + + var wrapper = document.createElement('div'); wrapper.className = 'code-fold-container collapsed'; block.parentNode.insertBefore(wrapper, block); wrapper.appendChild(block); - - // Add Button - const btn = document.createElement('button'); + + var btn = document.createElement('button'); btn.className = 'code-expand-btn'; btn.innerHTML = ' 展开代码 '; btn.onclick = function() { wrapper.classList.remove('collapsed'); - // Hide button after expand, or change to collapse? - // Usually just expand is enough. btn.style.display = 'none'; }; wrapper.appendChild(btn); @@ -90,62 +113,40 @@ window.initCodeFold = function() { // Code Copy Logic window.initCodeCopy = function() { - // Target pre tags that contain code, or .highlight containers - const codeBlocks = document.querySelectorAll('pre'); - - codeBlocks.forEach(pre => { - // Avoid double init + var codeBlocks = document.querySelectorAll('pre'); + + codeBlocks.forEach(function(pre) { if (pre.parentNode.querySelector('.copy-code-btn')) return; if (pre.querySelector('.copy-code-btn')) return; - - // Check if it's inside a highlight block (Hugo usually does .highlight > pre) - // We attach button to the pre or the highlight container? - // Attaching to pre is safer for positioning. - - const btn = document.createElement('button'); + + var btn = document.createElement('button'); btn.className = 'copy-code-btn'; btn.innerHTML = ''; btn.title = "复制代码"; - btn.style.cssText = ` - position: absolute; - top: -6px; - right: 5px; - background: transparent; - border: none; - color: #888; - cursor: pointer; - padding: 5px; - border-radius: 4px; - transition: all 0.2s; - opacity: 0; - z-index: 10; - `; - - // Show on hover - pre.addEventListener('mouseenter', () => btn.style.opacity = '1'); - pre.addEventListener('mouseleave', () => btn.style.opacity = '0'); - - // Handle touch devices - if (window.isMobile()) { - btn.style.opacity = '1'; - } + btn.style.cssText = + 'position:absolute;top:-6px;right:5px;background:transparent;border:none;' + + 'color:#888;cursor:pointer;padding:5px;border-radius:4px;' + + 'transition:all 0.2s;opacity:0;z-index:10;'; - btn.addEventListener('click', (e) => { + pre.addEventListener('mouseenter', function() { btn.style.opacity = '1'; }); + pre.addEventListener('mouseleave', function() { btn.style.opacity = '0'; }); + + if (window.isMobile()) { btn.style.opacity = '1'; } + + btn.addEventListener('click', function(e) { e.stopPropagation(); - const code = pre.querySelector('code') ? pre.querySelector('code').innerText : pre.innerText; - + var code = pre.querySelector('code') ? pre.querySelector('code').innerText : pre.innerText; + if (navigator.clipboard) { - navigator.clipboard.writeText(code).then(() => { + navigator.clipboard.writeText(code).then(function() { window.showToast('代码已复制 / Copied!', 'success'); btn.innerHTML = ''; - setTimeout(() => btn.innerHTML = '', 2000); - }).catch(err => { - console.error('Copy failed', err); + setTimeout(function() { btn.innerHTML = ''; }, 2000); + }).catch(function() { window.showToast('复制失败 / Failed', 'error'); }); } else { - // Fallback - const textarea = document.createElement('textarea'); + var textarea = document.createElement('textarea'); textarea.value = code; document.body.appendChild(textarea); textarea.select(); @@ -154,12 +155,11 @@ window.initCodeCopy = function() { window.showToast('代码已复制 / Copied!', 'success'); } }); - - // Ensure relative positioning + if (getComputedStyle(pre).position === 'static') { pre.style.position = 'relative'; } - + pre.appendChild(btn); }); };