fixed 图片懒加载bug
This commit is contained in:
1 parent
819b6d6e85
commit
f8730d09e6
6 files changed
+214
-83
No files matched your search
@@ -44,6 +44,8 @@ ai_comment: >-
|
|||||||
|
|
||||||
* **蕉太狼温馨提示**:在首页右下角加了一个小小的“蕉太狼”挂件。它会在早晨问好,深夜催睡。虽然只是简单的几行代码逻辑,但让博客多了一点“人情味”。
|
* **蕉太狼温馨提示**:在首页右下角加了一个小小的“蕉太狼”挂件。它会在早晨问好,深夜催睡。虽然只是简单的几行代码逻辑,但让博客多了一点“人情味”。
|
||||||
|
|
||||||
|
* **丝滑的图片加载**:给文章里的图片加了个“懒加载”动画。以前图片是“刷”的一下出来的,现在是当你滑到它的时候,它会优雅地从下往上浮现出来,那种顺滑的感觉,就像是德芙巧克力一样(夸张了哈),反正我是盯着看了好几遍。
|
||||||
|
|
||||||
## 效果展示
|
## 效果展示
|
||||||
|
|
||||||
旧样式
|
旧样式
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
})();
|
})();
|
||||||
@@ -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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 激活文章图片灯箱
|
// 激活文章图片灯箱
|
||||||
|
|||||||
@@ -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);
|
||||||
}, 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.
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user