mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat: enhance supporters modal with auto-scrolling and visual improvements
- Add auto-scrolling functionality to supporters list with user interaction controls (pause on hover, manual scroll) - Implement gradient overlays at top/bottom for credits-like appearance - Style custom scrollbar with subtle hover effects for better UX - Adjust padding and positioning to ensure all supporters remain visible during scroll
This commit is contained in:
@@ -378,6 +378,29 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
position: relative; /* Base for masks */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optional: Fading effect for credits feel at top and bottom */
|
||||||
|
.all-supporters-group::before,
|
||||||
|
.all-supporters-group::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 40px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.all-supporters-group::before {
|
||||||
|
top: 30px; /* Below the title */
|
||||||
|
background: linear-gradient(to bottom, var(--lora-surface), transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.all-supporters-group::after {
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(to top, var(--lora-surface), transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.all-supporters-group .supporters-group-title {
|
.all-supporters-group .supporters-group-title {
|
||||||
@@ -391,8 +414,27 @@
|
|||||||
line-height: 2.2;
|
line-height: 2.2;
|
||||||
max-height: 550px;
|
max-height: 550px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: var(--space-2) 0;
|
padding: var(--space-2) 0 40px 0; /* Extra padding at bottom for final visibility */
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
|
scroll-behavior: auto; /* Ensure manual scroll is immediate */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtle scrollbar for credits look */
|
||||||
|
.supporters-all-list::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.supporters-all-list::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.supporters-all-list::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.supporters-all-list:hover::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.supporter-name-item {
|
.supporter-name-item {
|
||||||
|
|||||||
@@ -60,6 +60,110 @@ export function clearSupportersCache() {
|
|||||||
supportersData = null;
|
supportersData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let autoScrollRequest = null;
|
||||||
|
let autoScrollTimeout = null;
|
||||||
|
let isUserInteracting = false;
|
||||||
|
let isHovering = false;
|
||||||
|
let currentScrollPos = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle user interaction to stop auto-scroll
|
||||||
|
*/
|
||||||
|
function handleInteraction() {
|
||||||
|
isUserInteracting = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle mouse enter to pause auto-scroll
|
||||||
|
*/
|
||||||
|
function handleMouseEnter() {
|
||||||
|
isHovering = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle mouse leave to resume auto-scroll
|
||||||
|
*/
|
||||||
|
function handleMouseLeave() {
|
||||||
|
isHovering = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize auto-scrolling for the supporters list like movie credits
|
||||||
|
* @param {HTMLElement} container The scrollable container
|
||||||
|
*/
|
||||||
|
function initAutoScroll(container) {
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
// Stop any existing animation and clear any pending timeout
|
||||||
|
if (autoScrollRequest) {
|
||||||
|
cancelAnimationFrame(autoScrollRequest);
|
||||||
|
autoScrollRequest = null;
|
||||||
|
}
|
||||||
|
if (autoScrollTimeout) {
|
||||||
|
clearTimeout(autoScrollTimeout);
|
||||||
|
autoScrollTimeout = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset state for new scroll
|
||||||
|
isUserInteracting = false;
|
||||||
|
isHovering = false;
|
||||||
|
container.scrollTop = 0;
|
||||||
|
currentScrollPos = 0;
|
||||||
|
|
||||||
|
const scrollSpeed = 0.4; // Pixels per frame (~24px/sec at 60fps)
|
||||||
|
|
||||||
|
const step = () => {
|
||||||
|
// Stop animation if container is hidden or no longer in DOM
|
||||||
|
if (!container.offsetParent) {
|
||||||
|
autoScrollRequest = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isHovering && !isUserInteracting) {
|
||||||
|
const prevScrollTop = container.scrollTop;
|
||||||
|
currentScrollPos += scrollSpeed;
|
||||||
|
container.scrollTop = currentScrollPos;
|
||||||
|
|
||||||
|
// Check if we reached the bottom
|
||||||
|
if (container.scrollTop === prevScrollTop && currentScrollPos > 1) {
|
||||||
|
const isAtBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 1;
|
||||||
|
if (isAtBottom) {
|
||||||
|
autoScrollRequest = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Keep currentScrollPos in sync if user scrolls manually or pauses
|
||||||
|
currentScrollPos = container.scrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
autoScrollRequest = requestAnimationFrame(step);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove existing listeners before adding to avoid duplicates
|
||||||
|
container.removeEventListener('mouseenter', handleMouseEnter);
|
||||||
|
container.removeEventListener('mouseleave', handleMouseLeave);
|
||||||
|
container.removeEventListener('wheel', handleInteraction);
|
||||||
|
container.removeEventListener('touchstart', handleInteraction);
|
||||||
|
container.removeEventListener('mousedown', handleInteraction);
|
||||||
|
|
||||||
|
// Event listeners to handle user control
|
||||||
|
container.addEventListener('mouseenter', handleMouseEnter);
|
||||||
|
container.addEventListener('mouseleave', handleMouseLeave);
|
||||||
|
|
||||||
|
// Use { passive: true } for better scroll performance
|
||||||
|
container.addEventListener('wheel', handleInteraction, { passive: true });
|
||||||
|
container.addEventListener('touchstart', handleInteraction, { passive: true });
|
||||||
|
container.addEventListener('mousedown', handleInteraction);
|
||||||
|
|
||||||
|
// Initial delay before starting the credits-style scroll
|
||||||
|
autoScrollTimeout = setTimeout(() => {
|
||||||
|
if (container.scrollHeight > container.clientHeight) {
|
||||||
|
autoScrollRequest = requestAnimationFrame(step);
|
||||||
|
}
|
||||||
|
}, 1800);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render supporters in the support modal
|
* Render supporters in the support modal
|
||||||
*/
|
*/
|
||||||
@@ -69,9 +173,7 @@ export async function renderSupporters() {
|
|||||||
// Update subtitle with total count
|
// Update subtitle with total count
|
||||||
const subtitleEl = document.getElementById('supportersSubtitle');
|
const subtitleEl = document.getElementById('supportersSubtitle');
|
||||||
if (subtitleEl) {
|
if (subtitleEl) {
|
||||||
// Get the translation key and replace count
|
|
||||||
const originalText = subtitleEl.textContent;
|
const originalText = subtitleEl.textContent;
|
||||||
// Replace the count in the text (simple approach)
|
|
||||||
subtitleEl.textContent = originalText.replace(/\d+/, supporters.totalCount);
|
subtitleEl.textContent = originalText.replace(/\d+/, supporters.totalCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,5 +202,8 @@ export async function renderSupporters() {
|
|||||||
`;
|
`;
|
||||||
})
|
})
|
||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
|
// Initialize the auto-scroll effect
|
||||||
|
initAutoScroll(supportersGrid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user