Skip to content

Instantly share code, notes, and snippets.

@carddass2018-svg
Created May 3, 2026 06:58
Show Gist options
  • Select an option

  • Save carddass2018-svg/27841cccf19623c72ffe7e57a84d6509 to your computer and use it in GitHub Desktop.

Select an option

Save carddass2018-svg/27841cccf19623c72ffe7e57a84d6509 to your computer and use it in GitHub Desktop.
<!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; transition: transform 0.1s ease-out; transform-style: preserve-3d; }
#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: 12px; font-size: 20px; font-weight: bold; cursor: pointer; }
.panel { position: fixed; top: 0; left: 0; width: 100%; background: #111; padding: 20px; border-bottom: 2px solid #333; z-index: 999; color: #fff; transition: 0.3s; }
.panel.hidden { transform: translateY(-110%); }
#set-btn { position: fixed; top: 15px; left: 15px; z-index: 1000; background: rgba(0,0,0,0.8); color: #fff; border: 1px solid #f1c40f; border-radius: 50%; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
#perspective-container { perspective: 1200px; padding: 50px; }
#card {
width: 85vw; max-width: 320px; aspect-ratio: 1/1.46; position: relative;
border-radius: 20px; background: #000;
box-shadow: 0 0 50px rgba(0,0,0,0.9);
overflow: hidden;
-webkit-mask-image: -webkit-radial-gradient(white, black);
mask-image: radial-gradient(white, black);
isolation: isolate;
transform: translateZ(0);
transform-style: preserve-3d;
}
.layer { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; object-fit: fill; border-radius: inherit; }
#base-img { z-index: 1; }
#foil-img { z-index: 5; mix-blend-mode: hard-light; opacity: 0.8; display: none; }
#shine-layer {
z-index: 10; position: absolute;
/* 增大容器範圍以支撐超大範圍位移 */
inset: -200%;
/* 🚀 讓光暈更凝聚,中心更亮 */
background: radial-gradient(circle at center, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0.2) 20%, transparent 50%);
background-size: 60% 60%; /* 🚀 縮小背景尺寸讓位移更劇烈 */
background-repeat: no-repeat;
mix-blend-mode: overlay;
filter: blur(15px); /* 稍微調低模糊度,讓光影邊界明確一點 */
pointer-events: none;
opacity: 0;
will-change: background-position;
}
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('hidden')">⚙️</div>
<div class="panel" id="p">
<div class="row" style="font-size:12px; margin-bottom:10px;">1. 底圖: <input type="file" accept="image/*" onchange="ld(this,'b')"></div>
<div class="row" style="font-size:12px; margin-bottom:10px;">2. 遮罩: <input type="file" accept="image/*" onchange="ld(this,'m')"></div>
<div class="row" style="font-size:12px; margin-bottom:10px;">3. 貼圖: <input type="file" accept="image/*" onchange="ld(this,'f')"></div>
<button id="gen-btn" onclick="render()" style="width:100%; padding:12px; background:#444; color:#888; border:none; border-radius:8px; font-weight:bold; cursor:not-allowed;">生成閃卡</button>
</div>
<div id="perspective-container">
<div id="card">
<img id="base-img" class="layer">
<img id="foil-img" class="layer">
<div id="shine-layer"></div>
</div>
</div>
<canvas id="cvs"></canvas>
<script>
let cache = { b:null, m:null, f:null };
const foilE = document.getElementById('foil-img'),
baseE = document.getElementById('base-img'),
shineE = document.getElementById('shine-layer'),
cardE = document.getElementById('card'),
cvs = document.getElementById('cvs');
function ld(input, type) {
if (!input.files[0]) return;
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
const max = 900;
let scale = Math.min(1, max / Math.max(img.width, img.height));
const tmpC = document.createElement('canvas');
tmpC.width = img.width * scale; tmpC.height = img.height * scale;
tmpC.getContext('2d').drawImage(img, 0, 0, tmpC.width, tmpC.height);
const smallImg = new Image();
smallImg.onload = () => {
cache[type] = smallImg;
if (type === 'b') baseE.src = smallImg.src;
if (cache.b && cache.m && cache.f) {
const btn = document.getElementById('gen-btn');
btn.style.background = "#f1c40f"; btn.style.color = "#000"; btn.style.cursor = "pointer";
}
};
smallImg.src = tmpC.toDataURL('image/png');
};
img.src = e.target.result;
};
reader.readAsDataURL(input.files[0]);
}
function render() {
if (!cache.b || !cache.m || !cache.f) return;
const w = cache.m.width, h = cache.m.height;
cvs.width = w; cvs.height = h;
const ctx = cvs.getContext('2d');
ctx.drawImage(cache.m, 0, 0, w, h);
const mData = ctx.getImageData(0,0,w,h).data;
ctx.clearRect(0,0,w,h);
ctx.drawImage(cache.f, 0, 0, w, h);
const fImgData = ctx.getImageData(0,0,w,h);
const fData = fImgData.data;
for (let i = 0; i < fData.length; i += 4) {
const br = (mData[i] + mData[i+1] + mData[i+2]) / 3;
if (br < 80) fData[i+3] = 0;
else if (br < 120) fData[i+3] = ((br - 80) / 40) * 255;
}
ctx.putImageData(fImgData, 0, 0);
foilE.src = cvs.toDataURL();
foilE.style.display = 'block';
document.getElementById('p').classList.add('hidden');
}
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';
let lastG = 0, lastB = 0, hueCycle = 0;
const LERP = 0.35; // 🚀 靈敏度再次拉滿
const OFFSET_B = 60;
window.addEventListener('deviceorientation', e => {
window.requestAnimationFrame(() => {
let curG = e.gamma || 0;
let curB = (e.beta || OFFSET_B) - OFFSET_B;
lastG += (curG - lastG) * LERP;
lastB += (curB - lastB) * LERP;
// 3D 旋轉
cardE.style.transform = `rotateX(${-Math.max(-25, Math.min(25, lastB * 0.9))}deg) rotateY(${Math.max(-25, Math.min(25, lastG * 0.9))}deg) translateZ(0)`;
// 🌈 顏色變幻加速
hueCycle += (Math.abs(lastG - curG) + Math.abs(lastB - curB)) * 3.5;
const hue = ((lastG + lastB) * 6) + hueCycle;
foilE.style.filter = `hue-rotate(${hue}deg) saturate(0.5) brightness(1.2)`;
// 🌫️ 🚀 2D 光影極速位移:使用超大倍率 45
// 這樣即使手機傾斜 10 度,光影中心也會移動 450%,從而快速劃過卡面
const posX = 50 + lastG * 45;
const posY = 50 + lastB * 45;
shineE.style.backgroundPosition = `${posX}% ${posY}%`;
// 強光感:傾斜時亮度顯著提升
const intensity = Math.min(1, (Math.abs(lastG) + Math.abs(lastB)) / 40);
shineE.style.opacity = 0.4 + intensity * 0.5;
});
});
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment