Created
April 30, 2026 15:37
-
-
Save carddass2018-svg/5dd5155d80fd66c7225a24fc6d49dd3e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="zh-Hant"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"> | |
| <title>龍珠閃卡 - 穩定版</title> | |
| <style> | |
| body { background: #000; margin: 0; height: 100vh; display: flex; align-items: center; justify-content: center; overflow: hidden; font-family: sans-serif; } | |
| #auth-overlay { position: fixed; inset: 0; background: #000; z-index: 9999; display: flex; flex-direction: column; align-items: center; justify-content: center; } | |
| #auth-btn { background: #f1c40f; border: none; padding: 20px 50px; border-radius: 15px; font-size: 22px; font-weight: bold; cursor: pointer; } | |
| #set-btn { position: fixed; top: 15px; left: 15px; z-index: 1000; background: rgba(0,0,0,0.6); color: #fff; border: 1px solid #555; padding: 10px; border-radius: 50%; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; cursor: pointer; display: none; } | |
| .panel { position: fixed; top: 0; left: 0; width: 100%; background: #111; padding: 25px; border-bottom: 2px solid #333; z-index: 999; box-sizing: border-box; transform: translateY(-110%); transition: 0.3s; color: #fff; } | |
| .panel.active { transform: translateY(0); } | |
| .row { margin-bottom: 15px; font-size: 13px; } | |
| .status { font-size: 10px; margin-left: 10px; color: #555; } | |
| .ready { color: #0f0 !important; } | |
| input[type="file"] { width: 100%; margin-top: 5px; color: #0f0; } | |
| #card { width: 82vw; max-width: 320px; aspect-ratio: 1 / 1.4; position: relative; border-radius: 18px; overflow: hidden; background: #111; box-shadow: 0 0 50px #000; } | |
| .layer { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; } | |
| #c-base { width: 100%; height: 100%; object-fit: cover; z-index: 1; } | |
| #foil-wrap { z-index: 2; mix-blend-mode: color-dodge; } | |
| #c-foil { background-size: cover; background-position: center; opacity: 0.55; filter: contrast(2.7) saturate(2.2) brightness(1.1); } | |
| #c-shine { z-index: 3; background: linear-gradient(110deg, transparent 40%, rgba(255,255,255,0.7) 50%, transparent 60%); background-size: 200% 200%; mix-blend-mode: overlay; } | |
| canvas { display: none; } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="auth-overlay"><button id="auth-btn" onclick="init()">進入龍珠珍藏館</button></div> | |
| <div id="set-btn" onclick="document.getElementById('p').classList.toggle('active')">⚙️</div> | |
| <div class="panel" id="p"> | |
| <div class="row">1. 原圖: <span id="s-b" class="status">未選取</span><input type="file" accept="image/*" onchange="ld(this,'b')"></div> | |
| <div class="row">2. 遮罩: <span id="s-m" class="status">未選取</span><input type="file" accept="image/*" onchange="ld(this,'m')"></div> | |
| <div class="row">3. 閃紋: <span id="s-f" class="status">未選取</span><input type="file" accept="image/*" onchange="ld(this,'f')"></div> | |
| <button id="gen-btn" onclick="process()" style="width:100%; padding:12px; background:#444; color:#888; border:none; border-radius:8px; font-weight:bold; cursor:not-allowed;">請先選取所有圖片</button> | |
| </div> | |
| <div id="card"> | |
| <img id="c-base" src="https://asobi-center.com"> | |
| <div id="foil-wrap" class="layer"><div id="c-foil" class="layer"></div></div> | |
| <div id="c-shine" class="layer"></div> | |
| </div> | |
| <canvas id="cvs"></canvas> | |
| <script> | |
| let imgB, imgM, imgF; | |
| const foil = document.getElementById('c-foil'), base = document.getElementById('c-base'), shine = document.getElementById('c-shine'), cvs = document.getElementById('cvs'), ctx = cvs.getContext('2d', { willReadFrequently: true }); | |
| function ld(input, type) { | |
| if (!input.files || !input.files[0]) return; | |
| const reader = new FileReader(); | |
| const status = document.getElementById('s-' + type); | |
| status.innerText = "讀取中..."; | |
| reader.onload = (e) => { | |
| const img = new Image(); | |
| img.onload = () => { | |
| if (type === 'b') { imgB = img; base.src = img.src; } | |
| if (type === 'm') { imgM = img; } | |
| if (type === 'f') { imgF = img; } | |
| status.innerText = "已就緒 ✓"; | |
| status.classList.add('ready'); | |
| checkReady(); | |
| }; | |
| img.src = e.target.result; | |
| }; | |
| reader.readAsDataURL(input.files[0]); | |
| } | |
| function checkReady() { | |
| if (imgM && imgF) { | |
| const btn = document.getElementById('gen-btn'); | |
| btn.style.background = "#f1c40f"; | |
| btn.style.color = "#000"; | |
| btn.style.cursor = "pointer"; | |
| btn.innerText = "⚡ 生成強化閃卡"; | |
| } | |
| } | |
| function process() { | |
| if (!imgF || !imgM) return; | |
| const maxLimit = 800; | |
| let scale = Math.min(1, maxLimit / Math.max(imgM.width, imgM.height)); | |
| cvs.width = imgM.width * scale; | |
| cvs.height = imgM.height * scale; | |
| ctx.drawImage(imgM, 0, 0, cvs.width, cvs.height); | |
| let mData = ctx.getImageData(0, 0, cvs.width, cvs.height); | |
| ctx.clearRect(0, 0, cvs.width, cvs.height); | |
| ctx.drawImage(imgF, 0, 0, cvs.width, cvs.height); | |
| let fData = ctx.getImageData(0, 0, cvs.width, cvs.height); | |
| for (let i = 0; i < mData.data.length; i += 4) { | |
| if (mData.data[i] < 230) fData.data[i + 3] = 0; | |
| } | |
| ctx.putImageData(fData, 0, 0); | |
| foil.style.backgroundImage = `url('${cvs.toDataURL('image/png')}')`; | |
| document.getElementById('p').classList.remove('active'); | |
| } | |
| function init() { | |
| if (typeof DeviceOrientationEvent !== 'undefined' && typeof DeviceOrientationEvent.requestPermission === 'function') { | |
| DeviceOrientationEvent.requestPermission().then(res => { if(res === 'granted') start(); }); | |
| } else { start(); } | |
| } | |
| function start() { | |
| document.getElementById('auth-overlay').style.display = 'none'; | |
| document.getElementById('set-btn').style.display = 'flex'; | |
| let ticking = false; | |
| window.addEventListener('deviceorientation', e => { | |
| if (!ticking) { | |
| window.requestAnimationFrame(() => { | |
| let lr = e.gamma || 0, fb = (e.beta || 45) - 45; | |
| let intensity = Math.abs(lr) / 45; | |
| foil.style.filter = `hue-rotate(${lr * 15}deg) brightness(${1.0 + intensity * 0.4}) contrast(2.7) saturate(2.2)`; | |
| foil.style.opacity = Math.min(0.55 + intensity * 0.3, 0.85); | |
| shine.style.backgroundPosition = `${50 + lr * 3}% ${50 + fb * 3}%`; | |
| ticking = false; | |
| }); | |
| ticking = true; | |
| } | |
| }); | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment