Files
aibis-dream/web-prototype/emotion-lab.html
T
2025-12-11 18:27:51 +08:00

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>