checkpoint

This commit is contained in:
Will Miao
2025-02-19 12:14:12 +08:00
parent 2f3061ce7c
commit 1d0af7f163
3 changed files with 151 additions and 53 deletions

View File

@@ -29,6 +29,8 @@ body.modal-open {
border: 1px solid var(--lora-border);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
transform: translateZ(0); /* 创建新的堆叠上下文 */
max-height: 90vh; /* 限制最大高度为视窗高度的90% */
overflow-y: auto; /* 添加垂直滚动条 */
}
/* Delete Modal specific styles */
@@ -617,44 +619,66 @@ body.modal-open {
}
.carousel {
display: flex;
flex-direction: column; /* 改为垂直布局 */
gap: var(--space-3);
overflow-y: auto; /* 垂直滚动 */
transition: max-height 0.3s ease-in-out;
overflow: hidden;
}
.carousel img,
.carousel video {
.carousel.collapsed {
max-height: 0;
}
.carousel-container {
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.media-wrapper {
position: relative;
width: 100%;
height: auto;
max-height: 70vh; /* 限制单张图片最大高度 */
background: var(--lora-surface);
margin-bottom: var(--space-2);
}
.media-wrapper:last-child {
margin-bottom: 0;
}
.media-wrapper img,
.media-wrapper video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: contain;
border-radius: var(--border-radius-base);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* Scroll Indicator */
.scroll-indicator {
position: sticky;
top: 0;
left: 0;
right: 0;
text-align: center;
cursor: pointer;
padding: var(--space-2);
background: linear-gradient(to bottom,
var(--lora-surface) 0%,
transparent 100%);
opacity: 0.8;
transition: opacity 0.3s;
pointer-events: none;
z-index: 1; /* 确保滚动提示在图片上层 */
background: var(--lora-surface);
border: 1px solid var(--lora-border);
border-radius: var(--border-radius-sm);
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
margin-bottom: var(--space-2);
}
/* Remove horizontal scrolling styles */
.carousel {
scroll-snap-type: none;
grid-auto-flow: initial;
overflow-x: initial;
.scroll-indicator:hover {
background: oklch(var(--lora-accent) / 0.1);
}
.lazy {
opacity: 0;
transition: opacity 0.3s;
}
.lazy[src] {
opacity: 1;
}
/* Update Trigger Words styles */

View File

@@ -171,15 +171,7 @@ export function showLoraModal(lora) {
</div>
<div class="showcase-section">
<div class="scroll-indicator">
<i class="fas fa-chevron-down"></i>
Scroll for more examples
</div>
<div class="carousel">
${renderShowcaseImages(lora.civitai.images)}
</div>
</div>
${renderShowcaseImages(lora.civitai.images)}
</div>
</div>
`;
@@ -275,21 +267,101 @@ function renderTriggerWords(words) {
function renderShowcaseImages(images) {
if (!images?.length) return '';
return images.map(img => {
if (img.type === 'video') {
return `
<video controls autoplay muted loop crossorigin="anonymous" referrerpolicy="no-referrer">
<source src="${img.url}" type="video/mp4">
Your browser does not support video playback
</video>
`;
return `
<div class="showcase-section">
<div class="scroll-indicator" onclick="toggleShowcase(this)">
<i class="fas fa-chevron-down"></i>
<span>Show ${images.length} examples</span>
</div>
<div class="carousel collapsed">
<div class="carousel-container">
${images.map(img => {
// 计算适当的展示高度:
// 1. 保持原始宽高比
// 2. 限制最大高度为视窗高度的60%
// 3. 确保最小高度为容器宽度的40%
const aspectRatio = (img.height / img.width) * 100;
const containerWidth = 800; // modal content的最大宽度
const minHeightPercent = 40; // 最小高度为容器宽度的40%
const maxHeightPercent = (window.innerHeight * 0.6 / containerWidth) * 100;
const heightPercent = Math.max(
minHeightPercent,
Math.min(maxHeightPercent, aspectRatio)
);
if (img.type === 'video') {
return `
<div class="media-wrapper" style="padding-bottom: ${heightPercent}%">
<video controls autoplay muted loop crossorigin="anonymous"
referrerpolicy="no-referrer" data-src="${img.url}"
class="lazy">
<source data-src="${img.url}" type="video/mp4">
Your browser does not support video playback
</video>
</div>
`;
}
return `
<div class="media-wrapper" style="padding-bottom: ${heightPercent}%">
<img data-src="${img.url}"
alt="Preview"
crossorigin="anonymous"
referrerpolicy="no-referrer"
width="${img.width}"
height="${img.height}"
class="lazy">
</div>
`;
}).join('')}
</div>
</div>
</div>
`;
}
// Add this to the window object for global access
export function toggleShowcase(element) {
const carousel = element.nextElementSibling;
const isCollapsed = carousel.classList.contains('collapsed');
const indicator = element.querySelector('span');
const icon = element.querySelector('i');
carousel.classList.toggle('collapsed');
if (isCollapsed) {
indicator.textContent = 'Hide examples';
icon.classList.replace('fa-chevron-down', 'fa-chevron-up');
initLazyLoading(carousel);
} else {
const count = carousel.querySelectorAll('.media-wrapper').length;
indicator.textContent = `Show ${count} examples`;
icon.classList.replace('fa-chevron-up', 'fa-chevron-down');
}
};
// Add lazy loading initialization
export function initLazyLoading(container) {
const lazyElements = container.querySelectorAll('.lazy');
const lazyLoad = (element) => {
if (element.tagName.toLowerCase() === 'video') {
element.src = element.dataset.src;
element.querySelector('source').src = element.dataset.src;
element.load();
} else {
element.src = element.dataset.src;
}
return `
<img src="${img.url}"
alt="Preview"
crossorigin="anonymous"
referrerpolicy="no-referrer"
loading="lazy">
`;
}).join('');
element.classList.remove('lazy');
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
lazyLoad(entry.target);
observer.unobserve(entry.target);
}
});
});
lazyElements.forEach(element => observer.observe(element));
}

View File

@@ -2,7 +2,7 @@ import { debounce } from './utils/debounce.js';
import { LoadingManager } from './managers/LoadingManager.js';
import { modalManager } from './managers/ModalManager.js';
import { state } from './state/index.js';
import { showLoraModal } from './components/LoraCard.js';
import { showLoraModal, toggleShowcase, initLazyLoading } from './components/LoraCard.js';
import { loadMoreLoras, fetchCivitai, deleteModel, replacePreview, resetAndReload, refreshLoras } from './api/loraApi.js';
import {
showToast,
@@ -45,6 +45,7 @@ window.toggleFolderTags = toggleFolderTags;
window.settingsManager = new SettingsManager();
window.toggleApiKeyVisibility = toggleApiKeyVisibility;
window.moveManager = moveManager;
window.toggleShowcase = toggleShowcase;
// Initialize everything when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
@@ -58,6 +59,7 @@ document.addEventListener('DOMContentLoaded', () => {
initTheme();
initFolderTagsVisibility();
initBackToTop();
initLazyLoading();
window.searchManager = new SearchManager();
new LoraContextMenu();
});