From f8730d09e6f4770b74ae044af8a0b7d0fb576360 Mon Sep 17 00:00:00 2001 From: Vaica <94612053+zqlit@users.noreply.github.com> Date: Mon, 2 Feb 2026 20:38:58 +0800 Subject: [PATCH] =?UTF-8?q?fixed=20=E5=9B=BE=E7=89=87=E6=87=92=E5=8A=A0?= =?UTF-8?q?=E8=BD=BDbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026/2026-02-03-blog-refurbish/index.md | 2 + themes/Ying/assets/css/main.css | 52 +++++-- themes/Ying/assets/js/main.js | 7 +- themes/Ying/assets/js/modules/lazyload.js | 139 ++++++++++++------ themes/Ying/assets/js/modules/mypjax.js | 7 +- themes/Ying/assets/js/modules/search.js | 94 +++++++++--- 6 files changed, 216 insertions(+), 85 deletions(-) diff --git a/content/post/2026/2026-02-03-blog-refurbish/index.md b/content/post/2026/2026-02-03-blog-refurbish/index.md index e8758db4..e873fba3 100644 --- a/content/post/2026/2026-02-03-blog-refurbish/index.md +++ b/content/post/2026/2026-02-03-blog-refurbish/index.md @@ -44,6 +44,8 @@ ai_comment: >- * **蕉太狼温馨提示**:在首页右下角加了一个小小的“蕉太狼”挂件。它会在早晨问好,深夜催睡。虽然只是简单的几行代码逻辑,但让博客多了一点“人情味”。 +* **丝滑的图片加载**:给文章里的图片加了个“懒加载”动画。以前图片是“刷”的一下出来的,现在是当你滑到它的时候,它会优雅地从下往上浮现出来,那种顺滑的感觉,就像是德芙巧克力一样(夸张了哈),反正我是盯着看了好几遍。 + ## 效果展示 旧样式 diff --git a/themes/Ying/assets/css/main.css b/themes/Ying/assets/css/main.css index b6a9ed5f..9744de18 100644 --- a/themes/Ying/assets/css/main.css +++ b/themes/Ying/assets/css/main.css @@ -689,36 +689,49 @@ ul { } .header-info { display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; /* Changed to flex-start to align with text */ + flex-direction: column; /* Stack vertically */ + align-content: flex-end; /* Push to bottom */ + justify-content: flex-end; /* Push to bottom */ + align-items: center; /* Center align horizontally */ margin-left: 10px; height: 80px; - width: calc(100vw - 140px); /* Ensure container takes available space */ + width: calc(100vw - 140px); + padding-bottom: 0px; } .header-social { display: flex; + align-items: center; flex-wrap: nowrap; - gap: 10px; - margin-bottom: 5px; - width: 100%; /* Take full width to allow centering */ - justify-content: center; /* Center icons horizontally */ + gap: 12px; /* Increased gap for better spacing */ + margin-bottom: 2px; /* Add small space between rows */ + margin-right: 0; + width: 100%; + justify-content: center; /* Center align icons */ + height: 24px; + } + + .header-social a { + margin: 0 !important; + display: flex; + align-items: center; } .header-bio { display: block; font-size: 0.85rem; + line-height: 24px; + min-height: 24px; margin: 0; opacity: 0.8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - width: 100%; /* Take full width */ - text-align: center; /* Center the text */ + width: 100%; /* Full width */ + text-align: center; /* Center align text */ /* Typewriter Effect Cursor */ - border-right: 2px solid transparent; /* Hidden by default, toggled by JS if needed, or use ::after */ + border-right: 2px solid transparent; } /* Cursor Animation Class */ @@ -743,7 +756,7 @@ ul { margin-bottom: 0; text-align: center; /* Reset gap for social icons as they rely on margin */ - gap: 0; + gap: 2; } .header-social a { @@ -4687,6 +4700,21 @@ html[data-theme="light"] .atk-loading-spinner svg circle { border-color: #333; } +/* Lazy Load Animation */ +.lazyload { + opacity: 0; + transform: translateY(30px); /* 稍微增加位移距离,增强上浮感 */ + filter: blur(5px); /* 减少模糊,避免过于影响性能 */ + transition: opacity 0.8s ease-out, transform 0.8s cubic-bezier(0.215, 0.61, 0.355, 1), filter 0.8s ease-out; /* 使用 ease-out 和自定义贝塞尔曲线,模拟自然惯性 */ + will-change: opacity, transform; /* 移除 filter 的 will-change 以节省内存 */ +} + +.lazyload.lazy-loaded { + opacity: 1; + transform: translateY(0); + filter: blur(0); +} + [data-theme="dark"] .ai-comment-card .atk-content p { color: var(--text-content-dark-color); } diff --git a/themes/Ying/assets/js/main.js b/themes/Ying/assets/js/main.js index 78ab5252..5c69ee16 100644 --- a/themes/Ying/assets/js/main.js +++ b/themes/Ying/assets/js/main.js @@ -8,9 +8,10 @@ document.addEventListener('DOMContentLoaded', function() { // 文章图片懒加载 (优先初始化,防止其他模块调用时出错) if (typeof lazyLoadInit === 'function') { lazyLoadInit({ - offsetBottom: 800, - offsetTopm: 800, - showTime: 1100 + offsetBottom: 50, + offsetTopm: 50, + showTime: 1100, + queueInterval: 100 }); } else { console.warn('lazyLoadInit is not defined'); diff --git a/themes/Ying/assets/js/modules/lazyload.js b/themes/Ying/assets/js/modules/lazyload.js index 1a23f555..6f1e9984 100644 --- a/themes/Ying/assets/js/modules/lazyload.js +++ b/themes/Ying/assets/js/modules/lazyload.js @@ -1,96 +1,143 @@ (function() { + // Default configuration var defaults = { - showTime: 800, - offsetBottom: 0, - offsetTopm: 50, - onLoadBackEnd: function (index, dom) { }, - onLoadBackStart: function (index, dom) { } + offsetBottom: 100, // Distance from bottom to start loading + showTime: 1100, // Transition duration (ms) + queueInterval: 200 // Time between showing successive images in queue }; var observer; + var imageQueue = []; // Queue for images waiting to be shown + var isProcessingQueue = false; function init(setting) { + // Merge settings defaults = Object.assign({}, defaults, setting || {}); - // Reset observer for PJAX support + // Reset queue + imageQueue = []; + isProcessingQueue = false; + + // Disconnect previous observer if exists (for PJAX) if (observer) { observer.disconnect(); observer = null; } + // Check for IntersectionObserver support + if (!window.IntersectionObserver) { + loadAllImages(); + return; + } + // Initialize IntersectionObserver observer = new IntersectionObserver(function(entries, self) { entries.forEach(function(entry) { if (entry.isIntersecting) { var img = entry.target; - showImg(img); + + // Add to queue instead of loading immediately + addToQueue(img); + + // Stop observing this image once it's triggered self.unobserve(img); } }); }, { - rootMargin: defaults.offsetTopm + "px 0px " + defaults.offsetBottom + "px 0px" + rootMargin: "0px 0px " + defaults.offsetBottom + "px 0px", + threshold: 0.01 }); - processImages(); + observeImages(); } - function processImages() { - if (!observer) { - init(); // Auto-initialize if not ready + function addToQueue(img) { + // Avoid duplicates + if (!imageQueue.includes(img)) { + imageQueue.push(img); + processQueue(); } - // Expanded selector to catch more images - var images = document.querySelectorAll('img.lazyload, img:not(.blog-img):not([data-comp])'); + } + + function processQueue() { + if (isProcessingQueue) return; + if (imageQueue.length === 0) return; + + isProcessingQueue = true; + + var img = imageQueue.shift(); + loadImage(img, function() { + // After this image starts showing (or fails), process next after delay + setTimeout(function() { + isProcessingQueue = false; + processQueue(); + }, defaults.queueInterval); + }); + } + + function observeImages() { + var images = document.querySelectorAll('img.lazyload, .post-content img:not(.lazy-loaded)'); + images.forEach(function(img) { - // Check if already processed or has data-lazy-src + if (img.classList.contains('lazy-loaded')) return; + + // Update transition duration based on settings + if (defaults.showTime) { + // We'll set this via inline style or rely on CSS class if it matches default + // But for full control, let's set it + img.style.transitionDuration = (defaults.showTime / 1000) + 's'; + } + if (img.getAttribute('data-lazy-src')) { - // Already setup for lazy load, just observe - observer.observe(img); - } else if (img.getAttribute('src')) { - // Convert to lazy load - var src = img.getAttribute('src'); - img.setAttribute('data-lazy-src', src); - img.setAttribute('src', 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='); observer.observe(img); } }); } - function showImg(dom) { - if (dom.getAttribute('data-comp')) return; - dom.setAttribute('data-comp', 'true'); - - var src = dom.getAttribute('data-lazy-src'); - if (!src) return; + function loadImage(img, callback) { + var src = img.getAttribute('data-lazy-src'); + if (!src) { + if (callback) callback(); + return; + } - // Create a temporary image to preload var temp = new Image(); - temp.onload = function() { - // Prepare for transition - dom.style.opacity = 0; - dom.style.transition = "opacity " + (defaults.showTime / 1000) + "s ease-in-out"; - + + var onComplete = function() { // Swap source - dom.setAttribute('src', src); + img.src = src; - // Force reflow - void dom.offsetWidth; - - // Fade in - dom.style.opacity = 1; + requestAnimationFrame(() => { + requestAnimationFrame(() => { + img.classList.add('lazy-loaded'); + img.removeAttribute('data-lazy-src'); + // Execute callback immediately after starting animation logic + if (callback) callback(); + }); + }); }; + + temp.onload = onComplete; temp.onerror = function() { - // On error, show anyway - dom.setAttribute('src', src); - dom.style.opacity = 1; + // Fallback + img.src = src; + requestAnimationFrame(() => { + img.classList.add('lazy-loaded'); + if (callback) callback(); + }); }; + temp.src = src; } - function tryAgain() { - processImages(); + function loadAllImages() { + var images = document.querySelectorAll('img.lazyload'); + images.forEach(function(img) { + loadImage(img); + }); } window.lazyLoadInit = init; - window.lazyLoadAgain = tryAgain; + window.lazyLoadRefresh = observeImages; })(); diff --git a/themes/Ying/assets/js/modules/mypjax.js b/themes/Ying/assets/js/modules/mypjax.js index 3913d707..f41a174a 100644 --- a/themes/Ying/assets/js/modules/mypjax.js +++ b/themes/Ying/assets/js/modules/mypjax.js @@ -67,9 +67,10 @@ document.addEventListener("pjax:success", function () { lazyLoadInit({ // coverColor: "white", // coverDiv: "
优世界·一个记录生活点滴的小站
", - offsetBottom: 800, - offsetTopm: 800, - showTime: 1100 + offsetBottom: 50, + offsetTopm: 50, + showTime: 1100, + queueInterval: 100 }); } // 激活文章图片灯箱 diff --git a/themes/Ying/assets/js/modules/search.js b/themes/Ying/assets/js/modules/search.js index e266fa05..1c213b42 100644 --- a/themes/Ying/assets/js/modules/search.js +++ b/themes/Ying/assets/js/modules/search.js @@ -348,34 +348,86 @@ function initInlineSearch() { } } - // Reset text content initially but keep it ready - // Pre-fill first character to avoid empty bubble on mobile (where CSS forces visibility) - if (text.length > 0) { - searchHintText.textContent = text.charAt(0); - } else { - searchHintText.textContent = ''; - } - - // Show wrapper initially - setTimeout(function() { - // Add visible class (triggers fade in on desktop) + var typeInterval; + var hideTimeout; + + function runTypewriter(str, callback) { + if (typeInterval) clearInterval(typeInterval); + if (hideTimeout) clearTimeout(hideTimeout); + + // Ensure visible searchHintWrapper.classList.add('visible'); - // Typewriter effect var i = 1; - var typeInterval = setInterval(function() { - if (i >= text.length) { + // Pre-fill first char + if (str.length > 0) { + searchHintText.textContent = str.charAt(0); + } + + typeInterval = setInterval(function() { + if (i >= str.length) { clearInterval(typeInterval); - - // Hide after 8 seconds (give more time to read greetings) - setTimeout(function() { - searchHintWrapper.classList.remove('visible'); - }, 8000); + if (callback) callback(); return; } - searchHintText.textContent = text.substring(0, i + 1); + searchHintText.textContent = str.substring(0, i + 1); i++; }, 100); - }, 500); + } + + // Check session storage for Jiaotailang tip + if (!sessionStorage.getItem('jiaotailang_shown')) { + // Initial Delay + setTimeout(function() { + // Step 1: Show Greeting + runTypewriter(text, function() { + // Animation Finished + + // Step 2: Check Mobile Logic + if (window.isMobile && window.isMobile()) { + // Wait 2s then show secret hint + hideTimeout = setTimeout(function() { + var secretHint = "悄悄咪咪告诉你 点击随笔开始搜索"; + runTypewriter(secretHint, function() { + // Finished secret hint + sessionStorage.setItem('jiaotailang_shown', 'true'); + // Hide after 5s + hideTimeout = setTimeout(function() { + searchHintWrapper.classList.remove('visible'); + }, 5000); + }); + }, 2000); + } else { + // Desktop: Just hide after 8s + sessionStorage.setItem('jiaotailang_shown', 'true'); + hideTimeout = setTimeout(function() { + searchHintWrapper.classList.remove('visible'); + }, 8000); + } + }); + }, 500); + } else { + // If already shown, set full text but don't auto-show + searchHintText.textContent = text; + } + + // --- Hover Logic (Desktop) --- + if (!window.isMobile || !window.isMobile()) { + header.addEventListener('mouseenter', function() { + // Stop any running animations + if (typeInterval) clearInterval(typeInterval); + if (hideTimeout) clearTimeout(hideTimeout); + + searchHintWrapper.classList.add('visible'); + searchHintText.textContent = "点击随笔,开始搜索"; + }); + + header.addEventListener('mouseleave', function() { + // Simply hide when leaving + searchHintWrapper.classList.remove('visible'); + // Optional: Reset text to greeting in case it's shown again by some other logic? + // But mostly it will be triggered by hover again. + }); + } } }