feat(me): 全面升级个人主页视觉与交互体验

- 引入昼夜模式下的动态渐变标题与光晕背景效果
- 新增极光动画层,提升页面沉浸感与视觉层次
- 优化粒子系统着色器,支持主题色彩漂移与星云渲染
- 改进叙事文本层样式,增强文字可读性与动效表现
- 调整双手合十解锁逻辑,提高手势识别准确率与稳定性
- 更新多语言文案内容,强化品牌表达与情感连接
- 增加退出冷却机制,避免误触并改善用户体验流畅度
- 统一动画曲线与过渡时长,确保界面响应自然和谐
This commit is contained in:
hehh
2025-12-04 16:36:50 +08:00
parent c21d276f40
commit 67049a126f

162
me.html
View File

@@ -18,9 +18,11 @@
[data-theme="day"] { [data-theme="day"] {
--bg-color: linear-gradient(135deg, #e0eafc 0%, #cfdef3 100%); --bg-color: linear-gradient(135deg, #e0eafc 0%, #cfdef3 100%);
--text-color: #1a1a2e; /* 极深蓝黑,保证可见度 */ --text-color: #1a1a2e; /* 极深蓝黑,保证可见度 */
--accent-color: #0044cc; --accent-color: #3567ff;
--loader-bg: #f0f2f5; --loader-bg: #f0f2f5;
--loader-text: #2c3e50; --loader-text: #2c3e50;
--title-gradient: linear-gradient(120deg, #1a1a2e 0%, #2b56ff 50%, #1947ff 100%);
--veil: radial-gradient(closest-side, rgba(255,255,255,0.0), rgba(255,255,255,0.0) 70%);
} }
/* === 黑夜模式 (霓虹/深空) === */ /* === 黑夜模式 (霓虹/深空) === */
@@ -30,6 +32,8 @@
--accent-color: #00FFFF; --accent-color: #00FFFF;
--loader-bg: #000000; --loader-bg: #000000;
--loader-text: #FFFFFF; --loader-text: #FFFFFF;
--title-gradient: linear-gradient(120deg, #ffffff 0%, #7dfbff 35%, #ff7af3 70%, #ffd36e 100%);
--veil: radial-gradient(closest-side, rgba(0,0,0,0.55), rgba(0,0,0,0.0) 72%);
} }
body { body {
@@ -73,19 +77,34 @@
/* === 2. 叙事文本层 (DOM覆盖) === */ /* === 2. 叙事文本层 (DOM覆盖) === */
#narrative-layer { #narrative-layer {
position: absolute; top: 40%; left: 50%; transform: translate(-50%, -50%); position: absolute; top: 28%; left: 50%; transform: translate(-50%, -50%);
text-align: center; width: 90%; pointer-events: none; z-index: 5; text-align: center; width: 72%; max-width: 1000px; pointer-events: none; z-index: 5;
padding: 16px 24px;
}
#narrative-layer::before {
content: '';
position: absolute; inset: -4% 10% 0 10%;
transform: translateZ(0);
background: var(--veil);
filter: blur(20px) saturate(1.1);
pointer-events: none;
z-index: -1;
} }
.n-title { .n-title {
font-size: 8vw; font-weight: 900; letter-spacing: -2px; margin-bottom: 10px; font-size: clamp(42px, 8vw, 128px); font-weight: 800; letter-spacing: 0; margin-bottom: 14px;
opacity: 0; transform: translateY(30px); transition: all 0.8s cubic-bezier(0.2, 1, 0.3, 1); opacity: 0; transform: translateY(30px); transition: all 0.8s cubic-bezier(0.2, 1, 0.3, 1);
background: linear-gradient(45deg, var(--text-color), var(--accent-color)); background: var(--title-gradient);
background-size: 200% 200%; animation: gradientShift 9s ease-in-out infinite;
-webkit-background-clip: text; -webkit-text-fill-color: transparent; -webkit-background-clip: text; -webkit-text-fill-color: transparent;
text-shadow: 0 10px 40px rgba(0,0,0,0.35);
} }
[data-theme="day"] .n-title { -webkit-text-stroke: 0.6px rgba(255,255,255,0.10); }
[data-theme="night"] .n-title { -webkit-text-stroke: 0.6px rgba(0,0,0,0.20); }
.n-sub { .n-sub {
font-size: 18px; font-weight: 400; letter-spacing: 6px; opacity: 0; font-size: 20px; font-weight: 500; letter-spacing: 6px; opacity: 0;
transform: translateY(20px); transition: all 0.8s 0.2s cubic-bezier(0.2, 1, 0.3, 1); transform: translateY(20px); transition: all 0.8s 0.2s cubic-bezier(0.2, 1, 0.3, 1);
color: var(--text-color); color: var(--text-color);
text-shadow: 0 6px 24px rgba(0,0,0,0.25);
} }
/* 激活状态类名 */ /* 激活状态类名 */
.show-text .n-title, .show-text .n-sub { opacity: 1; transform: translateY(0); } .show-text .n-title, .show-text .n-sub { opacity: 1; transform: translateY(0); }
@@ -104,7 +123,7 @@
} }
.center-stage { .center-stage {
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); position: absolute; top: 78%; left: 50%; transform: translate(-50%, -50%);
text-align: center; width: 100%; pointer-events: auto; text-align: center; width: 100%; pointer-events: auto;
} }
@@ -131,6 +150,20 @@
@keyframes breathe { 0%,100%{opacity:0.6} 50%{opacity:1} } @keyframes breathe { 0%,100%{opacity:0.6} 50%{opacity:1} }
@keyframes spin { to { transform: rotate(360deg); } } @keyframes spin { to { transform: rotate(360deg); } }
@keyframes gradientShift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
#canvas-container { position: fixed; inset: 0; z-index: 1; }
#aurora-layer { position: fixed; inset: 0; z-index: 2; pointer-events: none; }
#aurora-layer::before, #aurora-layer::after { content: ''; position: absolute; inset: -20% -10%; filter: blur(60px); opacity: 0.25; }
@keyframes auroraDrift { 0% { transform: translate3d(-4%, -2%, 0) scale(1); } 50% { transform: translate3d(4%, 2%, 0) scale(1.05); } 100% { transform: translate3d(-4%, 0, 0) scale(1); } }
[data-theme="day"] #aurora-layer::before { background: radial-gradient(60% 40% at 25% 30%, rgba(255,180,200,0.35), transparent 60%); animation: auroraDrift 22s ease-in-out infinite; }
[data-theme="day"] #aurora-layer::after { background: radial-gradient(60% 40% at 70% 60%, rgba(110,195,255,0.35), transparent 60%); animation: auroraDrift 28s ease-in-out infinite reverse; }
[data-theme="night"] #aurora-layer::before { background: radial-gradient(70% 45% at 20% 30%, rgba(0,255,255,0.28), transparent 62%); animation: auroraDrift 24s ease-in-out infinite; }
[data-theme="night"] #aurora-layer::after { background: radial-gradient(70% 45% at 75% 65%, rgba(255,122,243,0.24), transparent 62%); animation: auroraDrift 32s ease-in-out infinite reverse; }
</style> </style>
@@ -183,6 +216,7 @@
<video id="input-video" style="display:none"></video> <video id="input-video" style="display:none"></video>
<div id="canvas-container"></div> <div id="canvas-container"></div>
<div id="aurora-layer"></div>
<script> <script>
/** /**
@@ -196,8 +230,10 @@
handCount: 0, handCount: 0,
handL: new THREE.Vector3(9999,9999,0), handL: new THREE.Vector3(9999,9999,0),
handR: new THREE.Vector3(9999,9999,0), handR: new THREE.Vector3(9999,9999,0),
unlockProgress: 0 unlockProgress: 0,
exitCooldownUntil: 0
}; };
APP_STATE.namasteStableFrames = 0;
// 安全DOM操作防止报错 // 安全DOM操作防止报错
function safeUpdateText(id, text) { function safeUpdateText(id, text) {
@@ -255,46 +291,46 @@
const DICTIONARY = { const DICTIONARY = {
zh: { zh: {
load: [ load: [
"正在构建数字灵魂...", "正在唤醒灵感...",
"接入神经元网络...", "接入神经元网络...",
"校准引力波场...", "校准引力波场...",
"等待视觉信号...", "等待视觉信号...",
"系统准备就绪." "系统准备就绪."
], ],
hints: { hints: {
main: "点击 或 双手合十 开启", main: "双手合十 · 解锁档案",
sub: "单手·流体牵引 | 双手·力场排斥", sub: "单手·引力牵引|双手·力场排斥",
unlocking: "正在识别..." unlocking: "正在识别..."
}, },
slides: [ slides: [
{ t: "HE HOUHUI", s: "JAVA & AI 架构师" }, { t: "", s: "以诚为始,以敬为归" },
{ t: "INFJ", s: "1% 的提倡者 // 理想主义" }, { t: "深度", s: "深入一处,洞见万物" },
{ t: "深度 > 广度", s: "在垂直领域构建壁垒" }, { t: "长期主义", s: "与时间为友,向内修行" },
{ t: "长期主义", s: "做时间的朋友" }, { t: "代码诗性", s: "在逻辑中安放灵魂" },
{ t: "代码哲学", s: "代码是逻辑的载体" }, { t: "创造", s: "让技术拥有意义" },
{ t: "创造", s: "赋予技术以意义" } { t: "INFJ", s: "提倡者 // 温柔坚定" }
] ]
}, },
en: { en: {
load: [ load: [
"Constructing Digital Soul...", "Summoning Lucid Dream...",
"Connecting Neural Network...", "Linking Neurons...",
"Calibrating Gravity Field...", "Tuning Gravity Field...",
"Waiting for Visual Sensor...", "Awaiting Vision...",
"System Ready." "System Ready."
], ],
hints: { hints: {
main: "CLICK OR NAMASTE", main: "Click or Namaste",
sub: "1 Hand Drag · 2 Hands Repel", sub: "One Hand Drag · Two Hands Repel",
unlocking: "IDENTIFYING..." unlocking: "Identifying..."
}, },
slides: [ slides: [
{ t: "HE HOUHUI", s: "JAVA & AI ARCHITECT" }, { t: "Honesty", s: "Begin with truth, return with grace" },
{ t: "INFJ", s: "THE ADVOCATE // 1% UNIVERSE" }, { t: "Depth Over Breadth", s: "One well, a thousand rivers" },
{ t: "DEPTH > BREADTH", s: "BUILDING FORTRESSES" }, { t: "Long-Termism", s: "Friend of Time, seeker within" },
{ t: "LONG-TERMISM", s: "FRIEND OF TIME" }, { t: "Code Philosophy", s: "A vessel for the soul of logic" },
{ t: "CODE PHILOSOPHY", s: "VESSEL OF LOGIC" }, { t: "Creation", s: "Where technique meets meaning" },
{ t: "CREATE", s: "EMPOWER MEANING" } { t: "INFJ", s: "The Advocate // Gentle Resolve" }
] ]
} }
}; };
@@ -365,6 +401,7 @@
const origin = new Float32Array(CONFIG.particleCount * 3); const origin = new Float32Array(CONFIG.particleCount * 3);
const velocities = new Float32Array(CONFIG.particleCount * 3); const velocities = new Float32Array(CONFIG.particleCount * 3);
const sizes = new Float32Array(CONFIG.particleCount); const sizes = new Float32Array(CONFIG.particleCount);
const seeds = new Float32Array(CONFIG.particleCount);
const simplex = new SimplexNoise(); const simplex = new SimplexNoise();
@@ -386,34 +423,54 @@
// 白天粒子略大,增强可见度 // 白天粒子略大,增强可见度
sizes[i] = (Math.random() * 2.5 + 0.5) * (ENV.theme==='day'?1.3:1.0); sizes[i] = (Math.random() * 2.5 + 0.5) * (ENV.theme==='day'?1.3:1.0);
seeds[i] = Math.random();
} }
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1)); geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
geometry.setAttribute('seed', new THREE.BufferAttribute(seeds, 1));
const paletteA = ENV.theme==='day' ? new THREE.Color(0x6ec3ff) : new THREE.Color(0x00ffff);
const paletteB = ENV.theme==='day' ? new THREE.Color(0xffb4c8) : new THREE.Color(0xff7af3);
const material = new THREE.ShaderMaterial({ const material = new THREE.ShaderMaterial({
uniforms: { uniforms: {
scale: { value: window.innerHeight / 2 }, scale: { value: window.innerHeight / 2 },
baseColor: { value: CONFIG.colors.base }, baseColor: { value: CONFIG.colors.base },
activeColor: { value: CONFIG.colors.active }, activeColor: { value: CONFIG.colors.active },
mixVal: { value: 0.0 } mixVal: { value: 0.0 },
time: { value: 0.0 },
paletteA: { value: paletteA },
paletteB: { value: paletteB },
nebulaIntensity: { value: 0.0 }
}, },
vertexShader: ` vertexShader: `
attribute float size; attribute float size;
attribute float seed;
varying float vSeed;
void main() { void main() {
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = size * ( 500.0 / -mvPosition.z ); gl_PointSize = size * ( 500.0 / -mvPosition.z );
gl_Position = projectionMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition;
vSeed = seed;
} }
`, `,
fragmentShader: ` fragmentShader: `
uniform vec3 baseColor; uniform vec3 baseColor;
uniform vec3 activeColor; uniform vec3 activeColor;
uniform vec3 paletteA;
uniform vec3 paletteB;
uniform float mixVal; uniform float mixVal;
uniform float time;
uniform float nebulaIntensity;
varying float vSeed;
void main() { void main() {
float r = length(gl_PointCoord - vec2(0.5)); float r = length(gl_PointCoord - vec2(0.5));
if (r > 0.5) discard; if (r > 0.5) discard;
vec3 finalColor = mix(baseColor, activeColor, mixVal); vec3 baseMix = mix(baseColor, activeColor, mixVal);
float drift = 0.5 + 0.5 * sin(time * 0.15 + vSeed * 6.28318);
vec3 nebula = mix(paletteA, paletteB, drift);
vec3 finalColor = mix(baseMix, nebula, nebulaIntensity);
gl_FragColor = vec4(finalColor, 1.0); gl_FragColor = vec4(finalColor, 1.0);
} }
`, `,
@@ -447,6 +504,12 @@
// 颜色插值 // 颜色插值
material.uniforms.mixVal.value += (targetMix - material.uniforms.mixVal.value) * 0.1; material.uniforms.mixVal.value += (targetMix - material.uniforms.mixVal.value) * 0.1;
material.uniforms.time.value = time;
if (ENV.theme === 'night') {
material.uniforms.nebulaIntensity.value = APP_STATE.mode === 'UNLOCKED' ? 0.55 : 0.35;
} else {
material.uniforms.nebulaIntensity.value = APP_STATE.mode === 'UNLOCKED' ? 0.22 : 0.12;
}
// 1. 形状更新 (锁定模式:呼吸球) // 1. 形状更新 (锁定模式:呼吸球)
if (APP_STATE.mode === 'LOCKED') { if (APP_STATE.mode === 'LOCKED') {
@@ -559,12 +622,15 @@
window.exitArchive = function() { window.exitArchive = function() {
APP_STATE.mode = 'LOCKED'; APP_STATE.mode = 'LOCKED';
APP_STATE.exitCooldownUntil = Date.now() + 2000;
document.getElementById('main-hint').style.display = 'block'; document.getElementById('main-hint').style.display = 'block';
document.getElementById('sub-hint').style.display = 'block'; document.getElementById('sub-hint').style.display = 'block';
safeClass('exit-btn', 'remove', 'visible'); safeClass('exit-btn', 'remove', 'visible');
safeClass('narrative-layer', 'remove', 'show-text'); safeClass('narrative-layer', 'remove', 'show-text');
APP_STATE.unlockProgress = 0;
safeUpdateText('main-hint', CONTENT.hints.main);
clearTimeout(narrativeTimer); clearTimeout(narrativeTimer);
explode(50); explode(50);
} }
@@ -621,7 +687,7 @@
* ============================================================================ * ============================================================================
*/ */
const hands = new Hands({locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`}); const hands = new Hands({locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`});
hands.setOptions({ maxNumHands: 2, modelComplexity: 1, minDetectionConfidence: 0.7, minTrackingConfidence: 0.7 }); hands.setOptions({ maxNumHands: 2, modelComplexity: 1, minDetectionConfidence: 0.85, minTrackingConfidence: 0.85 });
hands.onResults(results => { hands.onResults(results => {
// Loader logic // Loader logic
@@ -658,22 +724,34 @@
} }
// 合十检测 // 合十检测
if (landmarks.length === 2 && APP_STATE.mode === 'LOCKED') { if (landmarks.length === 2 && APP_STATE.mode === 'LOCKED' && Date.now() > APP_STATE.exitCooldownUntil) {
const w1=landmarks[0][0]; const w2=landmarks[1][0]; const w1 = landmarks[0][0], w2 = landmarks[1][0];
const t1=landmarks[0][12]; const t2=landmarks[1][12]; const i1 = landmarks[0][8], i2 = landmarks[1][8];
if (Math.hypot(w1.x-w2.x, w1.y-w2.y)<0.25 && Math.hypot(t1.x-t2.x, t1.y-t2.y)<0.2) { const m1 = landmarks[0][12], m2 = landmarks[1][12];
APP_STATE.unlockProgress++; const dW = Math.hypot(w1.x - w2.x, w1.y - w2.y);
safeUpdateText('main-hint', `${CONTENT.hints.unlocking} ${APP_STATE.unlockProgress}%`); const dI = Math.hypot(i1.x - i2.x, i1.y - i2.y);
if(APP_STATE.unlockProgress > 50) enterArchive(); const dM = Math.hypot(m1.x - m2.x, m1.y - m2.y);
const okW = dW < 0.28;
const okI = dI < 0.20;
const okM = dM < 0.20;
const ok = ((okW?1:0) + (okI?1:0) + (okM?1:0)) >= 2;
if (ok) {
APP_STATE.namasteStableFrames++;
} else { } else {
APP_STATE.unlockProgress = 0; APP_STATE.namasteStableFrames = Math.max(0, APP_STATE.namasteStableFrames - 1);
safeUpdateText('main-hint', CONTENT.hints.main);
} }
const targetFrames = 20;
const pct = Math.min(100, Math.round(APP_STATE.namasteStableFrames / targetFrames * 100));
if (pct > 0) safeUpdateText('main-hint', `${CONTENT.hints.unlocking} ${pct}%`);
if (APP_STATE.namasteStableFrames >= targetFrames) enterArchive();
if (!ok && APP_STATE.namasteStableFrames === 0) safeUpdateText('main-hint', CONTENT.hints.main);
} }
} else { } else {
APP_STATE.handCount = 0; APP_STATE.handCount = 0;
safeUpdateText('sys-status', 'SEARCHING...'); safeUpdateText('sys-status', 'SEARCHING...');
if(sysStatus) sysStatus.style.color = 'inherit'; if(sysStatus) sysStatus.style.color = 'inherit';
APP_STATE.unlockProgress = 0;
safeUpdateText('main-hint', CONTENT.hints.main);
} }
}); });