353 lines
12 KiB
HTML
353 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>HuoShan Emotion Electrolyte Module</title>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
padding: 0;
|
|
background-color: #050505;
|
|
color: #fff;
|
|
font-family: 'Courier New', Courier, monospace;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100vh;
|
|
}
|
|
|
|
/*
|
|
核心视觉魔法:
|
|
通过高对比度和模糊滤镜,让独立的圆球看起来像融合的液体
|
|
*/
|
|
#canvas-container {
|
|
position: relative;
|
|
width: 800px;
|
|
height: 600px;
|
|
background: #000;
|
|
border: 2px solid #333;
|
|
box-shadow: 0 0 20px rgba(0, 0, 0, 0.8) inset;
|
|
/* 这种滤镜组合创造了"Metaball"液体效果 */
|
|
filter: contrast(15);
|
|
background-color: black;
|
|
}
|
|
|
|
/*
|
|
由于父容器用了contrast滤镜,背景必须是黑的,
|
|
我们在里面放一个不受滤镜影响的UI层在外面,或者调整层级。
|
|
这里为了简单,Canvas产生的内容会被液体化。
|
|
*/
|
|
canvas {
|
|
filter: blur(8px); /* 配合父级的 contrast 产生融合效果 */
|
|
background: #000;
|
|
}
|
|
|
|
.ui-panel {
|
|
margin-top: 20px;
|
|
z-index: 10;
|
|
background: rgba(20, 20, 20, 0.8);
|
|
padding: 20px;
|
|
border: 1px solid #444;
|
|
border-radius: 4px;
|
|
text-align: center;
|
|
}
|
|
|
|
button {
|
|
background: #333;
|
|
color: #fff;
|
|
border: 1px solid #666;
|
|
padding: 10px 20px;
|
|
margin: 0 10px;
|
|
cursor: pointer;
|
|
font-family: inherit;
|
|
font-size: 14px;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
button:hover {
|
|
background: #555;
|
|
border-color: #888;
|
|
}
|
|
|
|
button.active {
|
|
background: #004400;
|
|
border-color: #00ff00;
|
|
box-shadow: 0 0 10px #00ff00;
|
|
}
|
|
|
|
button#btn-anxiety.active {
|
|
background: #440044;
|
|
border-color: #ff00ff;
|
|
box-shadow: 0 0 10px #ff00ff;
|
|
}
|
|
|
|
.status-text {
|
|
margin-bottom: 10px;
|
|
color: #aaa;
|
|
font-size: 12px;
|
|
}
|
|
|
|
/* 模拟玻璃容器的覆盖层,不受液体滤镜影响 */
|
|
.glass-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
pointer-events: none;
|
|
background: linear-gradient(135deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0) 40%, rgba(255,255,255,0) 60%, rgba(255,255,255,0.02) 100%);
|
|
box-shadow: inset 0 0 50px rgba(0,0,0,0.9);
|
|
border: 1px solid rgba(255,255,255,0.1);
|
|
z-index: 5;
|
|
}
|
|
|
|
.electrode {
|
|
position: absolute;
|
|
width: 20px;
|
|
height: 60px;
|
|
background: #222;
|
|
border: 1px solid #555;
|
|
z-index: 6;
|
|
}
|
|
.elec-left { left: -10px; top: 50%; transform: translateY(-50%); }
|
|
.elec-right { right: -10px; top: 50%; transform: translateY(-50%); }
|
|
.elec-bottom { bottom: -10px; left: 50%; transform: translateX(-50%) rotate(90deg); }
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div id="canvas-container">
|
|
<canvas id="liquidCanvas"></canvas>
|
|
<div class="glass-overlay"></div>
|
|
<div class="electrode elec-left"></div>
|
|
<div class="electrode elec-right"></div>
|
|
<div class="electrode elec-bottom"></div>
|
|
</div>
|
|
|
|
<div class="ui-panel">
|
|
<div class="status-text" id="status-display">等待指令...</div>
|
|
<button id="btn-anxiety" onclick="setMode('anxiety')">状态:掩盖焦虑 (Jokes/Stutter)</button>
|
|
<button id="btn-heal" onclick="setMode('heal')">状态:真情流露 (Truth/Release)</button>
|
|
</div>
|
|
|
|
<script>
|
|
const canvas = document.getElementById('liquidCanvas');
|
|
const ctx = canvas.getContext('2d');
|
|
const statusDisplay = document.getElementById('status-display');
|
|
|
|
let width, height;
|
|
let particles = [];
|
|
let bubbles = [];
|
|
|
|
// 当前模式
|
|
let currentMode = 'idle'; // 'anxiety', 'heal'
|
|
|
|
// 初始化画布大小
|
|
function resize() {
|
|
width = 800;
|
|
height = 600;
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
}
|
|
resize();
|
|
|
|
class Particle {
|
|
constructor(x, y, type) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.type = type; // 'anxiety', 'joke', 'relief'
|
|
this.vx = (Math.random() - 0.5) * 2;
|
|
this.vy = (Math.random() - 0.5) * 2;
|
|
this.size = Math.random() * 20 + 10;
|
|
this.life = 1.0;
|
|
this.decay = Math.random() * 0.005 + 0.002;
|
|
|
|
// 物理属性
|
|
this.friction = 0.95;
|
|
this.gravity = 0;
|
|
}
|
|
|
|
update() {
|
|
// 基础物理
|
|
this.x += this.vx;
|
|
this.y += this.vy;
|
|
this.vx *= this.friction;
|
|
this.vy *= this.friction;
|
|
this.life -= this.decay;
|
|
|
|
if (currentMode === 'anxiety') {
|
|
this.updateAnxietyBehavior();
|
|
} else if (currentMode === 'heal') {
|
|
this.updateHealBehavior();
|
|
}
|
|
}
|
|
|
|
updateAnxietyBehavior() {
|
|
// 焦虑粒子:深色,沉重,试图下沉但被搅动
|
|
if (this.type === 'anxiety') {
|
|
this.gravity = 0.05;
|
|
this.vy += this.gravity;
|
|
|
|
// 受到"笑话"粒子的冲击,产生随机抖动
|
|
this.vx += (Math.random() - 0.5) * 1.5;
|
|
this.vy += (Math.random() - 0.5) * 1.5;
|
|
|
|
// 限制在画布中心区域,像被困住
|
|
const centerX = width / 2;
|
|
const centerY = height / 2;
|
|
const dx = this.x - centerX;
|
|
const dy = this.y - centerY;
|
|
const dist = Math.sqrt(dx*dx + dy*dy);
|
|
|
|
if (dist > 150) {
|
|
this.vx -= dx * 0.005;
|
|
this.vy -= dy * 0.005;
|
|
}
|
|
}
|
|
|
|
// 笑话粒子:高频,快速,像电火花
|
|
if (this.type === 'joke') {
|
|
this.size = Math.random() * 15 + 5; // 忽大忽小
|
|
this.vx += (Math.random() - 0.5) * 4; // 极其疯狂的速度
|
|
this.vy += (Math.random() - 0.5) * 4;
|
|
|
|
// 围绕中心旋转/攻击
|
|
const centerX = width / 2;
|
|
const centerY = height / 2;
|
|
// 简单的向心力,但也保持高速度
|
|
this.vx -= (this.x - centerX) * 0.02;
|
|
this.vy -= (this.y - centerY) * 0.02;
|
|
}
|
|
}
|
|
|
|
updateHealBehavior() {
|
|
// 治愈状态:所有粒子都变得轻盈,向上升腾
|
|
this.gravity = -0.05; // 向上浮力
|
|
this.vy += this.gravity;
|
|
|
|
// 减少水平抖动,变成层流
|
|
this.vx *= 0.9;
|
|
|
|
// 颜色逐渐转变(通过重新生成或视觉欺骗,这里简化为让它们飘走)
|
|
}
|
|
|
|
draw() {
|
|
ctx.beginPath();
|
|
ctx.arc(this.x, this.y, this.size * this.life, 0, Math.PI * 2);
|
|
|
|
if (this.type === 'anxiety') {
|
|
// 深紫色/暗红色 - 沉重的情绪
|
|
ctx.fillStyle = `rgba(100, 0, 50, ${this.life})`;
|
|
} else if (this.type === 'joke') {
|
|
// 亮黄色/白色 - 虚假的掩饰,高光
|
|
ctx.fillStyle = `rgba(255, 255, 0, ${this.life})`;
|
|
} else if (this.type === 'relief') {
|
|
// 青蓝色 - 真实的平静
|
|
ctx.fillStyle = `rgba(0, 200, 255, ${this.life})`;
|
|
} else if (this.type === 'converted') {
|
|
// 转化后的物质 - 柔和的粉/橙
|
|
ctx.fillStyle = `rgba(255, 150, 100, ${this.life})`;
|
|
}
|
|
|
|
ctx.fill();
|
|
}
|
|
}
|
|
|
|
// 生成器
|
|
function spawnParticles() {
|
|
const centerX = width / 2;
|
|
const centerY = height / 2;
|
|
|
|
if (currentMode === 'anxiety') {
|
|
// 底部产生焦虑源
|
|
for(let i=0; i<2; i++) {
|
|
particles.push(new Particle(centerX + (Math.random()-0.5)*50, height - 100, 'anxiety'));
|
|
}
|
|
// 四周产生"笑话"干扰源
|
|
for(let i=0; i<4; i++) {
|
|
if (Math.random() > 0.5) {
|
|
particles.push(new Particle(
|
|
centerX + (Math.random()-0.5) * 300,
|
|
centerY + (Math.random()-0.5) * 300,
|
|
'joke'
|
|
));
|
|
}
|
|
}
|
|
} else if (currentMode === 'heal') {
|
|
// 底部产生清澈的物质
|
|
for(let i=0; i<3; i++) {
|
|
particles.push(new Particle(centerX + (Math.random()-0.5)*100, height + 20, 'relief'));
|
|
}
|
|
if (Math.random() > 0.8) {
|
|
particles.push(new Particle(centerX + (Math.random()-0.5)*150, height + 20, 'converted'));
|
|
}
|
|
}
|
|
}
|
|
|
|
function setMode(mode) {
|
|
currentMode = mode;
|
|
|
|
// 更新UI
|
|
document.querySelectorAll('button').forEach(b => b.classList.remove('active'));
|
|
if (mode === 'anxiety') {
|
|
document.getElementById('btn-anxiety').classList.add('active');
|
|
statusDisplay.innerHTML = "警告:检测到高频干扰波(笑话)。核心情绪(焦虑)压力积聚中。";
|
|
statusDisplay.style.color = "#ff5555";
|
|
} else {
|
|
document.getElementById('btn-heal').classList.add('active');
|
|
statusDisplay.innerHTML = "干扰解除。电解质成分重组。情绪释放,反应稳定。";
|
|
statusDisplay.style.color = "#55ffff";
|
|
|
|
// 转换现有的粒子
|
|
particles.forEach(p => {
|
|
if (p.type === 'anxiety') {
|
|
p.type = 'converted'; // 焦虑转化为释怀
|
|
p.vx *= 0.1; // 瞬间平静
|
|
p.vy *= 0.1;
|
|
}
|
|
if (p.type === 'joke') {
|
|
p.life = 0; // 笑话瞬间消失
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function animate() {
|
|
// 每一帧稍微清除一下画布,而不是完全清除,制造一点拖尾感(在液体里可能不需要)
|
|
ctx.clearRect(0, 0, width, height);
|
|
|
|
spawnParticles();
|
|
|
|
// 更新和绘制所有粒子
|
|
for (let i = particles.length - 1; i >= 0; i--) {
|
|
const p = particles[i];
|
|
p.update();
|
|
p.draw();
|
|
|
|
// 移除寿命结束的粒子
|
|
if (p.life <= 0.05 || p.y < -50 || p.x < -50 || p.x > width + 50 || p.y > height + 50) {
|
|
particles.splice(i, 1);
|
|
}
|
|
}
|
|
|
|
// 绘制电极发光效果(装饰)
|
|
if (currentMode === 'anxiety') {
|
|
ctx.globalCompositeOperation = 'source-over';
|
|
ctx.fillStyle = 'rgba(255, 255, 0, 0.1)';
|
|
ctx.fillRect(0, 0, width, height);
|
|
}
|
|
|
|
requestAnimationFrame(animate);
|
|
}
|
|
|
|
// 启动
|
|
setMode('anxiety'); // 默认进入问题状态
|
|
animate();
|
|
|
|
</script>
|
|
</body>
|
|
</html> |