fixed 图片懒加载bug

This commit is contained in:
Vaica committed 2026-02-02 20:38:58 +08:00
1 parent 819b6d6e85
commit f8730d09e6
6 files changed
+213 -82

No files matched your search

@@ -44,6 +44,8 @@ ai_comment: >-
* **蕉太狼温馨提示**:在首页右下角加了一个小小的“蕉太狼”挂件。它会在早晨问好,深夜催睡。虽然只是简单的几行代码逻辑,但让博客多了一点“人情味”。 * **蕉太狼温馨提示**:在首页右下角加了一个小小的“蕉太狼”挂件。它会在早晨问好,深夜催睡。虽然只是简单的几行代码逻辑,但让博客多了一点“人情味”。
* **丝滑的图片加载**:给文章里的图片加了个“懒加载”动画。以前图片是“刷”的一下出来的,现在是当你滑到它的时候,它会优雅地从下往上浮现出来,那种顺滑的感觉,就像是德芙巧克力一样(夸张了哈),反正我是盯着看了好几遍。
## 效果展示 ## 效果展示
旧样式 旧样式
+40 -12
View File
@@ -689,36 +689,49 @@ ul {
} }
.header-info { .header-info {
display: flex; display: flex;
flex-direction: column; flex-direction: column; /* Stack vertically */
justify-content: center; align-content: flex-end; /* Push to bottom */
align-items: flex-start; /* Changed to flex-start to align with text */ justify-content: flex-end; /* Push to bottom */
align-items: center; /* Center align horizontally */
margin-left: 10px; margin-left: 10px;
height: 80px; height: 80px;
width: calc(100vw - 140px); /* Ensure container takes available space */ width: calc(100vw - 140px);
padding-bottom: 0px;
} }
.header-social { .header-social {
display: flex; display: flex;
align-items: center;
flex-wrap: nowrap; flex-wrap: nowrap;
gap: 10px; gap: 12px; /* Increased gap for better spacing */
margin-bottom: 5px; margin-bottom: 2px; /* Add small space between rows */
width: 100%; /* Take full width to allow centering */ margin-right: 0;
justify-content: center; /* Center icons horizontally */ width: 100%;
justify-content: center; /* Center align icons */
height: 24px;
}
.header-social a {
margin: 0 !important;
display: flex;
align-items: center;
} }
.header-bio { .header-bio {
display: block; display: block;
font-size: 0.85rem; font-size: 0.85rem;
line-height: 24px;
min-height: 24px;
margin: 0; margin: 0;
opacity: 0.8; opacity: 0.8;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
width: 100%; /* Take full width */ width: 100%; /* Full width */
text-align: center; /* Center the text */ text-align: center; /* Center align text */
/* Typewriter Effect Cursor */ /* 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 */ /* Cursor Animation Class */
@@ -743,7 +756,7 @@ ul {
margin-bottom: 0; margin-bottom: 0;
text-align: center; text-align: center;
/* Reset gap for social icons as they rely on margin */ /* Reset gap for social icons as they rely on margin */
gap: 0; gap: 2;
} }
.header-social a { .header-social a {
@@ -4687,6 +4700,21 @@ html[data-theme="light"] .atk-loading-spinner svg circle {
border-color: #333; 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 { [data-theme="dark"] .ai-comment-card .atk-content p {
color: var(--text-content-dark-color); color: var(--text-content-dark-color);
} }
+4 -3
View File
@@ -8,9 +8,10 @@ document.addEventListener('DOMContentLoaded', function() {
// 文章图片懒加载 (优先初始化,防止其他模块调用时出错) // 文章图片懒加载 (优先初始化,防止其他模块调用时出错)
if (typeof lazyLoadInit === 'function') { if (typeof lazyLoadInit === 'function') {
lazyLoadInit({ lazyLoadInit({
offsetBottom: 800, offsetBottom: 50,
offsetTopm: 800, offsetTopm: 50,
showTime: 1100 showTime: 1100,
queueInterval: 100
}); });
} else { } else {
console.warn('lazyLoadInit is not defined'); console.warn('lazyLoadInit is not defined');
+92 -45
View File
@@ -1,96 +1,143 @@
(function() { (function() {
// Default configuration
var defaults = { var defaults = {
showTime: 800, offsetBottom: 100, // Distance from bottom to start loading
offsetBottom: 0, showTime: 1100, // Transition duration (ms)
offsetTopm: 50, queueInterval: 200 // Time between showing successive images in queue
onLoadBackEnd: function (index, dom) { },
onLoadBackStart: function (index, dom) { }
}; };
var observer; var observer;
var imageQueue = []; // Queue for images waiting to be shown
var isProcessingQueue = false;
function init(setting) { function init(setting) {
// Merge settings
defaults = Object.assign({}, defaults, setting || {}); defaults = Object.assign({}, defaults, setting || {});
// Reset observer for PJAX support // Reset queue
imageQueue = [];
isProcessingQueue = false;
// Disconnect previous observer if exists (for PJAX)
if (observer) { if (observer) {
observer.disconnect(); observer.disconnect();
observer = null; observer = null;
} }
// Check for IntersectionObserver support
if (!window.IntersectionObserver) {
loadAllImages();
return;
}
// Initialize IntersectionObserver // Initialize IntersectionObserver
observer = new IntersectionObserver(function(entries, self) { observer = new IntersectionObserver(function(entries, self) {
entries.forEach(function(entry) { entries.forEach(function(entry) {
if (entry.isIntersecting) { if (entry.isIntersecting) {
var img = entry.target; 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); 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() { function addToQueue(img) {
if (!observer) { // Avoid duplicates
init(); // Auto-initialize if not ready 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) { 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')) { 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); observer.observe(img);
} }
}); });
} }
function showImg(dom) { function loadImage(img, callback) {
if (dom.getAttribute('data-comp')) return; var src = img.getAttribute('data-lazy-src');
dom.setAttribute('data-comp', 'true'); if (!src) {
if (callback) callback();
return;
}
var src = dom.getAttribute('data-lazy-src');
if (!src) return;
// Create a temporary image to preload
var temp = new Image(); 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 // Swap source
dom.setAttribute('src', src); img.src = src;
// Force reflow requestAnimationFrame(() => {
void dom.offsetWidth; requestAnimationFrame(() => {
img.classList.add('lazy-loaded');
// Fade in img.removeAttribute('data-lazy-src');
dom.style.opacity = 1; // Execute callback immediately after starting animation logic
if (callback) callback();
});
});
}; };
temp.onload = onComplete;
temp.onerror = function() { temp.onerror = function() {
// On error, show anyway // Fallback
dom.setAttribute('src', src); img.src = src;
dom.style.opacity = 1; requestAnimationFrame(() => {
img.classList.add('lazy-loaded');
if (callback) callback();
});
}; };
temp.src = src; temp.src = src;
} }
function tryAgain() { function loadAllImages() {
processImages(); var images = document.querySelectorAll('img.lazyload');
images.forEach(function(img) {
loadImage(img);
});
} }
window.lazyLoadInit = init; window.lazyLoadInit = init;
window.lazyLoadAgain = tryAgain; window.lazyLoadRefresh = observeImages;
})(); })();
+4 -3
View File
@@ -67,9 +67,10 @@ document.addEventListener("pjax:success", function () {
lazyLoadInit({ lazyLoadInit({
// coverColor: "white", // coverColor: "white",
// coverDiv: "<div style='display: flex; justify-content: center; align-items: center; height: 200px; color: #000000;'><span>优世界·一个记录生活点滴的小站</span></div>", // coverDiv: "<div style='display: flex; justify-content: center; align-items: center; height: 200px; color: #000000;'><span>优世界·一个记录生活点滴的小站</span></div>",
offsetBottom: 800, offsetBottom: 50,
offsetTopm: 800, offsetTopm: 50,
showTime: 1100 showTime: 1100,
queueInterval: 100
}); });
} }
// 激活文章图片灯箱 // 激活文章图片灯箱
+71 -19
View File
@@ -348,34 +348,86 @@ function initInlineSearch() {
} }
} }
// Reset text content initially but keep it ready var typeInterval;
// Pre-fill first character to avoid empty bubble on mobile (where CSS forces visibility) var hideTimeout;
if (text.length > 0) {
searchHintText.textContent = text.charAt(0);
} else {
searchHintText.textContent = '';
}
// Show wrapper initially function runTypewriter(str, callback) {
setTimeout(function() { if (typeInterval) clearInterval(typeInterval);
// Add visible class (triggers fade in on desktop) if (hideTimeout) clearTimeout(hideTimeout);
// Ensure visible
searchHintWrapper.classList.add('visible'); searchHintWrapper.classList.add('visible');
// Typewriter effect
var i = 1; var i = 1;
var typeInterval = setInterval(function() { // Pre-fill first char
if (i >= text.length) { if (str.length > 0) {
clearInterval(typeInterval); searchHintText.textContent = str.charAt(0);
}
// Hide after 8 seconds (give more time to read greetings) typeInterval = setInterval(function() {
setTimeout(function() { if (i >= str.length) {
searchHintWrapper.classList.remove('visible'); clearInterval(typeInterval);
}, 8000); if (callback) callback();
return; return;
} }
searchHintText.textContent = text.substring(0, i + 1); searchHintText.textContent = str.substring(0, i + 1);
i++; i++;
}, 100); }, 100);
}
// 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); }, 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.
});
}
} }
} }