diff --git a/src/pages/qrcode_generator.js b/src/pages/qrcode_generator.js index c331bfd..12ee10c 100644 --- a/src/pages/qrcode_generator.js +++ b/src/pages/qrcode_generator.js @@ -10,7 +10,9 @@ const logo = "https://i.meee.com.tw/0SiZRVA.jpg"; const QRCodeGeneratorPage = () => { const [urlInput, setUrlInput] = useState(""); + const [titleInput, setTitleInput] = useState(""); const [qrCodeUrl, setQrCodeUrl] = useState(null); + const [composedUrl, setComposedUrl] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -19,8 +21,57 @@ const QRCodeGeneratorPage = () => { if (qrCodeUrl && qrCodeUrl.startsWith("blob:")) { URL.revokeObjectURL(qrCodeUrl); } + if (composedUrl && composedUrl.startsWith("blob:")) { + URL.revokeObjectURL(composedUrl); + } }; - }, [qrCodeUrl]); + }, [qrCodeUrl, composedUrl]); + + const buildPoster = async (qrBlobUrl, titleText) => { + const img = new Image(); + img.src = qrBlobUrl; + + await new Promise((resolve, reject) => { + img.onload = resolve; + img.onerror = reject; + }); + + const width = 1080; + const height = 2340; + const qrSize = 1000; + + const canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + + const ctx = canvas.getContext("2d"); + if (!ctx) throw new Error("Canvas 不支援"); + + ctx.fillStyle = "#FFFFFF"; + ctx.fillRect(0, 0, width, height); + + ctx.textAlign = "center"; + ctx.textBaseline = "top"; + + ctx.fillStyle = "#FFAF73"; + ctx.font = "bold 80px 'Noto Serif TC', serif"; + ctx.fillText(titleText, width / 2, 325); + + const qrX = (width - qrSize) / 2; + const qrY = (height - qrSize) / 2; + ctx.drawImage(img, qrX, qrY, qrSize, qrSize); + + ctx.fillStyle = "#6D9DF8"; + ctx.font = "40px 'Noto Serif TC', serif"; + ctx.fillText("FCU iOS Club", width / 2, 2000); + + return new Promise((resolve, reject) => { + canvas.toBlob((blob) => { + if (!blob) return reject(new Error("toBlob failed")); + resolve(URL.createObjectURL(blob)); + }, "image/png"); + }); + }; const generateQRCode = async () => { if (!urlInput.trim()) { @@ -31,6 +82,7 @@ const QRCodeGeneratorPage = () => { setLoading(true); setError(null); setQrCodeUrl(null); + setComposedUrl(null); const payload = { data: urlInput, @@ -43,7 +95,7 @@ const QRCodeGeneratorPage = () => { gradientColor1: "#FFAF73", gradientColor2: "#6D9DF8", gradientOnEyes: true, - bgColor: "#ffffff", + bgColor: "#FFFFFF", logo: logo, logoMode: "clean", }, @@ -80,6 +132,14 @@ const QRCodeGeneratorPage = () => { const blob = await response.blob(); const url = URL.createObjectURL(blob); setQrCodeUrl(url); + if (titleInput.trim()) { + try { + const posterUrl = await buildPoster(url, titleInput.trim()); + setComposedUrl(posterUrl); + } catch (composeError) { + console.error("Poster generation error:", composeError); + } + } } else { const result = await response.json(); console.log("API Response:", result); @@ -103,6 +163,14 @@ const QRCodeGeneratorPage = () => { const blob = await imgResponse.blob(); const url = URL.createObjectURL(blob); setQrCodeUrl(url); + if (titleInput.trim()) { + try { + const posterUrl = await buildPoster(url, titleInput.trim()); + setComposedUrl(posterUrl); + } catch (composeError) { + console.error("Poster generation error:", composeError); + } + } } else { throw new Error(`圖片下載失敗 (${imgResponse.status})`); } @@ -126,19 +194,28 @@ const QRCodeGeneratorPage = () => { }; const downloadQRCode = () => { - if (!qrCodeUrl) return; + const targetUrl = composedUrl || qrCodeUrl; + if (!targetUrl) return; const link = document.createElement("a"); - link.href = qrCodeUrl; + link.href = targetUrl; link.download = "iOSClub_QRCode.png"; document.body.appendChild(link); link.click(); document.body.removeChild(link); }; - const clearInput = () => { + const clearUrlInput = () => { setUrlInput(""); setQrCodeUrl(null); + setComposedUrl(null); + setError(null); + }; + + const clearTitleInput = () => { + setTitleInput(""); + setQrCodeUrl(null); + setComposedUrl(null); setError(null); }; @@ -193,6 +270,13 @@ const QRCodeGeneratorPage = () => { 社團 Logo:內嵌 iOS Club 官方標誌 +