Created
April 30, 2026 15:53
-
-
Save carddass2018-svg/7317e929703055f0b6201d9e160cc9ed 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: 20px; border-bottom: 2px solid #333; z-index: 999; box-sizing: border-box; transform: translateY(-110%); transition: 0.3s; color: #fff; max-height: 80vh; overflow-y: auto; } | |
| .panel.active { transform: translateY(0); } | |
| .row { margin-bottom: 12px; font-size: 13px; border-bottom: 1px solid #222; padding-bottom: 8px; } | |
| .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; } | |
| /* 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); } | |
| /* 2. 燙金層 (Gold Foil) - 獨立層次 */ | |
| #gold-wrap { z-index: 3; mix-blend-mode: color-dodge; } | |
| #c-gold { | |
| background-size: cover; background-position: center; opacity: 0; | |
| filter: contrast(3) saturate(1.5) brightness(1.5) sepia(1) hue-rotate(5deg); | |
| /* 預設金色濾鏡: sepia 加上黃色調 */ | |
| } | |
| /* 3. 玻璃高光 */ | |
| #c-shine { z-index: 4; 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> | |
| <div class="row" style="background: #221a00;">4. 燙金遮罩 (可選): <span id="s-g" class="status">未選取</span><input type="file" accept="image/*" onchange="ld(this,'g')"></div> | |
| <button id="gen-btn" onclick="processAll()" 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="gold-wrap" class="layer"><div id="c-gold" class="layer"></div></div> | |
| <div id="c-shine" class="layer"></div> | |
| </div> | |
| <canvas id="cvs"></canvas> | |
| <script> | |
| let imgM, imgF, imgG; | |
| const foil = document.getElementById('c-foil'), gold = document.getElementById('c-gold'), 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 status = document.getElementById('s-' + type); | |
| status.innerText = "讀取中..."; | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { | |
| const img = new Image(); | |
| img.onload = () => { | |
| if (type === 'b') base.src = img.src; | |
| if (type === 'm') imgM = img; | |
| if (type === 'f') imgF = img; | |
| if (type === 'g') imgG = 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 createMaskedImg(mask, texture) { | |
| const maxLimit = 800; | |
| let scale = Math.min(1, maxLimit / Math.max(mask.width, mask.height)); | |
| cvs.width = mask.width * scale; cvs.height = mask.height * scale; | |
| ctx.clearRect(0, 0, cvs.width, cvs.height); | |
| ctx.drawImage(mask, 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(texture, 0, 0, cvs.width, cvs.height); | |
| let tData = ctx.getImageData(0, 0, cvs.width, cvs.height); | |
| for (let i = 0; i < mData.data.length; i += 4) { | |
| if (mData.data[i] < 230) tData.data[i + 3] = 0; | |
| } | |
| ctx.putImageData(tData, 0, 0); | |
| return cvs.toDataURL('image/png'); | |
| } | |
| function processAll() { | |
| if (!imgF || !imgM) return; | |
| // 生成一般閃紋 | |
| foil.style.backgroundImage = `url('${createMaskedImg(imgM, imgF)}')`; | |
| // 如果有選燙金圖,生成燙金效果 | |
| if (imgG) { | |
| gold.style.backgroundImage = `url('${createMaskedImg(imgG, imgF)}')`; | |
| gold.style.opacity = "0.7"; // 燙金層基礎透明度 | |
| } else { | |
| gold.style.backgroundImage = ""; | |
| gold.style.opacity = "0"; | |
| } | |
| 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 lastLR = 0, lastFB = 0; | |
| const lerp = 0.2; | |
| window.addEventListener('deviceorientation', e => { | |
| window.requestAnimationFrame(() => { | |
| let currentLR = e.gamma || 0; | |
| let currentFB = (e.beta || 45) - 45; | |
| lastLR += (currentLR - lastLR) * lerp; | |
| lastFB += (currentFB - lastFB) * lerp; | |
| let intensity = Math.abs(lastLR) / 45; | |
| // 一般閃紋:虹彩變色 | |
| foil.style.filter = `hue-rotate(${lastLR * 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); | |
| // 燙金層:固定金色,僅改變亮度 | |
| if (imgG) { | |
| gold.style.filter = `brightness(${1.2 + intensity * 0.8}) contrast(2.7) saturate(1.5) sepia(1) hue-rotate(5deg)`; | |
| gold.style.opacity = Math.min(0.7 + intensity * 0.3, 1.0); | |
| } | |
| shine.style.backgroundPosition = `${50 + lastLR * 3}% ${50 + lastFB * 3}%`; | |
| }); | |
| }); | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment