420 lines
15 KiB
HTML
420 lines
15 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Deck Result</title>
|
|
<!-- Lib -->
|
|
<script src="/lib/html2canvas-1.4.1/html2canvas.min.js"></script>
|
|
<script src="/script/navigation.js"></script>
|
|
<script type="module" src="/i18n/i18n.js"></script>
|
|
<link href="/style/navigation.css" rel="stylesheet" />
|
|
<link href="/style/style.css" rel="stylesheet" />
|
|
<link href="/style/output.css" rel="stylesheet" />
|
|
</head>
|
|
|
|
<body>
|
|
<div id="app">
|
|
<div
|
|
id="loading-screen"
|
|
class="fixed top-0 left-0 w-full h-full bg-white flex justify-center items-center"
|
|
>
|
|
<div
|
|
class="animate-spin rounded-full h-32 w-32 border-t-2 border-b-2 border-gray-900"
|
|
></div>
|
|
</div>
|
|
|
|
<div
|
|
class="mx-auto max-w-6xl px-4 pt-4 pb-8 sm:px-8 lg:max-w-7xl lg:px-12"
|
|
>
|
|
<div class="w-full flex text-md font-semibold mt-4 mb-4">
|
|
<div class="w-32">파일명 설정:</div>
|
|
<input
|
|
class="w-full"
|
|
type="text"
|
|
id="deckName"
|
|
class="px-2"
|
|
placeholder="미입력시 시리즈명으로 저장"
|
|
/>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<button
|
|
type="button"
|
|
class="relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent bg-gray-200 transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2"
|
|
role="switch"
|
|
aria-checked="false"
|
|
aria-labelledby="annual-billing-label"
|
|
id="annual-billing-toggle"
|
|
>
|
|
<span
|
|
aria-hidden="true"
|
|
class="pointer-events-none inline-block h-5 w-5 translate-x-0 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
|
|
id="annual-billing-circle"
|
|
></span>
|
|
</button>
|
|
<span class="ml-3 text-sm" id="annual-billing-label">
|
|
<span class="font-medium text-gray-900">고화질로 저장</span>
|
|
<span class="text-gray-500">(용량은 2배, 사이즈는 4배)</span>
|
|
</span>
|
|
<input type="checkbox" id="highQuality" class="hidden" />
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="mx-auto max-w-2xl px-4 sm:px-6 pb-24 sm:max-w-4xl lg:max-w-5xl lg:px-8 xl:max-w-7xl"
|
|
>
|
|
<!-- <h2 id="SearchResults" class="text-2xl font-bold my-4" data-i18n="preview"></h2> -->
|
|
<div
|
|
id="resultArea"
|
|
class="result_area px-4 sm:px-8 py-16 border-2 border-solid"
|
|
></div>
|
|
</div>
|
|
<div class="fixed bottom-0 flex w-full bg-white go-result-page-btn">
|
|
<div
|
|
onclick="goBackToCardList()"
|
|
class="w-1/3 flex items-center justify-center h-12 cursor-pointer bg-gray-400 px-3 py-2 font-semibold text-white shadow-sm hover:bg-gray-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-400"
|
|
data-i18n="back"
|
|
></div>
|
|
<div
|
|
onclick="saveAsImage()"
|
|
class="w-2/3 flex items-center justify-center h-12 cursor-pointer bg-indigo-500 px-3 py-2 font-semibold text-white shadow-sm hover:bg-indigo-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500"
|
|
data-i18n="saveAsImage"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
<script src="/script/loading.js"></script>
|
|
|
|
<script>
|
|
// 토글 버튼 이벤트 핸들러
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
initializeToggleButton();
|
|
initializeResultArea();
|
|
});
|
|
|
|
function initializeToggleButton() {
|
|
const toggleButton = document.getElementById("annual-billing-toggle");
|
|
const toggleCircle = document.getElementById("annual-billing-circle");
|
|
const highQualityCheckbox = document.getElementById("highQuality");
|
|
|
|
toggleButton.addEventListener("click", function () {
|
|
const isChecked =
|
|
toggleButton.getAttribute("aria-checked") === "true";
|
|
updateToggleState(
|
|
!isChecked,
|
|
toggleButton,
|
|
toggleCircle,
|
|
highQualityCheckbox
|
|
);
|
|
});
|
|
|
|
// 초기 상태 설정
|
|
updateToggleState(
|
|
highQualityCheckbox.checked,
|
|
toggleButton,
|
|
toggleCircle,
|
|
highQualityCheckbox
|
|
);
|
|
}
|
|
|
|
function updateToggleState(
|
|
isChecked,
|
|
toggleButton,
|
|
toggleCircle,
|
|
checkbox
|
|
) {
|
|
toggleButton.setAttribute("aria-checked", isChecked);
|
|
toggleButton.classList.toggle("bg-indigo-600", isChecked);
|
|
toggleButton.classList.toggle("bg-gray-200", !isChecked);
|
|
toggleCircle.style.transform = isChecked
|
|
? "translateX(19px)"
|
|
: "translateX(0px)";
|
|
checkbox.checked = isChecked;
|
|
}
|
|
|
|
function initializeResultArea() {
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
let selectedCardsParam =
|
|
urlParams.get("cards") || localStorage.getItem("selectedCards");
|
|
let seriesKey =
|
|
urlParams.get("series") || localStorage.getItem("selectedSeries");
|
|
|
|
if (selectedCardsParam && seriesKey) {
|
|
const selectedCards = JSON.parse(
|
|
decodeURIComponent(selectedCardsParam)
|
|
);
|
|
displayResultCards(selectedCards, seriesKey);
|
|
}
|
|
}
|
|
|
|
function displayResultCards(cards, seriesKey) {
|
|
const resultGrid = document.getElementById("resultArea");
|
|
resultGrid.innerHTML = "";
|
|
|
|
const keyCards = cards.filter((card) => card.key);
|
|
const otherCards = cards.filter((card) => !card.key);
|
|
|
|
// 키 카드 행 (첫 번째 행)
|
|
const keyCardRow = document.createElement("div");
|
|
keyCardRow.className = "flex flex-wrap gap-2 mb-4 justify-center";
|
|
keyCards.forEach((card) => {
|
|
const cardDiv = createCardElement(card, seriesKey, "keyCard");
|
|
cardDiv.className =
|
|
"responsive-keycard relative w-56 overflow-hidden rounded-md lg:aspect-none group-hover:opacity-75";
|
|
keyCardRow.appendChild(cardDiv);
|
|
});
|
|
resultGrid.appendChild(keyCardRow);
|
|
|
|
// 나머지 카드 행 (여러 행으로 균등 분배)
|
|
const rowCounts = calculateRowCounts(otherCards.length);
|
|
let cardIndex = 0;
|
|
rowCounts.forEach((rowCount) => {
|
|
const rowCards = otherCards.slice(cardIndex, cardIndex + rowCount);
|
|
const row = createRow(rowCards, seriesKey);
|
|
resultGrid.appendChild(row);
|
|
cardIndex += rowCount;
|
|
});
|
|
}
|
|
|
|
function calculateRowCounts(cardCount) {
|
|
if (cardCount <= 6) {
|
|
return [cardCount];
|
|
}
|
|
|
|
const rowCounts = [];
|
|
let remainingCards = cardCount;
|
|
while (remainingCards > 0) {
|
|
const rowCount = Math.min(6, remainingCards);
|
|
rowCounts.push(rowCount);
|
|
remainingCards -= rowCount;
|
|
}
|
|
|
|
// 모든 행의 평균 카드 수를 계산
|
|
const averageCount = Math.floor(cardCount / rowCounts.length);
|
|
let remainder = cardCount % rowCounts.length;
|
|
|
|
// 각 행의 카드 수를 평균에 맞게 조정
|
|
for (let i = 0; i < rowCounts.length; i++) {
|
|
rowCounts[i] = averageCount;
|
|
if (remainder > 0) {
|
|
rowCounts[i]++;
|
|
remainder--;
|
|
}
|
|
}
|
|
|
|
return rowCounts;
|
|
}
|
|
|
|
function createRow(cards, seriesKey) {
|
|
const rowDiv = document.createElement("div");
|
|
rowDiv.className = "flex flex-wrap gap-2 justify-center w-full mb-4";
|
|
cards.forEach((card) => {
|
|
const cardDiv = createCardElement(card, seriesKey);
|
|
cardDiv.className =
|
|
"responsive-card relative w-36 overflow-hidden rounded-md lg:aspect-none group-hover:opacity-75";
|
|
rowDiv.appendChild(cardDiv);
|
|
});
|
|
return rowDiv;
|
|
}
|
|
|
|
function createCardElement(card, seriesKey, type = "card") {
|
|
const cardDiv = document.createElement("div");
|
|
cardDiv.className = `border rounded-md p-2 ${
|
|
type === "keyCard" ? "col-span-3" : ""
|
|
}`;
|
|
|
|
const img = document.createElement("img");
|
|
img.src = `/images/${seriesKey}/${card.imgSrc.split("/").pop()}`;
|
|
img.alt = card.Number;
|
|
img.className = "w-full";
|
|
|
|
// 카드 넘버
|
|
const name = document.createElement("div");
|
|
name.textContent = card.Number;
|
|
name.className = "card-number text-xs text-center";
|
|
|
|
// 카드 개수
|
|
const cardCount = document.createElement("div");
|
|
cardCount.className = "card-count text-md text-center font-bold";
|
|
|
|
// 카드 개수 텍스트 (예: x 3)
|
|
cardCount.textContent = `x ${card.count}`;
|
|
|
|
// 넘버와 개수를 감싸는 컨테이너
|
|
const infoContainer = document.createElement("div");
|
|
infoContainer.className =
|
|
"infoContainer flex flex-col items-center mb-2";
|
|
|
|
// 하위 요소 추가
|
|
infoContainer.appendChild(name);
|
|
infoContainer.appendChild(cardCount);
|
|
|
|
// 최종 카드 구성
|
|
cardDiv.appendChild(img);
|
|
cardDiv.appendChild(infoContainer);
|
|
|
|
return cardDiv;
|
|
}
|
|
|
|
async function saveAsImage() {
|
|
showLoading(true);
|
|
|
|
const elementToCapture = document.getElementById("resultArea");
|
|
const highQuality = document.getElementById("highQuality").checked;
|
|
|
|
if (elementToCapture) {
|
|
// 테두리 임시 제거
|
|
elementToCapture.classList.remove("border-2", "border-solid");
|
|
|
|
elementToCapture.style.width = highQuality ? "2048px" : "1024px";
|
|
elementToCapture.style.height = "auto";
|
|
|
|
const responsiveCards = document.querySelectorAll(".responsive-card");
|
|
const responsiveKeycards = document.querySelectorAll(
|
|
".responsive-keycard"
|
|
);
|
|
const infoContainers = document.querySelectorAll(".infoContainer");
|
|
|
|
// 스타일 적용
|
|
responsiveCards.forEach((card) => {
|
|
card.classList.remove("responsive-card");
|
|
card.classList.add("save-responsive-card");
|
|
if (highQuality) card.classList.add("HQ");
|
|
});
|
|
|
|
responsiveKeycards.forEach((card) => {
|
|
card.classList.remove("responsive-keycard");
|
|
card.classList.add("save-responsive-keycard");
|
|
if (highQuality) card.classList.add("HQ");
|
|
});
|
|
|
|
infoContainers.forEach((info) => {
|
|
console.log(info);
|
|
if (highQuality) info.classList.add("HQ");
|
|
});
|
|
|
|
try {
|
|
// 이미지 로드 대기
|
|
const images = elementToCapture.getElementsByTagName("img");
|
|
await Promise.all(
|
|
Array.from(images).map((img) => {
|
|
return new Promise((resolve) => {
|
|
if (img.complete) {
|
|
resolve();
|
|
} else {
|
|
img.onload = () => resolve();
|
|
img.onerror = () => resolve();
|
|
}
|
|
});
|
|
})
|
|
);
|
|
|
|
// html2canvas를 사용한 캡처
|
|
const canvas = await html2canvas(elementToCapture, {
|
|
scale: highQuality ? 2 : 1,
|
|
useCORS: true,
|
|
logging: false,
|
|
allowTaint: true,
|
|
backgroundColor: "#ffffff",
|
|
});
|
|
|
|
// 파일명 설정
|
|
const deckName = document.getElementById("deckName").value;
|
|
let fileName = deckName
|
|
? `uadeck_${deckName}.png`
|
|
: "uadeck_image.png";
|
|
|
|
if (!deckName) {
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const seriesName = urlParams.get("seriesName");
|
|
if (seriesName) {
|
|
fileName = `uadeck_${seriesName}.png`;
|
|
}
|
|
}
|
|
|
|
const now = new Date();
|
|
const timestamp = `${now.getFullYear()}${String(
|
|
now.getMonth() + 1
|
|
).padStart(2, "0")}${String(now.getDate()).padStart(
|
|
2,
|
|
"0"
|
|
)}_${String(now.getHours()).padStart(2, "0")}${String(
|
|
now.getMinutes()
|
|
).padStart(2, "0")}${String(now.getSeconds()).padStart(2, "0")}`;
|
|
fileName = fileName.replace(
|
|
".png",
|
|
`_${timestamp}${highQuality ? "_HQ" : ""}.png`
|
|
);
|
|
|
|
// iOS Safari에서의 다운로드 처리
|
|
if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {
|
|
canvas.toBlob((blob) => {
|
|
const blobUrl = URL.createObjectURL(blob);
|
|
const link = document.createElement("a");
|
|
link.href = blobUrl;
|
|
link.download = fileName;
|
|
link.click();
|
|
setTimeout(() => {
|
|
URL.revokeObjectURL(blobUrl);
|
|
}, 1000);
|
|
});
|
|
} else {
|
|
canvas.toBlob((blob) => {
|
|
const blobUrl = URL.createObjectURL(blob);
|
|
const link = document.createElement("a");
|
|
link.href = blobUrl;
|
|
link.download = fileName;
|
|
link.click();
|
|
URL.revokeObjectURL(blobUrl);
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error("오류 발생:", error);
|
|
} finally {
|
|
// 원래 상태로 복원
|
|
elementToCapture.classList.add("border-2", "border-solid");
|
|
responsiveCards.forEach((card) => {
|
|
card.classList.remove("save-responsive-card", "HQ");
|
|
card.classList.add("responsive-card");
|
|
});
|
|
|
|
responsiveKeycards.forEach((card) => {
|
|
card.classList.remove("save-responsive-keycard", "HQ");
|
|
card.classList.add("responsive-keycard");
|
|
});
|
|
|
|
infoContainers.forEach((info) => {
|
|
info.classList.remove("HQ");
|
|
});
|
|
|
|
elementToCapture.style.width = "initial";
|
|
elementToCapture.style.height = "auto";
|
|
showLoading(false);
|
|
}
|
|
} else {
|
|
console.error("resultArea element not found.");
|
|
showLoading(false);
|
|
}
|
|
}
|
|
|
|
function showLoading(isLoading) {
|
|
const loadingScreen = document.getElementById("loading-screen");
|
|
loadingScreen.style.display = isLoading ? "flex" : "none";
|
|
}
|
|
|
|
function goBackToCardList() {
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const series = urlParams.get("series");
|
|
const cards = urlParams.get("cards");
|
|
const seriesName = urlParams.get("seriesName");
|
|
|
|
if (series) {
|
|
// 카드 목록과 시리즈명도 함께 전달
|
|
window.location.href = `cardList.html?series=${series}&seriesName=${seriesName}&selectedCards=${cards}`;
|
|
} else {
|
|
window.location.href = "index.html";
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|