덱 목록 무한 스크롤과 WebP 썸네일 최적화 추가
- 내 덱·공개 덱 목록을 무한 스크롤로 전환(공용 페이저 모듈 추가) - 즐겨찾기 토글 시 카드만 in-place 갱신해 스크롤 위치 유지 - 저장 시 420px WebP 썸네일 생성·전송, 목록은 썸네일 우선 사용 - 삭제·미참조 정리에서 원본 PNG와 썸네일 함께 처리 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
48
result.html
48
result.html
@@ -841,6 +841,7 @@
|
||||
|
||||
/** 저장·다운로드 캡처와 동일한 미리보기 기준 너비(px) */
|
||||
const DECK_PREVIEW_SAVE_WIDTH = 1200;
|
||||
const DECK_THUMBNAIL_WIDTH = 420;
|
||||
|
||||
/**
|
||||
* 저장용 미리보기를 기준 너비로 정규화
|
||||
@@ -880,6 +881,50 @@
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 목록용 축소 WebP 썸네일 생성 (WebP 인코딩 실패 시 null)
|
||||
* @param {string} dataUrl 원본 미리보기 data URL
|
||||
* @param {number} [targetWidth]
|
||||
* @returns {Promise<string|null>}
|
||||
*/
|
||||
async function createThumbnailWebp(
|
||||
dataUrl,
|
||||
targetWidth = DECK_THUMBNAIL_WIDTH,
|
||||
) {
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
if (!img.width) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
const w = Math.min(targetWidth, img.width);
|
||||
const h = Math.max(1, Math.round(img.height * (w / img.width)));
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
ctx.drawImage(img, 0, 0, w, h);
|
||||
let out = null;
|
||||
try {
|
||||
out = canvas.toDataURL('image/webp', 0.82);
|
||||
} catch {
|
||||
out = null;
|
||||
}
|
||||
// WebP 미지원 브라우저는 PNG로 폴백되므로 이 경우 전송하지 않는다.
|
||||
resolve(out && out.startsWith('data:image/webp') ? out : null);
|
||||
};
|
||||
img.onerror = () => resolve(null);
|
||||
img.src = dataUrl;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 저장·공유 로딩 UI
|
||||
* @param {string} message
|
||||
@@ -1131,10 +1176,12 @@
|
||||
loader.setProgress(8, t('savingPreview', '미리보기 생성 중...'));
|
||||
loader.startAuto(52);
|
||||
let previewImage = null;
|
||||
let thumbnailImage = null;
|
||||
try {
|
||||
previewImage = await capturePreviewPng({ highQuality: false });
|
||||
if (previewImage) {
|
||||
previewImage = await normalizePreviewForSave(previewImage);
|
||||
thumbnailImage = await createThumbnailWebp(previewImage);
|
||||
}
|
||||
} catch (captureErr) {
|
||||
console.warn('Preview capture failed:', captureErr);
|
||||
@@ -1148,6 +1195,7 @@
|
||||
name: getDeckSaveName(),
|
||||
cards,
|
||||
previewImage,
|
||||
thumbnailImage,
|
||||
isPublic,
|
||||
});
|
||||
loader.stopAuto();
|
||||
|
||||
Reference in New Issue
Block a user