[260111] 프로젝트 이전 및 초기화
This commit is contained in:
419
result.html
Normal file
419
result.html
Normal file
@@ -0,0 +1,419 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user