独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>微观机械原理3D极慢速自锁透视动画</title>
<style>
:root {
--bg-color: #050914;
--grid-color: rgba(30, 58, 138, 0.2);
--shell-normal: #334155;
--shell-xray: rgba(0, 229, 255, 0.1);
--shell-stroke-xray: #00e5ff;
--steel-color: #64748b;
--mech-primary: #f59e0b;
--mech-secondary: #ef4444;
--hud-text: #38bdf8;
--force-glow: #39ff14;
}
body, html {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
background-color: var(--bg-color);
background-image:
linear-gradient(var(--grid-color) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
background-size: 40px 40px;
background-position: center center;
overflow: hidden;
font-family: 'Courier New', Courier, monospace;
display: flex;
justify-content: center;
align-items: center;
}
/* 扫描线特效 */
.scanline {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(to bottom, rgba(255,255,255,0), rgba(255,255,255,0) 50%, rgba(0, 229, 255, 0.05) 50%, rgba(0, 229, 255, 0.05));
background-size: 100% 4px;
pointer-events: none;
z-index: 10;
}
/* HUD - 绝对边缘与角落布局,字体极小,绝不遮挡中心 */
.hud-panel {
position: absolute;
color: var(--hud-text);
font-size: 12px;
letter-spacing: 1px;
z-index: 20;
opacity: 0.8;
pointer-events: none;
text-transform: uppercase;
}
.hud-top-left { top: 20px; left: 20px; }
.hud-top-right { top: 20px; right: 20px; text-align: right; }
.hud-bottom-left { bottom: 20px; left: 20px; }
.hud-bottom-right { bottom: 20px; right: 20px; text-align: right; }
.hud-value {
color: #fff;
font-weight: bold;
text-shadow: 0 0 5px var(--hud-text);
}
.hud-alert {
color: var(--mech-secondary);
animation: pulse-alert 1s infinite;
display: none;
}
@keyframes pulse-alert {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; text-shadow: 0 0 8px var(--mech-secondary); }
}
/* 核心动画容器 */
#animation-viewport {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
/* 初始宏观视角 */
transform: scale(0.35) translateY(100px);
transition: transform 0.8s cubic-bezier(0.8, 0, 0.2, 1);
will-change: transform;
}
/* 震动特效 */
@keyframes camera-shake {
0% { transform: scale(1.6) translate(0, 0); }
20% { transform: scale(1.6) translate(-5px, 3px); }
40% { transform: scale(1.6) translate(4px, -4px); }
60% { transform: scale(1.6) translate(-3px, 5px); }
80% { transform: scale(1.6) translate(5px, -2px); }
100% { transform: scale(1.6) translate(0, 0); }
}
/* SVG 样式 */
svg {
width: 1000px;
height: 1000px;
overflow: visible;
}
/* 部件默认与过渡样式 */
.tool-assembly {
transform: translateY(800px);
transition: transform 0.3s cubic-bezier(0.1, 1, 0.1, 1);
}
.shell {
fill: var(--shell-normal);
stroke: #1e293b;
stroke-width: 2;
transition: all 1s ease-in-out;
}
.internal-mech {
opacity: 0;
transition: opacity 1s ease-in-out;
}
.eccentric-wheel {
transform-origin: 580px 480px; /* 偏心轴点 */
transform: rotate(0deg);
fill: var(--mech-primary);
stroke: #fff;
stroke-width: 2;
}
.locking-tongue {
transform-origin: 580px 600px;
transform: rotate(-15deg);
fill: var(--mech-primary);
}
.spring {
transform-origin: center bottom;
stroke: #cbd5e1;
stroke-width: 6;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.force-arrow {
opacity: 0;
fill: var(--force-glow);
filter: drop-shadow(0 0 8px var(--force-glow));
}
.spark {
opacity: 0;
fill: #fff;
}
/*=========================================
动画阶段状态类 (由 JS 切换)
=========================================*/
/* 1. 接近瞬间 */
.phase-approach .tool-assembly {
transform: translateY(0);
}
/* 2. 镜头拉近与撞击震动 */
.phase-zoom #animation-viewport {
transform: scale(1.6) translateY(20px);
animation: camera-shake 0.3s cubic-bezier(.36,.07,.19,.97) both;
}
/* 3. X射线透视模式开启 */
.phase-xray .shell {
fill: var(--shell-xray);
stroke: var(--shell-stroke-xray);
stroke-width: 3;
filter: drop-shadow(0 0 15px rgba(0, 229, 255, 0.4));
}
.phase-xray .internal-mech {
opacity: 1;
}
/* 4. 超慢动作咬合 (120fps 慢放视觉) */
.phase-slowmo .eccentric-wheel {
transform: rotate(-55deg);
transition: transform 3.5s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.phase-slowmo .locking-tongue {
transform: rotate(0deg);
transition: transform 3.5s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.phase-slowmo .spring-compress {
/* 模拟弹簧压缩 */
transform: translateY(5px) scaleY(0.7);
transition: transform 3.5s cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* 5. 锁定与力学矢量展示 */
.phase-force .force-arrow {
opacity: 1;
transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
/* 箭头从中心往外推的动画 */
}
.phase-force .arrow-1 { transform: translate(-10px, 10px); }
.phase-force .arrow-2 { transform: translate(15px, 0); }
.phase-force .spark {
animation: spark-burst 0.5s ease-out forwards;
}
@keyframes spark-burst {
0% { opacity: 1; transform: scale(0.5) translate(0,0); }
100% { opacity: 0; transform: scale(2) translate(var(--tx), var(--ty)); }
}
.phase-force .hud-alert {
display: block;
}
</style>
</head>
<body>
<div class="scanline"></div>
<!-- 极简HUD,置于绝对角落 -->
<div class="hud-panel hud-top-left">
<div>System: <span class="hud-value">AUTO-LOCKING RIG</span></div>
<div>Cam Mode: <span class="hud-value" id="hud-cam">MACRO_WIDE</span></div>
<div style="margin-top: 10px; color: #00e5ff;">[IFR_ACHIEVED: DIRECT_ENGAGEMENT]</div>
</div>
<div class="hud-panel hud-top-right">
<div>Capture Rate: <span class="hud-value" id="hud-fps">24 FPS</span></div>
<div>Material Alpha: <span class="hud-value" id="hud-alpha">1.00</span></div>
</div>
<div class="hud-panel hud-bottom-left">
<div class="hud-alert" id="hud-alert">>>> LOCKING SEQUENCE INITIATED <<<</div>
<div style="margin-top: 5px;">TIME DILATION: <span class="hud-value" id="hud-time">1.0x</span></div>
</div>
<div class="hud-panel hud-bottom-right">
<div>Tension Force: <span class="hud-value" id="hud-force">0.0 kN</span></div>
<div>Status: <span class="hud-value" id="hud-status" style="color: #94a3b8;">APPROACHING</span></div>
</div>
<!-- 核心视觉容器 -->
<div id="animation-viewport">
<svg viewBox="0 0 1000 1000" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="steel-texture" width="20" height="20" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="20" y2="20" stroke="#475569" stroke-width="1" opacity="0.3"/>
</pattern>
<filter id="glow-neon">
<feGaussianBlur stdDeviation="3" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<!-- 环境基准:角钢横担 (Target) -->
<g id="angle-steel" transform="translate(420, 250)">
<path d="M 0 0 h 60 v 250 h -60 z" fill="var(--steel-color)"/>
<path d="M 0 0 h 200 v 40 h -200 z" fill="var(--steel-color)"/>
<path d="M 0 0 h 60 v 250 h -60 z" fill="url(#steel-texture)"/>
<path d="M 0 0 h 200 v 40 h -200 z" fill="url(#steel-texture)"/>
<line x1="60" y1="40" x2="60" y2="250" stroke="#334155" stroke-width="4"/>
<line x1="60" y1="40" x2="200" y2="40" stroke="#334155" stroke-width="4"/>
</g>
<!-- 动态升降工具组 -->
<g class="tool-assembly">
<!-- 内部机械结构 (X射线透视后可见) -->
<g class="internal-mech">
<!-- 弹簧与底座 -->
<g transform="translate(580, 660)">
<rect x="-15" y="0" width="30" height="20" fill="#64748b" />
<path class="spring spring-compress" d="M 0 0 L 10 -20 L -10 -40 L 10 -60 L -10 -80 L 0 -100" />
</g>
<!-- 单向锁舌 (棘爪) -->
<path class="locking-tongue" d="M 570 610 L 610 590 L 600 550 L 560 550 Z" />
<circle cx="580" cy="600" r="8" fill="#1e293b"/>
<!-- 偏心轮与齿槽 (棘轮) -->
<g class="eccentric-wheel">
<!-- 偏心轮主体外廓 (咬合面) -->
<path d="M 580 480 C 450 480, 430 350, 480 320 C 530 290, 650 350, 660 450 C 660 510, 620 520, 580 480 Z" />
<!-- 棘轮齿 -->
<path d="M 640 450 L 660 440 L 650 420 L 670 410 L 660 390 L 680 380 L 660 360 L 670 350 L 640 330 Z" fill="var(--mech-primary)" stroke-linejoin="round"/>
<!-- 轴心与偏心距指示 -->
<circle cx="580" cy="480" r="12" fill="#0f172a" stroke="#fff" stroke-width="3"/>
<circle cx="520" cy="410" r="6" fill="#0f172a" stroke="#fff" stroke-width="2"/> <!-- 几何中心 -->
<line x1="580" y1="480" x2="520" y2="410" stroke="#00e5ff" stroke-width="2" stroke-dasharray="4,2"/>
</g>
</g>
<!-- 外壳 (透明度变化) -->
<path class="shell" d="M 400 700 L 400 350 L 460 350 L 460 600 C 460 620, 480 650, 520 650 L 680 650 L 680 300 L 720 300 L 720 700 Z"/>
<!-- 固定夹爪内垫片 -->
<rect x="440" y="360" width="20" height="80" fill="#94a3b8" />
<!-- 力学分析效果层 (透明) -->
<g class="force-overlays">
<!-- 左侧反作用力箭头 -->
<g class="force-arrow arrow-1" transform="translate(460, 400)">
<path d="M 0 0 L -30 -15 L -25 -5 L -50 -5 L -50 5 L -25 5 L -30 15 Z" />
<text x="-65" y="4" fill="var(--force-glow)" font-size="14" font-weight="bold" filter="url(#glow-neon)">Fn</text>
</g>
<!-- 右侧偏心压紧力箭头 -->
<g class="force-arrow arrow-2" transform="translate(480, 400)">
<path d="M 0 0 L 30 -15 L 25 -5 L 50 -5 L 50 5 L 25 5 L 30 15 Z" />
<text x="55" y="4" fill="var(--force-glow)" font-size="14" font-weight="bold" filter="url(#glow-neon)">Fc</text>
</g>
<!-- 咬合瞬间火花特效 -->
<g transform="translate(470, 400)">
<circle class="spark" cx="0" cy="0" r="3" style="--tx: -20px; --ty: -30px;"/>
<circle class="spark" cx="0" cy="0" r="2" style="--tx: -30px; --ty: 10px;"/>
<circle class="spark" cx="0" cy="0" r="4" style="--tx: 10px; --ty: -40px;"/>
<circle class="spark" cx="0" cy="0" r="2" style="--tx: 25px; --ty: -20px;"/>
</g>
</g>
</g>
</svg>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const body = document.body;
const uiCam = document.getElementById('hud-cam');
const uiFps = document.getElementById('hud-fps');
const uiAlpha = document.getElementById('hud-alpha');
const uiTime = document.getElementById('hud-time');
const uiForce = document.getElementById('hud-force');
const uiStatus = document.getElementById('hud-status');
// 动画状态机流水线
const timeline = [
{ time: 500, action: () => {
body.classList.add('phase-approach');
uiStatus.innerText = 'IMPACT_IMMINENT';
uiStatus.style.color = '#f59e0b';
}},
{ time: 900, action: () => {
body.classList.add('phase-zoom');
uiCam.innerText = 'MICRO_TRACKING';
uiCam.style.color = '#00e5ff';
uiStatus.innerText = 'CONTACT';
}},
{ time: 1400, action: () => {
body.classList.add('phase-xray');
uiAlpha.innerText = '0.30';
uiAlpha.style.color = '#00e5ff';
}},
{ time: 2000, action: () => {
body.classList.add('phase-slowmo');
uiFps.innerText = '120 FPS';
uiFps.style.color = '#00e5ff';
uiTime.innerText = '0.01x (SUPER SLOW)';
uiTime.style.color = '#f59e0b';
uiStatus.innerText = 'SELF_LOCKING...';
}},
{ time: 5500, action: () => {
body.classList.add('phase-force');
uiStatus.innerText = 'SECURED';
uiStatus.style.color = '#39ff14';
animateValue(uiForce, 0, 58.4, 800, ' kN');
}},
{ time: 9000, action: () => {
// 重置,形成无限循环
body.className = '';
uiCam.innerText = 'MACRO_WIDE';
uiCam.style.color = '#fff';
uiFps.innerText = '24 FPS';
uiFps.style.color = '#fff';
uiAlpha.innerText = '1.00';
uiAlpha.style.color = '#fff';
uiTime.innerText = '1.0x';
uiTime.style.color = '#fff';
uiStatus.innerText = 'APPROACHING';
uiStatus.style.color = '#94a3b8';
uiForce.innerText = '0.0 kN';
}}
];
// 数值滚动动画函数
function animateValue(obj, start, end, duration, suffix) {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = (progress * (end - start) + start).toFixed(1) + suffix;
if (progress < 1) {
window.requestAnimationFrame(step);
}
};
window.requestAnimationFrame(step);
}
// 循环控制
function runSequence() {
timeline.forEach(step => {
setTimeout(step.action, step.time);
});
}
// 加载完成后直接启动并循环
runSequence();
setInterval(runSequence, 10000); // 每10秒完整循环一次
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>弹簧效应 - 阻尼谐振子动态物理模型</title>
<style>
:root {
--bg-base: #030712;
--grid-color: rgba(14, 165, 233, 0.1);
--hud-color: #38bdf8;
--hud-alert: #f43f5e;
--hud-text-size: 11px;
--spring-rest: #0ea5e9;
--spring-max: #ef4444;
--metal-dark: #1e293b;
--metal-light: #475569;
}
body, html {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
background-color: var(--bg-base);
background-image:
radial-gradient(circle at center, rgba(14, 165, 233, 0.05) 0%, transparent 70%),
linear-gradient(var(--grid-color) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
background-size: 100% 100%, 50px 50px, 50px 50px;
background-position: center center;
overflow: hidden;
font-family: 'SF Pro Display', 'Roboto Mono', monospace;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
}
/* 扫描线与暗角 */
.vignette {
position: absolute;
inset: 0;
background: radial-gradient(circle at center, transparent 40%, rgba(0,0,0,0.8) 100%);
pointer-events: none;
z-index: 5;
}
.scanline {
position: absolute;
inset: 0;
background: linear-gradient(to bottom, transparent 50%, rgba(14, 165, 233, 0.03) 51%);
background-size: 100% 4px;
pointer-events: none;
z-index: 6;
}
/* HUD 系统 - 极端边角布局,超小字体,严禁遮挡中心 */
.hud-container {
position: absolute;
z-index: 20;
font-size: var(--hud-text-size);
letter-spacing: 1.5px;
text-transform: uppercase;
opacity: 0.85;
pointer-events: none;
text-shadow: 0 0 4px rgba(56, 189, 248, 0.5);
}
.hud-tl { top: 20px; left: 24px; }
.hud-tr { top: 20px; right: 24px; text-align: right; }
.hud-bl { bottom: 20px; left: 24px; }
.hud-br { bottom: 20px; right: 24px; text-align: right; }
.hud-title {
color: var(--hud-color);
font-weight: 700;
margin-bottom: 6px;
font-size: 12px;
border-bottom: 1px solid rgba(56, 189, 248, 0.3);
padding-bottom: 4px;
}
.hud-row {
margin-bottom: 4px;
display: flex;
justify-content: space-between;
gap: 16px;
}
.hud-val {
color: #fff;
font-family: monospace;
font-weight: bold;
}
/* 能量条 UI */
.energy-bar-bg {
width: 120px;
height: 4px;
background: rgba(255,255,255,0.1);
margin-top: 4px;
position: relative;
overflow: hidden;
}
.energy-bar-fill {
height: 100%;
width: 0%;
transition: width 0.1s linear, background-color 0.1s;
}
#bar-pe { background: var(--spring-max); box-shadow: 0 0 8px var(--spring-max); }
#bar-ke { background: var(--spring-rest); box-shadow: 0 0 8px var(--spring-rest); }
/* 核心动画视口 */
#viewport {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
svg {
width: 100%;
height: 100%;
max-width: 1200px;
max-height: 1200px;
overflow: visible;
}
/* SVG 元素样式 */
.mech-base {
fill: var(--metal-dark);
stroke: var(--metal-light);
stroke-width: 2;
}
.spring-wire {
fill: none;
stroke-width: 12;
stroke-linecap: round;
stroke-linejoin: round;
/* 发光效果通过内联样式结合 JS 动态修改 */
transition: stroke 0.1s ease-out;
filter: drop-shadow(0 0 10px currentColor);
}
.weight-block {
fill: #0f172a;
stroke: #cbd5e1;
stroke-width: 3;
}
.weight-core {
fill: rgba(14, 165, 233, 0.2);
stroke: var(--hud-color);
stroke-width: 1;
}
.force-vector {
fill: none;
stroke: var(--hud-alert);
stroke-width: 3;
marker-end: url(#arrow-head);
transition: opacity 0.2s;
opacity: 0;
}
.grid-line {
stroke: rgba(255,255,255,0.1);
stroke-width: 1;
stroke-dasharray: 4 4;
}
</style>
</head>
<body>
<div class="vignette"></div>
<div class="scanline"></div>
<!-- HUD Top Left -->
<div class="hud-container hud-tl">
<div class="hud-title">弹簧效应 // 阻尼谐振子动态模型</div>
<div class="hud-row"><span>SYS_STATUS:</span> <span class="hud-val" id="sys-status" style="color: var(--hud-color);">INITIALIZING</span></div>
<div class="hud-row"><span>PHASE:</span> <span class="hud-val" id="sys-phase">IDLE</span></div>
</div>
<!-- HUD Top Right -->
<div class="hud-container hud-tr">
<div class="hud-title">物理定律方程组 // KINEMATICS</div>
<div class="hud-row"><span>HOOKE'S LAW:</span> <span class="hud-val">F = -k·Δx</span></div>
<div class="hud-row"><span>DAMPING:</span> <span class="hud-val">Fd = -c·v</span></div>
<div class="hud-row"><span>CONSTANT (k):</span> <span class="hud-val">0.08 N/m</span></div>
<div class="hud-row"><span>DAMPING (c):</span> <span class="hud-val">0.975</span></div>
</div>
<!-- HUD Bottom Left -->
<div class="hud-container hud-bl">
<div class="hud-title">系统能量转化 // THERMODYNAMICS</div>
<div class="hud-row" style="display: block;">
<div style="display: flex; justify-content: space-between;">
<span>POTENTIAL ENG (Ep):</span> <span class="hud-val" id="val-pe">0.00 J</span>
</div>
<div class="energy-bar-bg"><div class="energy-bar-fill" id="bar-pe"></div></div>
</div>
<div class="hud-row" style="display: block; margin-top: 10px;">
<div style="display: flex; justify-content: space-between;">
<span>KINETIC ENG (Ek):</span> <span class="hud-val" id="val-ke">0.00 J</span>
</div>
<div class="energy-bar-bg"><div class="energy-bar-fill" id="bar-ke"></div></div>
</div>
</div>
<!-- HUD Bottom Right -->
<div class="hud-container hud-br">
<div class="hud-title">实时遥测数据 // TELEMETRY</div>
<div class="hud-row"><span>DISPLACEMENT (Δx):</span> <span class="hud-val" id="val-disp">+0.00 mm</span></div>
<div class="hud-row"><span>VELOCITY (v):</span> <span class="hud-val" id="val-vel">0.00 m/s</span></div>
<div class="hud-row"><span>RESTORING FORCE (F):</span> <span class="hud-val" id="val-force">0.00 N</span></div>
</div>
<!-- 核心 SVG 动画区 -->
<div id="viewport">
<svg viewBox="0 0 1000 1000" preserveAspectRatio="xMidYMid meet">
<defs>
<marker id="arrow-head" markerWidth="10" markerHeight="10" refX="5" refY="5" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--hud-alert)" />
</marker>
<pattern id="carbon" width="10" height="10" patternUnits="userSpaceOnUse">
<path d="M0 10L10 0ZM-1 1L1 -1ZM9 11L11 9Z" stroke="rgba(255,255,255,0.1)" stroke-width="1"/>
</pattern>
<filter id="glow-intense">
<feGaussianBlur stdDeviation="5" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<!-- 背景刻度标尺 -->
<g transform="translate(650, 0)">
<line x1="0" y1="200" x2="0" y2="900" class="grid-line" />
<line x1="-10" y1="500" x2="20" y2="500" stroke="#0ea5e9" stroke-width="2" />
<text x="30" y="504" fill="#0ea5e9" font-size="12" font-family="monospace">ORIGIN (0)</text>
<line x1="0" y1="300" x2="10" y2="300" class="grid-line" />
<text x="20" y="304" fill="#64748b" font-size="10">-200</text>
<line x1="0" y1="700" x2="10" y2="700" class="grid-line" />
<text x="20" y="704" fill="#64748b" font-size="10">+200</text>
<line x1="0" y1="900" x2="10" y2="900" class="grid-line" />
<text x="20" y="904" fill="#64748b" font-size="10">+400</text>
</g>
<!-- 顶部固定基座 -->
<g transform="translate(500, 100)">
<rect x="-80" y="0" width="160" height="40" rx="5" class="mech-base" fill="url(#carbon)" />
<path d="M -40 40 L -20 80 L 20 80 L 40 40 Z" class="mech-base" />
<circle cx="0" cy="70" r="8" fill="#030712" stroke="#475569" stroke-width="2" />
</g>
<!-- 动态弹簧路径 -->
<path id="spring-wire" class="spring-wire" d="" stroke="#0ea5e9" />
<!-- 动态配重块及受力箭头 -->
<g id="weight-group" transform="translate(500, 500)">
<!-- 恢复力矢量箭头 -->
<line id="force-arrow" x1="90" y1="0" x2="90" y2="0" class="force-vector" />
<!-- 速度矢量箭头 (向左偏一点避免重叠) -->
<line id="vel-arrow" x1="-90" y1="0" x2="-90" y2="0" class="force-vector" stroke="#0ea5e9" />
<!-- 挂钩处 -->
<path d="M 0 -20 L 0 -50" stroke="#94a3b8" stroke-width="6" fill="none" />
<!-- 配重主体 -->
<rect x="-60" y="-20" width="120" height="80" rx="10" class="weight-block" fill="url(#carbon)"/>
<!-- 发光核心/传感器显示 -->
<rect x="-40" y="0" width="80" height="40" rx="4" class="weight-core" />
<circle cx="-20" cy="20" r="4" fill="#38bdf8" id="sensor-led" filter="url(#glow-intense)"/>
<line x1="0" y1="10" x2="0" y2="30" stroke="rgba(255,255,255,0.2)" stroke-width="2" />
<line x1="10" y1="12" x2="10" y2="28" stroke="rgba(255,255,255,0.2)" stroke-width="2" />
<line x1="20" y1="15" x2="20" y2="25" stroke="rgba(255,255,255,0.2)" stroke-width="2" />
<!-- 当前刻度指示器 -->
<path d="M 60 20 L 75 20 L 80 15 L 80 25 Z" fill="#0ea5e9" />
<line x1="75" y1="20" x2="140" y2="20" stroke="#0ea5e9" stroke-width="1" stroke-dasharray="2 2" />
</g>
</svg>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// DOM 元素缓存
const springEl = document.getElementById('spring-wire');
const weightEl = document.getElementById('weight-group');
const forceArrowEl = document.getElementById('force-arrow');
const velArrowEl = document.getElementById('vel-arrow');
const sensorLed = document.getElementById('sensor-led');
// HUD 元素
const statusEl = document.getElementById('sys-status');
const phaseEl = document.getElementById('sys-phase');
const dispEl = document.getElementById('val-disp');
const velEl = document.getElementById('val-vel');
const forceEl = document.getElementById('val-force');
const peEl = document.getElementById('val-pe');
const keEl = document.getElementById('val-ke');
const barPe = document.getElementById('bar-pe');
const barKe = document.getElementById('bar-ke');
// 物理常量与变量
const yTop = 180; // 弹簧顶部固定Y坐标
const restY = 500; // 平衡位置 (物块中心Y坐标)
const k = 0.08; // 弹簧劲度系数
const damping = 0.985; // 阻尼系数 (1.0为无阻尼)
const mass = 1.0; // 质量
let currentY = restY;
let velocityY = 0;
// 状态机: 0: 等待, 1: 下拉蓄能, 2: 释放谐振
let state = 0;
let pullTargetY = 880; // 最大拉伸位置
let pullProgress = 0;
// 颜色插值函数 (基于形变程度,将颜色从冷蓝变为炽红)
function getStressColor(displacement, maxDisp = 400) {
const ratio = Math.min(Math.abs(displacement) / maxDisp, 1);
// 蓝(14, 165, 233) -> 红(239, 68, 68)
const r = Math.round(14 + ratio * (239 - 14));
const g = Math.round(165 + ratio * (68 - 165));
const b = Math.round(233 + ratio * (68 - 233));
return `rgb(${r}, ${g}, ${b})`;
}
// 生成3D质感折线弹簧路径
function generateSpringPath(yBottom) {
const coils = 16;
const width = 45;
const centerX = 500;
// 从顶部连接点开始
let path = `M ${centerX} ${yTop} L ${centerX} ${yTop + 20} `;
const springHeight = (yBottom - 40) - (yTop + 20);
const step = springHeight / coils;
for (let i = 0; i <= coils; i++) {
const py = (yTop + 20) + (i * step);
// 交替左右形成螺旋视觉
const px = i % 2 === 0 ? centerX + width : centerX - width;
// 使用二次贝塞尔曲线让拐角圆滑,增加工业机械感
if (i === 0) {
path += `L ${px} ${py} `;
} else {
// 计算控制点以平滑过渡
path += `L ${px} ${py} `;
}
}
// 连接到底部挂钩
path += `L ${centerX} ${yBottom - 20} L ${centerX} ${yBottom}`;
return path;
}
// 主渲染与物理循环
function render() {
// 物理状态更新
if (state === 1) {
// 下拉蓄能阶段 (强制匀速拉伸)
pullProgress += 0.005;
// 使用 easeInOut 曲线让下拉显得有阻力且沉重
const ease = pullProgress < 0.5 ? 2 * pullProgress * pullProgress : -1 + (4 - 2 * pullProgress) * pullProgress;
currentY = restY + (pullTargetY - restY) * ease;
if (pullProgress >= 1) {
state = 2; // 达到最大拉力,释放
velocityY = 0;
phaseEl.innerText = "RELEASE -> HARMONIC OSCILLATION";
phaseEl.style.color = "#39ff14"; // 绿色
statusEl.innerText = "ACTIVE_SIMULATION";
statusEl.style.color = "#39ff14";
forceArrowEl.style.opacity = 1;
velArrowEl.style.opacity = 1;
}
} else if (state === 2) {
// 释放后的阻尼谐振物理引擎 (胡克定律 + 阻尼)
const displacement = currentY - restY;
const restoringForce = -k * displacement;
// a = F / m
const acceleration = restoringForce / mass;
// v = v + a
velocityY += acceleration;
// v = v * damping
velocityY *= damping;
// y = y + v
currentY += velocityY;
// 判断能量衰减殆尽,重置循环
if (Math.abs(displacement) < 1 && Math.abs(velocityY) < 0.1) {
state = 0;
setTimeout(() => {
state = 1;
pullProgress = 0;
phaseEl.innerText = "POTENTIAL ENERGY BUILDUP";
phaseEl.style.color = "#ef4444"; // 红色
statusEl.innerText = "EXTERNAL_FORCE_APPLIED";
statusEl.style.color = "#ef4444";
forceArrowEl.style.opacity = 0;
velArrowEl.style.opacity = 0;
}, 2000); // 停顿2秒后重新开始
}
}
// 核心变量计算
const displacement = currentY - restY;
const force = -k * displacement;
const pe = 0.5 * k * displacement * displacement; // 势能 Ep = 1/2 k x^2
const ke = 0.5 * mass * velocityY * velocityY; // 动能 Ek = 1/2 m v^2
// 视觉更新
// 1. 更新配重位置
weightEl.setAttribute('transform', `translate(500, ${currentY})`);
// 2. 更新弹簧路径与颜色
springEl.setAttribute('d', generateSpringPath(currentY - 20)); // -20是挂钩偏移
const stressColor = getStressColor(displacement);
springEl.style.stroke = stressColor;
sensorLed.style.fill = stressColor;
// 3. 动态更新力与速度矢量箭头 (仅在震荡阶段显示显著)
if (state === 2) {
// 力的方向与位移相反
forceArrowEl.setAttribute('y2', force * 3); // 视觉放大系数
// 速度方向
velArrowEl.setAttribute('y2', velocityY * 5); // 视觉放大系数
}
// 4. 更新 HUD 数据 (节流减少DOM操作开销,每帧更新纯数值)
const sign = displacement >= 0 ? '+' : '';
dispEl.innerText = `${sign}${displacement.toFixed(2)} mm`;
dispEl.style.color = stressColor;
const velSign = velocityY >= 0 ? '+' : '';
velEl.innerText = `${velSign}${velocityY.toFixed(2)} m/s`;
const forceSign = force >= 0 ? '+' : '';
forceEl.innerText = `${forceSign}${force.toFixed(2)} N`;
peEl.innerText = `${pe.toFixed(2)} J`;
keEl.innerText = `${ke.toFixed(2)} J`;
// 能量条宽度更新 (假定最大能量为 6500)
const maxEng = 6500;
barPe.style.width = `${Math.min((pe / maxEng) * 100, 100)}%`;
barPe.style.backgroundColor = stressColor;
barPe.style.boxShadow = `0 0 8px ${stressColor}`;
barKe.style.width = `${Math.min((ke / maxEng) * 100, 100)}%`;
// 循环渲染
requestAnimationFrame(render);
}
// 启动序列
setTimeout(() => {
state = 1; // 开始下拉
phaseEl.innerText = "POTENTIAL ENERGY BUILDUP";
phaseEl.style.color = "#ef4444";
statusEl.innerText = "EXTERNAL_FORCE_APPLIED";
statusEl.style.color = "#ef4444";
}, 1000);
// 启动物理引擎
render();
});
</script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
