fixed 打赏卡片逻辑修复
This commit is contained in:
1 parent
1577beda42
commit
761d9024e7
4 files changed
+111
-64
No files matched your search
@@ -4776,7 +4776,7 @@ html[data-theme="light"] .atk-loading-spinner svg circle {
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: rgba(0,0,0,0.6);
|
/* background: rgba(0,0,0,0.6); */
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -33,41 +33,57 @@ window.initFloatingTools = function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Back to Top
|
// 1. Back to Top (Optimized with IntersectionObserver)
|
||||||
var backToTopButton = document.getElementById('back-to-top');
|
var backToTopButton = document.getElementById('back-to-top');
|
||||||
if (backToTopButton) {
|
if (backToTopButton) {
|
||||||
var ticking = false;
|
|
||||||
|
|
||||||
function update() {
|
// Clean up old scroll handler if exists (from previous Pjax or version)
|
||||||
var btn = document.getElementById('back-to-top');
|
if (window.backToTopScrollHandler) {
|
||||||
if (!btn) return;
|
window.removeEventListener('scroll', window.backToTopScrollHandler);
|
||||||
|
window.backToTopScrollHandler = null;
|
||||||
// Only show if NOT on homepage (redundant if container is hidden, but good for safety)
|
|
||||||
if (window.scrollY > 300) {
|
|
||||||
if (!btn.classList.contains('show')) {
|
|
||||||
btn.classList.add('show');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (btn.classList.contains('show')) {
|
|
||||||
btn.classList.remove('show');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ticking = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global Scroll Handler (Singleton)
|
// Create or reuse sentinel for IntersectionObserver
|
||||||
if (!window.backToTopScrollHandler) {
|
var sentinelId = 'back-to-top-sentinel';
|
||||||
|
var sentinel = document.getElementById(sentinelId);
|
||||||
|
if (!sentinel) {
|
||||||
|
sentinel = document.createElement('div');
|
||||||
|
sentinel.id = sentinelId;
|
||||||
|
// Sentinel covers the top 300px. When it exits viewport, we show button.
|
||||||
|
sentinel.style.cssText = 'position: absolute; top: 0; left: 0; width: 1px; height: 300px; pointer-events: none; visibility: hidden; z-index: -9999;';
|
||||||
|
document.body.appendChild(sentinel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.IntersectionObserver) {
|
||||||
|
var observer = 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);
|
||||||
|
} else {
|
||||||
|
// Fallback for very old browsers
|
||||||
|
var ticking = false;
|
||||||
|
var update = function() {
|
||||||
|
if (window.scrollY > 300) {
|
||||||
|
backToTopButton.classList.add('show');
|
||||||
|
} else {
|
||||||
|
backToTopButton.classList.remove('show');
|
||||||
|
}
|
||||||
|
ticking = false;
|
||||||
|
};
|
||||||
window.backToTopScrollHandler = function() {
|
window.backToTopScrollHandler = function() {
|
||||||
// Performance Optimization: Check scroll less frequently?
|
|
||||||
// No, rAF is best. But we can add a check if the button is even in DOM/Visible?
|
|
||||||
// But checking DOM layout is expensive.
|
|
||||||
// Keep rAF but ensure 'update' is light.
|
|
||||||
if (!ticking) {
|
if (!ticking) {
|
||||||
window.requestAnimationFrame(update);
|
window.requestAnimationFrame(update);
|
||||||
ticking = true;
|
ticking = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Use passive listener to prevent scroll blocking
|
|
||||||
window.addEventListener('scroll', window.backToTopScrollHandler, { passive: true });
|
window.addEventListener('scroll', window.backToTopScrollHandler, { passive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,9 +94,6 @@ window.initFloatingTools = function() {
|
|||||||
backToTopButton = newBtn;
|
backToTopButton = newBtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize state immediately
|
|
||||||
update();
|
|
||||||
|
|
||||||
backToTopButton.addEventListener('click', function(e) {
|
backToTopButton.addEventListener('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
window.scrollTo({
|
window.scrollTo({
|
||||||
|
|||||||
@@ -36,8 +36,8 @@
|
|||||||
if (entry.isIntersecting) {
|
if (entry.isIntersecting) {
|
||||||
var img = entry.target;
|
var img = entry.target;
|
||||||
|
|
||||||
// Add to queue instead of loading immediately
|
// Load immediately without queue
|
||||||
addToQueue(img);
|
loadImage(img);
|
||||||
|
|
||||||
// Stop observing this image once it's triggered
|
// Stop observing this image once it's triggered
|
||||||
self.unobserve(img);
|
self.unobserve(img);
|
||||||
@@ -51,29 +51,7 @@
|
|||||||
observeImages();
|
observeImages();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addToQueue(img) {
|
// Removed addToQueue and processQueue functions as we want instant loading
|
||||||
// Avoid duplicates
|
|
||||||
if (!imageQueue.includes(img)) {
|
|
||||||
imageQueue.push(img);
|
|
||||||
processQueue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
function observeImages() {
|
||||||
var images = document.querySelectorAll('img.lazyload, .post-content img:not(.lazy-loaded)');
|
var images = document.querySelectorAll('img.lazyload, .post-content img:not(.lazy-loaded)');
|
||||||
|
|||||||
@@ -9,6 +9,24 @@
|
|||||||
|
|
||||||
if (!btn || !modal) return;
|
if (!btn || !modal) return;
|
||||||
|
|
||||||
|
// Global Click Handler
|
||||||
|
// We attach this to document only when modal is open to handle "click anywhere outside content"
|
||||||
|
// This is more robust than attaching to the modal container, which might have size/z-index issues.
|
||||||
|
|
||||||
|
const handleGlobalClick = (e) => {
|
||||||
|
const activeModal = document.getElementById('reward-modal');
|
||||||
|
if (!activeModal || !activeModal.classList.contains('show')) return;
|
||||||
|
|
||||||
|
// If click is inside the content, ignore
|
||||||
|
if (e.target.closest('.reward-content')) return;
|
||||||
|
|
||||||
|
// If click is on the open button, ignore (it has its own handler, or we might double-toggle)
|
||||||
|
if (e.target.closest('#reward-btn')) return;
|
||||||
|
|
||||||
|
// Otherwise, close it
|
||||||
|
hideModal();
|
||||||
|
};
|
||||||
|
|
||||||
// Helper to show modal
|
// Helper to show modal
|
||||||
const showModal = (e) => {
|
const showModal = (e) => {
|
||||||
if(e) {
|
if(e) {
|
||||||
@@ -17,12 +35,18 @@
|
|||||||
}
|
}
|
||||||
modal.classList.add('show');
|
modal.classList.add('show');
|
||||||
document.body.style.overflow = 'hidden'; // Prevent scrolling
|
document.body.style.overflow = 'hidden'; // Prevent scrolling
|
||||||
|
|
||||||
|
// Attach global click listener with a slight delay to avoid current click triggering it
|
||||||
|
setTimeout(() => {
|
||||||
|
document.addEventListener('click', handleGlobalClick);
|
||||||
|
}, 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper to hide modal
|
// Helper to hide modal
|
||||||
const hideModal = () => {
|
const hideModal = () => {
|
||||||
modal.classList.remove('show');
|
modal.classList.remove('show');
|
||||||
document.body.style.overflow = '';
|
document.body.style.overflow = '';
|
||||||
|
document.removeEventListener('click', handleGlobalClick);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Button click
|
// Button click
|
||||||
@@ -30,27 +54,55 @@
|
|||||||
|
|
||||||
// Close button click
|
// Close button click
|
||||||
if (close) {
|
if (close) {
|
||||||
close.addEventListener('click', hideModal);
|
close.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation(); // Prevent bubbling to global handler (though redundant as handler checks)
|
||||||
|
hideModal();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click outside modal content
|
// Remove old direct listeners if any (cleaner approach replaces them)
|
||||||
modal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === modal) {
|
|
||||||
hideModal();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Escape key to close
|
// Escape key to close
|
||||||
if (!window.rewardKeyHandlerAttached) {
|
if (!window.rewardKeyHandlerAttached) {
|
||||||
document.addEventListener('keydown', function(e) {
|
window.rewardKeyHandler = function(e) {
|
||||||
const activeModal = document.getElementById('reward-modal');
|
const activeModal = document.getElementById('reward-modal');
|
||||||
if (e.key === 'Escape' && activeModal && activeModal.classList.contains('show')) {
|
if (e.key === 'Escape' && activeModal && activeModal.classList.contains('show')) {
|
||||||
activeModal.classList.remove('show');
|
hideModal(); // Use the same hide function to clean up listeners
|
||||||
document.body.style.overflow = '';
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
document.addEventListener('keydown', window.rewardKeyHandler);
|
||||||
window.rewardKeyHandlerAttached = true;
|
window.rewardKeyHandlerAttached = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expose hideModal for destroy function if needed?
|
||||||
|
// We can just rely on the fact that destroyReward queries DOM.
|
||||||
|
// But we need to make sure destroyReward removes the global listener too.
|
||||||
|
// Since handleGlobalClick is local scope, we need a way to reference it or just define it outside?
|
||||||
|
// Or better: make handleGlobalClick a named function available to destroyReward via a shared scope or window.
|
||||||
|
// Let's attach it to window for safety in this module pattern.
|
||||||
|
window.rewardGlobalClickHandler = handleGlobalClick;
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroyReward() {
|
||||||
|
// Cleanup keydown listener
|
||||||
|
if (window.rewardKeyHandlerAttached && window.rewardKeyHandler) {
|
||||||
|
document.removeEventListener('keydown', window.rewardKeyHandler);
|
||||||
|
window.rewardKeyHandlerAttached = false;
|
||||||
|
window.rewardKeyHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup global click listener
|
||||||
|
if (window.rewardGlobalClickHandler) {
|
||||||
|
document.removeEventListener('click', window.rewardGlobalClickHandler);
|
||||||
|
window.rewardGlobalClickHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide modal if open (restore body scroll)
|
||||||
|
const modal = document.getElementById('reward-modal');
|
||||||
|
if (modal && modal.classList.contains('show')) {
|
||||||
|
modal.classList.remove('show');
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize on load
|
// Initialize on load
|
||||||
@@ -59,6 +111,10 @@
|
|||||||
// Initialize on Pjax complete
|
// Initialize on Pjax complete
|
||||||
document.addEventListener('pjax:complete', initReward);
|
document.addEventListener('pjax:complete', initReward);
|
||||||
|
|
||||||
|
// Destroy on Pjax send
|
||||||
|
document.addEventListener('pjax:send', destroyReward);
|
||||||
|
|
||||||
// Export
|
// Export
|
||||||
window.initReward = initReward;
|
window.initReward = initReward;
|
||||||
|
window.destroyReward = destroyReward;
|
||||||
})();
|
})();
|
||||||
Reference in new issue
Block a user