独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>高保真绝缘机械手原理动画 - 最终理想解(IFR)</title>
<style>
:root {
--bg-color: #050914;
--grid-color: rgba(32, 178, 170, 0.1);
--primary-cyan: #00F0FF;
--accent-orange: #FF5E00;
--success-green: #39FF14;
--tech-gray: #4A5568;
--text-main: #E2E8F0;
--text-muted: #718096;
/* Animation Timings */
--total-duration: 12s;
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
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;
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
color: var(--text-main);
}
/* ---------------- Layout & HUD ---------------- */
.hud-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10;
}
.panel {
position: absolute;
background: rgba(5, 9, 20, 0.85);
border: 1px solid rgba(0, 240, 255, 0.3);
padding: 16px;
backdrop-filter: blur(4px);
}
.panel-top-left { top: 24px; left: 24px; max-width: 300px; border-left: 3px solid var(--primary-cyan); }
.panel-top-right { top: 24px; right: 24px; text-align: right; border-right: 3px solid var(--success-green); }
.panel-bottom { bottom: 24px; left: 50%; transform: translateX(-50%); display: flex; gap: 24px; border-top: 1px solid var(--primary-cyan); padding: 12px 24px; }
.title { font-size: 16px; font-weight: bold; color: var(--primary-cyan); margin-bottom: 8px; letter-spacing: 1px; }
.desc { font-size: 12px; color: var(--text-muted); line-height: 1.5; }
.highlight { color: var(--accent-orange); font-weight: bold; }
.data-row { display: flex; justify-content: space-between; margin-bottom: 6px; font-size: 12px; }
.data-label { color: var(--text-muted); margin-right: 16px; }
.data-value { color: var(--success-green); font-family: monospace; }
.status-step { font-size: 12px; color: var(--tech-gray); transition: color 0.3s; display: flex; align-items: center; gap: 6px; }
.status-step::before { content: ''; display: block; width: 6px; height: 6px; border-radius: 50%; background: var(--tech-gray); }
.status-step.active { color: var(--primary-cyan); }
.status-step.active::before { background: var(--primary-cyan); box-shadow: 0 0 8px var(--primary-cyan); }
/* ---------------- SVG Container ---------------- */
.svg-container {
width: 100%;
max-width: 1200px;
height: 100%;
max-height: 900px;
}
svg {
width: 100%;
height: 100%;
overflow: visible;
}
/* ---------------- Animations ---------------- */
/* 1. 全局升降 (举升装置) */
.anim-lift {
animation: liftSequence var(--total-duration) ease-in-out infinite;
}
@keyframes liftSequence {
0%, 5% { transform: translateY(200px); opacity: 0; }
15%, 85% { transform: translateY(0); opacity: 1; }
95%, 100% { transform: translateY(200px); opacity: 0; }
}
/* 2. 底座螺旋锁紧 */
.anim-screw {
transform-origin: center 540px;
animation: screwLock var(--total-duration) ease-in-out infinite;
}
@keyframes screwLock {
0%, 15% { transform: translateY(40px) rotate(0deg); }
25%, 85% { transform: translateY(0) rotate(720deg); }
95%, 100% { transform: translateY(40px) rotate(0deg); }
}
/* 3. 绝缘杆上推推压夹爪 */
.anim-rod-push {
animation: rodPush var(--total-duration) ease-in-out infinite;
}
@keyframes rodPush {
0%, 25% { transform: translateY(80px); }
35%, 85% { transform: translateY(0); }
90%, 100% { transform: translateY(80px); }
}
/* 4. 导线自重下沉 */
.anim-wire-sink {
animation: wireSink var(--total-duration) ease-in-out infinite;
}
@keyframes wireSink {
0%, 35% { transform: translateY(-30px); }
45%, 85% { transform: translateY(5px); }
95%, 100% { transform: translateY(-30px); }
}
/* 5. 楔形自锁 (IFR 核心) */
.anim-wedge-left {
animation: wedgeLeftLock var(--total-duration) ease-out infinite;
}
.anim-wedge-right {
animation: wedgeRightLock var(--total-duration) ease-out infinite;
}
@keyframes wedgeLeftLock {
0%, 40% { transform: translate(-10px, -20px); }
48%, 85% { transform: translate(3px, 5px); } /* 向下向内移动 */
95%, 100% { transform: translate(-10px, -20px); }
}
@keyframes wedgeRightLock {
0%, 40% { transform: translate(10px, -20px); }
48%, 85% { transform: translate(-3px, 5px); } /* 向下向内移动 */
95%, 100% { transform: translate(10px, -20px); }
}
/* 6. 核心力流特效展示 (IFR视觉引导) */
.anim-force-line {
stroke-dasharray: 10 5;
animation: forceFlow 1s linear infinite, forceFade var(--total-duration) infinite;
}
@keyframes forceFlow {
to { stroke-dashoffset: -15; }
}
@keyframes forceFade {
0%, 45% { opacity: 0; }
50%, 80% { opacity: 1; }
85%, 100% { opacity: 0; }
}
.anim-lock-glow {
animation: lockGlowPulse var(--total-duration) infinite;
}
@keyframes lockGlowPulse {
0%, 45% { filter: drop-shadow(0 0 0px transparent); stroke: var(--tech-gray); }
50%, 80% { filter: drop-shadow(0 0 12px var(--success-green)); stroke: var(--success-green); }
85%, 100% { filter: drop-shadow(0 0 0px transparent); stroke: var(--tech-gray); }
}
</style>
</head>
<body>
<!-- HUD 层,使用绝对定位,避免遮挡中央核心区域 -->
<div class="hud-container">
<div class="panel panel-top-left">
<div class="title">TRIZ - 最终理想解 (IFR)</div>
<div class="desc">
系统利用<span class="highlight">导线自身的重力</span>作为核心驱动资源。无需外部复杂机械结构,导线下沉迫使楔形夹爪沿斜面下滑,自动转化为巨大的侧向夹紧力。<br/><br/>
<span style="color:var(--primary-cyan)">消除矛盾:</span> 在极少增加系统复杂度的前提下,实现了地面快速挂载与高强度自锁的统一。
</div>
</div>
<div class="panel panel-top-right">
<div class="title">关键参数监控</div>
<div class="data-row">
<span class="data-label">楔形夹爪夹紧力:</span>
<span class="data-value" id="val-force">0 N</span>
</div>
<div class="data-row">
<span class="data-label">主杆绝缘耐压:</span>
<span class="data-value">> 20 kV</span>
</div>
<div class="data-row">
<span class="data-label">系统状态:</span>
<span class="data-value" style="color:var(--primary-cyan)" id="val-status">初始举升</span>
</div>
</div>
<div class="panel panel-bottom">
<div class="status-step" id="step-1">1. 举升装置</div>
<div class="status-step" id="step-2">2. 底座卡入锁紧</div>
<div class="status-step" id="step-3">3. 上推压入导线</div>
<div class="status-step" id="step-4">4. 导线自重触发自锁</div>
</div>
</div>
<!-- 核心 SVG 动画区域 -->
<div class="svg-container">
<svg viewBox="0 0 1000 800" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- 滤镜与渐变 -->
<filter id="neon-cyan" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<filter id="glow-orange">
<feGaussianBlur stdDeviation="6" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<linearGradient id="rod-grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#2D3748" />
<stop offset="50%" stop-color="#4A5568" />
<stop offset="100%" stop-color="#2D3748" />
</linearGradient>
<pattern id="metal-texture" width="10" height="10" patternUnits="userSpaceOnUse">
<path d="M 0,10 l 10,-10 M -2,2 l 4,-4 M 8,12 l 4,-4" stroke="#718096" stroke-width="0.5" opacity="0.3"/>
</pattern>
<!-- 箭头标记 -->
<marker id="arrow-down" viewBox="0 0 10 10" refX="5" refY="10" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 5 10 L 10 0 z" fill="#FF5E00" />
</marker>
<marker id="arrow-inward" viewBox="0 0 10 10" refX="5" refY="10" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 5 10 L 10 0 z" fill="#39FF14" />
</marker>
</defs>
<!-- 场景静物:横担 -->
<g transform="translate(500, 500)">
<rect x="-150" y="-30" width="300" height="60" fill="url(#metal-texture)" stroke="#A0AEC0" stroke-width="2" rx="4"/>
<line x1="-150" y1="-15" x2="150" y2="-15" stroke="#718096" stroke-width="1"/>
<line x1="-150" y1="15" x2="150" y2="15" stroke="#718096" stroke-width="1"/>
<text x="-140" y="-40" fill="#718096" font-size="14" font-family="monospace">CROSSARM_STRUCT</text>
</g>
<!-- 动态系统整体 (举升控制) -->
<g class="anim-lift">
<!-- 底部倒U型卡线器与螺旋机构 -->
<g transform="translate(500, 480)">
<!-- U型卡座 -->
<path d="M -60,60 L -60,-40 C -60,-50 -50,-60 -40,-60 L 40,-60 C 50,-60 60,-50 60,-40 L 60,60 L 40,60 L 40,-40 L -40,-40 L -40,60 Z" fill="#2D3748" stroke="var(--primary-cyan)" stroke-width="2" filter="url(#neon-cyan)"/>
<!-- 旋转锁紧底座组件 -->
<g class="anim-screw">
<!-- 螺杆 -->
<rect x="-10" y="20" width="20" height="60" fill="#A0AEC0" stroke="#4A5568"/>
<!-- 螺纹细节 -->
<path d="M -10,30 L 10,25 M -10,40 L 10,35 M -10,50 L 10,45 M -10,60 L 10,55 M -10,70 L 10,65" stroke="#2D3748" stroke-width="2"/>
<!-- 压板 -->
<rect x="-35" y="10" width="70" height="15" fill="#4A5568" stroke="var(--primary-cyan)" stroke-width="1" rx="2"/>
<!-- 底部绝缘杆连接头 -->
<rect x="-25" y="80" width="50" height="30" fill="url(#rod-grad)" rx="5"/>
<path d="M -15,80 L -15,110 M 15,80 L 15,110" stroke="#1A202C" stroke-width="1"/>
</g>
</g>
<!-- 绝缘主杆与顶部机构 (上推控制) -->
<g class="anim-rod-push">
<!-- 环氧树脂绝缘主杆 -->
<rect x="485" y="240" width="30" height="180" fill="url(#rod-grad)" stroke="#2D3748" stroke-width="2"/>
<text x="525" y="340" fill="#718096" font-size="12" font-family="monospace" transform="rotate(-90 525 340)">EPOXY_RESIN_ROD > 20kV</text>
<!-- 顶部夹爪底座 -->
<g transform="translate(500, 240)">
<path d="M -40,0 L 40,0 L 25,-40 L -25,-40 Z" fill="#2D3748" stroke="var(--primary-cyan)" stroke-width="2"/>
<!-- 楔形夹爪外壳 (V型槽) -->
<path d="M -25,-40 L -55,-120 L -35,-120 L -10,-40 Z" fill="#1A202C" stroke="var(--tech-gray)" stroke-width="2" class="anim-lock-glow"/>
<path d="M 25,-40 L 55,-120 L 35,-120 L 10,-40 Z" fill="#1A202C" stroke="var(--tech-gray)" stroke-width="2" class="anim-lock-glow"/>
<!-- 左侧楔形滑块 -->
<g class="anim-wedge-left">
<!-- 楔形块 -->
<path d="M -30,-110 L -45,-110 L -20,-50 L -10,-50 Z" fill="#4A5568" stroke="var(--primary-cyan)" stroke-width="1"/>
<!-- 橡胶防滑垫 -->
<path d="M -28,-110 L -26,-110 L -8,-50 L -10,-50 Z" fill="#2D3748" stroke="#000"/>
</g>
<!-- 右侧楔形滑块 -->
<g class="anim-wedge-right">
<path d="M 30,-110 L 45,-110 L 20,-50 L 10,-50 Z" fill="#4A5568" stroke="var(--primary-cyan)" stroke-width="1"/>
<path d="M 28,-110 L 26,-110 L 8,-50 L 10,-50 Z" fill="#2D3748" stroke="#000"/>
</g>
<!-- IFR 视觉引导力线 (仅在锁紧时显示) -->
<g class="anim-force-line">
<!-- 导线重力向下 -->
<line x1="0" y1="-130" x2="0" y2="-90" stroke="var(--accent-orange)" stroke-width="3" marker-end="url(#arrow-down)"/>
<!-- 左侧侧向夹紧力 -->
<line x1="-40" y1="-70" x2="-15" y2="-70" stroke="var(--success-green)" stroke-width="3" marker-end="url(#arrow-inward)"/>
<!-- 右侧侧向夹紧力 -->
<line x1="40" y1="-70" x2="15" y2="-70" stroke="var(--success-green)" stroke-width="3" marker-end="url(#arrow-inward)"/>
</g>
</g>
</g>
</g>
<!-- 导线 (受重力下沉) -->
<g transform="translate(500, 160)" class="anim-wire-sink">
<!-- 导线光晕 -->
<circle cx="0" cy="0" r="15" fill="none" stroke="var(--accent-orange)" stroke-width="4" filter="url(#glow-orange)" opacity="0.5"/>
<!-- 导线实体 -->
<circle cx="0" cy="0" r="12" fill="#E2E8F0" stroke="var(--accent-orange)" stroke-width="3"/>
<!-- 导线纹理 -->
<circle cx="0" cy="0" r="6" fill="none" stroke="#718096" stroke-width="1" stroke-dasharray="2 2"/>
<text x="25" y="-15" fill="var(--accent-orange)" font-size="14" font-weight="bold">CONDUCTOR_WIRE</text>
</g>
</svg>
</div>
<!-- JavaScript 用于同步文本状态和数据展示,增强高保真体验 -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const valStatus = document.getElementById('val-status');
const valForce = document.getElementById('val-force');
const steps = [
document.getElementById('step-1'),
document.getElementById('step-2'),
document.getElementById('step-3'),
document.getElementById('step-4')
];
const totalDuration = 12000; // 与 CSS 变量 --total-duration 保持一致
function updateStatus() {
const now = Date.now();
// 使用取模获取当前动画周期内的进度
const progress = (now % totalDuration) / totalDuration;
// 重置所有步骤样式
steps.forEach(s => s.classList.remove('active'));
// 状态机映射 (与 CSS Keyframes 对应)
// 0-15%: 举升装置 (step 1)
// 15-35%: 底座锁紧 (step 2)
// 35-48%: 上推入线 (step 3)
// 48-85%: 自重锁紧 (step 4, IFR阶段)
// 85-100%: 撤离/重置
if (progress < 0.15) {
steps[0].classList.add('active');
valStatus.textContent = "举升就位中...";
valStatus.style.color = "var(--text-main)";
valForce.textContent = "0 N";
} else if (progress < 0.35) {
steps[1].classList.add('active');
valStatus.textContent = "底座螺旋锁紧";
valStatus.style.color = "var(--primary-cyan)";
valForce.textContent = "0 N";
} else if (progress < 0.48) {
steps[2].classList.add('active');
valStatus.textContent = "推压导线入槽";
valStatus.style.color = "var(--accent-orange)";
valForce.textContent = "120 N (接触力)";
} else if (progress < 0.85) {
steps[3].classList.add('active');
valStatus.textContent = "自锁完成 (IFR触发)";
valStatus.style.color = "var(--success-green)";
// 模拟力值快速上升并稳定
let force = 800;
if(progress < 0.55) {
const ratio = (progress - 0.48) / 0.07;
force = Math.floor(120 + ratio * 695); // 到达 815
} else {
// 稳定在 800N 以上,带有微小扰动以增加真实感
force = 812 + Math.floor(Math.random() * 15);
}
valForce.textContent = `≥ ${force} N`;
} else {
valStatus.textContent = "系统复位";
valStatus.style.color = "var(--text-muted)";
valForce.textContent = "0 N";
}
requestAnimationFrame(updateStatus);
}
// 启动状态更新循环
requestAnimationFrame(updateStatus);
});
</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: #0a0a0f;
--grid-line: rgba(77, 208, 225, 0.05);
--hud-cyan: #4DD0E1;
--hud-cyan-dim: rgba(77, 208, 225, 0.3);
--alert-red: #FF3366;
--alert-amber: #FFB300;
--text-main: #E0F7FA;
--text-muted: #809C9F;
--font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body, html {
width: 100%;
height: 100%;
background-color: var(--bg-base);
background-image:
linear-gradient(var(--grid-line) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
background-size: 50px 50px;
font-family: var(--font-mono);
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
color: var(--text-main);
}
/* ---------------- HUD 层布局 (严格控制字号与边缘位置) ---------------- */
.hud-layer {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
pointer-events: none;
z-index: 10;
}
.panel {
position: absolute;
background: rgba(10, 10, 15, 0.8);
border: 1px solid var(--hud-cyan-dim);
padding: 12px 16px;
backdrop-filter: blur(4px);
font-size: 12px;
box-shadow: 0 4px 16px rgba(0,0,0,0.5);
}
.panel-top-left {
top: 20px; left: 20px;
width: 240px;
border-left: 3px solid var(--alert-amber);
}
.panel-top-right {
top: 20px; right: 20px;
width: 200px;
text-align: right;
border-right: 3px solid var(--hud-cyan);
}
.panel-bottom {
bottom: 20px; left: 50%;
transform: translateX(-50%);
display: flex;
gap: 20px;
border-top: 2px solid var(--hud-cyan);
align-items: center;
padding: 8px 24px;
}
.panel-title {
font-size: 13px;
font-weight: bold;
color: var(--hud-cyan);
margin-bottom: 6px;
letter-spacing: 1px;
text-transform: uppercase;
}
.panel-title.warning {
color: var(--alert-amber);
}
.data-row {
display: flex;
justify-content: space-between;
margin-bottom: 4px;
line-height: 1.4;
}
.text-dim { color: var(--text-muted); }
.text-red { color: var(--alert-red); font-weight: bold; text-shadow: 0 0 5px rgba(255,51,102,0.5); }
.text-amber { color: var(--alert-amber); font-weight: bold; }
.text-cyan { color: var(--hud-cyan); }
/* ---------------- 中心动画容器 ---------------- */
.hero-container {
position: relative;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
z-index: 1;
}
svg {
width: 100%;
height: 100%;
max-width: 900px;
max-height: 900px;
overflow: visible;
}
/* ---------------- 动画类 ---------------- */
.spin-slow {
animation: spin 24s linear infinite;
transform-origin: 400px 400px;
}
.spin-reverse {
animation: spin-rev 16s linear infinite;
transform-origin: 400px 400px;
}
@keyframes spin { 100% { transform: rotate(360deg); } }
@keyframes spin-rev { 100% { transform: rotate(-360deg); } }
.pulse-danger {
animation: pulse-red 1s ease-in-out infinite alternate;
}
@keyframes pulse-red {
0% { filter: drop-shadow(0 0 4px var(--alert-red)); opacity: 0.6; }
100% { filter: drop-shadow(0 0 12px var(--alert-red)); opacity: 1; }
}
.warning-flash {
animation: flash 0.5s infinite alternate;
}
@keyframes flash {
0% { opacity: 0.2; }
100% { opacity: 0.9; }
}
/* SVG 样式 */
.step-text {
font-family: var(--font-mono);
font-size: 12px;
fill: var(--text-muted);
transition: all 0.3s ease;
}
.step-text.active {
fill: var(--hud-cyan);
font-size: 14px;
font-weight: bold;
filter: drop-shadow(0 0 5px var(--hud-cyan));
}
.step-text.danger {
fill: var(--alert-red);
font-weight: bold;
}
.step-line {
stroke: var(--hud-cyan-dim);
stroke-width: 2;
transition: all 0.3s;
}
.step-line.active {
stroke: var(--hud-cyan);
stroke-width: 4;
filter: drop-shadow(0 0 4px var(--hud-cyan));
}
.step-line.danger {
stroke: var(--alert-red);
stroke-width: 4;
filter: drop-shadow(0 0 4px var(--alert-red));
}
</style>
</head>
<body>
<!-- HUD 信息面板 (边缘分布,文字极简,避免遮挡中心) -->
<div class="hud-layer">
<div class="panel panel-top-left">
<div class="panel-title warning">传统流程缺陷分析</div>
<div class="text-dim" style="margin-bottom: 8px;">12节点人工作业痛点监控</div>
<div class="data-row"><span class="text-dim">停电范围:</span><span class="text-red">大面积</span></div>
<div class="data-row"><span class="text-dim">高坠触电:</span><span class="text-red">高风险</span></div>
<div class="data-row"><span class="text-dim">工具状态:</span><span class="text-amber">不趁手</span></div>
<div class="data-row"><span class="text-dim">预警手段:</span><span class="text-amber">缺失</span></div>
<div class="data-row"><span class="text-dim">绑扎效率:</span><span class="text-red">耗时长</span></div>
</div>
<div class="panel panel-top-right">
<div class="panel-title">实时状态遥测</div>
<div class="data-row"><span class="text-dim">当前用时:</span><span id="hud-time" class="text-cyan">0 MIN</span></div>
<div class="data-row"><span class="text-dim">系统状态:</span><span id="hud-sys" class="text-cyan">运行正常</span></div>
<div class="data-row"><span class="text-dim">风险等级:</span><span id="hud-risk" class="text-cyan">LOW</span></div>
</div>
<div class="panel panel-bottom">
<span class="text-dim">当前节点执行中:</span>
<span id="hud-current-step" class="text-cyan" style="font-weight: bold; font-size: 14px; letter-spacing: 2px;">准备工作</span>
<span id="hud-alert-msg" class="text-red" style="margin-left: 20px; display: none;"></span>
</div>
</div>
<!-- 中心核心可视化区 -->
<div class="hero-container">
<svg viewBox="0 0 800 800" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- 发光滤镜 -->
<filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feMerge><feMergeNode in="blur" /><feMergeNode in="SourceGraphic" /></feMerge>
</filter>
<filter id="glow-red" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="6" result="blur" />
<feMerge><feMergeNode in="blur" /><feMergeNode in="SourceGraphic" /></feMerge>
</filter>
<filter id="glow-amber">
<feGaussianBlur stdDeviation="3" result="blur" />
<feMerge><feMergeNode in="blur" /><feMergeNode in="SourceGraphic" /></feMerge>
</filter>
<!-- 雷达扫描渐变 -->
<radialGradient id="radar-grad">
<stop offset="80%" stop-color="var(--hud-cyan)" stop-opacity="0"/>
<stop offset="100%" stop-color="var(--hud-cyan)" stop-opacity="0.2"/>
</radialGradient>
</defs>
<!-- 背景装饰环 -->
<g transform="translate(400, 400)">
<circle cx="0" cy="0" r="320" fill="none" stroke="var(--hud-cyan-dim)" stroke-width="1" stroke-dasharray="4 8" class="spin-slow" />
<circle cx="0" cy="0" r="300" fill="none" stroke="var(--hud-cyan-dim)" stroke-width="1" />
<circle cx="0" cy="0" r="160" fill="none" stroke="var(--hud-cyan-dim)" stroke-width="0.5" stroke-dasharray="2 4" class="spin-reverse" />
</g>
<!-- 动态生成的 12 步进度刻度盘将插入此处 -->
<g id="step-ring" transform="translate(400, 400)"></g>
<!-- 中心被测物体 (电线杆与作业环境) -->
<g transform="translate(400, 400)" id="center-pole">
<!-- 地面线 -->
<line x1="-80" y1="120" x2="80" y2="120" stroke="var(--hud-cyan)" stroke-width="2" filter="url(#glow-cyan)"/>
<line x1="-40" y1="130" x2="40" y2="130" stroke="var(--hud-cyan-dim)" stroke-width="1"/>
<!-- 主杆 -->
<rect x="-8" y="-80" width="16" height="200" fill="none" stroke="var(--hud-cyan)" stroke-width="2" filter="url(#glow-cyan)" id="pole-body"/>
<line x1="0" y1="-80" x2="0" y2="120" stroke="var(--hud-cyan)" stroke-width="1" stroke-dasharray="4 4" opacity="0.5"/>
<!-- 横担 -->
<rect x="-60" y="-60" width="120" height="10" fill="#0a0a0f" stroke="var(--hud-cyan)" stroke-width="2"/>
<rect x="-70" y="-30" width="140" height="10" fill="#0a0a0f" stroke="var(--hud-cyan)" stroke-width="2"/>
<!-- 绝缘子及绑扎点 -->
<circle cx="-50" cy="-65" r="4" fill="var(--hud-cyan)" id="insulator-1"/>
<circle cx="50" cy="-65" r="4" fill="var(--hud-cyan)" id="insulator-2"/>
<circle cx="-60" cy="-35" r="4" fill="var(--hud-cyan)" id="insulator-3"/>
<circle cx="60" cy="-35" r="4" fill="var(--hud-cyan)" id="insulator-4"/>
<!-- 导线 -->
<path d="M -150,-50 Q -50,-65 -50,-65 Q 0,-40 50,-65 Q 50,-65 150,-50" fill="none" stroke="var(--hud-cyan)" stroke-width="1.5" id="wire-1"/>
<path d="M -150,-10 Q -60,-35 -60,-35 Q 0,-10 60,-35 Q 60,-35 150,-10" fill="none" stroke="var(--hud-cyan)" stroke-width="1.5" id="wire-2"/>
<!-- 危险警告标记 (默认隐藏,JS控制显示) -->
<g id="danger-overlay" style="display: none;">
<!-- 停电断开符号 -->
<path id="power-cut-icon" d="M -20,-10 L 10,-30 L 5,-15 L 25,-15 L -10,15 L -5,0 Z" fill="var(--alert-red)" filter="url(#glow-red)" style="display: none;"/>
<!-- 坠落警告波纹 -->
<circle id="fall-wave" cx="0" cy="0" r="40" fill="none" stroke="var(--alert-red)" stroke-width="2" class="warning-flash" style="display: none;"/>
<!-- 耗时沙漏图标 -->
<g id="time-warn-icon" transform="translate(0,-100)" fill="none" stroke="var(--alert-amber)" stroke-width="2" class="pulse-danger" style="display: none;">
<path d="M -15,-15 L 15,-15 L 0,0 L 15,15 L -15,15 L 0,0 Z"/>
<circle cx="0" cy="0" r="25" stroke-dasharray="4 4"/>
</g>
</g>
</g>
<!-- 扫描雷达指针 -->
<g transform="translate(400, 400)" id="radar-pointer">
<line x1="0" y1="0" x2="0" y2="-280" stroke="var(--hud-cyan)" stroke-width="2" filter="url(#glow-cyan)"/>
<path d="M 0,0 L 40,-280 A 280 280 0 0 0 0,-280 Z" fill="url(#radar-grad)"/>
</g>
</svg>
</div>
<script>
// 工作流程 12 个节点及对应元数据
const workflowSteps = [
{ name: "现场勘查", type: "normal", timeObj: 5, risk: "LOW" },
{ name: "开票", type: "normal", timeObj: 10, risk: "LOW" },
{ name: "工作许可", type: "normal", timeObj: 15, risk: "LOW" },
{ name: "停电", type: "danger", timeObj: 20, risk: "HIGH", alert: "大面积停电区域扩大", action: "cut-power" },
{ name: "转检修", type: "normal", timeObj: 30, risk: "MEDIUM" },
{ name: "登杆", type: "danger", timeObj: 45, risk: "CRITICAL", alert: "高空作业坠落/触电风险", action: "fall-risk" },
{ name: "更换绝缘子", type: "warning", timeObj: 60, risk: "HIGH", alert: "工具不趁手, 操作困难", action: "time-loss" },
{ name: "绑扎线缠绕", type: "danger", timeObj: 90, risk: "HIGH", alert: "手工缠绕耗时长且费力", action: "time-loss" },
{ name: "紧固", type: "warning", timeObj: 100, risk: "MEDIUM", alert: "依赖人工经验", action: "time-loss" },
{ name: "转运行", type: "normal", timeObj: 105, risk: "LOW" },
{ name: "恢复供电", type: "normal", timeObj: 115, risk: "LOW", action: "restore-power" },
{ name: "工作终结", type: "normal", timeObj: 120, risk: "LOW" }
];
const totalSteps = workflowSteps.length;
const ringRadius = 240;
const textRadius = 280;
const stepRingGroup = document.getElementById('step-ring');
// 动态生成环形刻度和文字
workflowSteps.forEach((step, index) => {
const angle = (index * (360 / totalSteps)) - 90; // -90 使其从12点钟方向开始
const rad = angle * Math.PI / 180;
// 计算坐标
const x1 = Math.cos(rad) * (ringRadius - 10);
const y1 = Math.sin(rad) * (ringRadius - 10);
const x2 = Math.cos(rad) * (ringRadius + 10);
const y2 = Math.sin(rad) * (ringRadius + 10);
const tx = Math.cos(rad) * textRadius;
const ty = Math.sin(rad) * textRadius;
// 绘制刻度线
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
line.setAttribute("x1", x1);
line.setAttribute("y1", y1);
line.setAttribute("x2", x2);
line.setAttribute("y2", y2);
line.setAttribute("class", "step-line");
line.id = `line-${index}`;
stepRingGroup.appendChild(line);
// 绘制文字
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("x", tx);
text.setAttribute("y", ty);
text.setAttribute("text-anchor", "middle");
text.setAttribute("dominant-baseline", "middle");
text.setAttribute("class", "step-text");
text.id = `text-${index}`;
// 根据角度翻转文字以保证可读性
let rotateAngle = angle + 90;
if (angle > 90 && angle < 270) rotateAngle += 180;
text.setAttribute("transform", `rotate(${rotateAngle}, ${tx}, ${ty})`);
text.textContent = step.name;
stepRingGroup.appendChild(text);
});
// 动画控制逻辑
const DURATION_PER_STEP = 1500; // 每步 1.5 秒
let startTime = null;
const uiElements = {
time: document.getElementById('hud-time'),
sys: document.getElementById('hud-sys'),
risk: document.getElementById('hud-risk'),
curStep: document.getElementById('hud-current-step'),
alertMsg: document.getElementById('hud-alert-msg'),
radar: document.getElementById('radar-pointer'),
poleBody: document.getElementById('pole-body'),
wire1: document.getElementById('wire-1'),
wire2: document.getElementById('wire-2'),
dangerOverlay: document.getElementById('danger-overlay'),
powerCutIcon: document.getElementById('power-cut-icon'),
fallWave: document.getElementById('fall-wave'),
timeWarnIcon: document.getElementById('time-warn-icon')
};
function resetCenterGraphic() {
uiElements.poleBody.setAttribute("stroke", "var(--hud-cyan)");
uiElements.poleBody.setAttribute("filter", "url(#glow-cyan)");
uiElements.wire1.setAttribute("stroke", "var(--hud-cyan)");
uiElements.wire2.setAttribute("stroke", "var(--hud-cyan)");
uiElements.dangerOverlay.style.display = "none";
uiElements.powerCutIcon.style.display = "none";
uiElements.fallWave.style.display = "none";
uiElements.timeWarnIcon.style.display = "none";
}
function triggerAction(action) {
resetCenterGraphic();
if (!action) return;
uiElements.dangerOverlay.style.display = "block";
if (action === "cut-power") {
uiElements.poleBody.setAttribute("stroke", "var(--text-muted)");
uiElements.poleBody.removeAttribute("filter");
uiElements.wire1.setAttribute("stroke", "var(--alert-red)");
uiElements.wire2.setAttribute("stroke", "var(--alert-red)");
uiElements.powerCutIcon.style.display = "block";
} else if (action === "fall-risk") {
uiElements.fallWave.style.display = "block";
} else if (action === "time-loss") {
uiElements.poleBody.setAttribute("stroke", "var(--alert-amber)");
uiElements.timeWarnIcon.style.display = "block";
} else if (action === "restore-power") {
resetCenterGraphic();
}
}
function renderFrame(timestamp) {
if (!startTime) startTime = timestamp;
const elapsed = timestamp - startTime;
// 总周期时间
const totalDuration = DURATION_PER_STEP * totalSteps;
const currentLoopTime = elapsed % totalDuration;
// 计算当前所在的阶段 (0-11)
const currentStepIndex = Math.floor(currentLoopTime / DURATION_PER_STEP);
// 计算雷达指针平滑旋转角度 (0-360)
const currentAngle = (currentLoopTime / totalDuration) * 360;
// 旋转雷达
uiElements.radar.setAttribute("transform", `rotate(${currentAngle})`);
// 更新所有刻度和文字的样式
workflowSteps.forEach((step, idx) => {
const line = document.getElementById(`line-${idx}`);
const text = document.getElementById(`text-${idx}`);
// 重置类名
line.setAttribute("class", "step-line");
text.setAttribute("class", "step-text");
if (idx === currentStepIndex) {
if (step.type === 'danger' || step.type === 'warning') {
line.setAttribute("class", "step-line danger");
text.setAttribute("class", "step-text danger");
} else {
line.setAttribute("class", "step-line active");
text.setAttribute("class", "step-text active");
}
}
});
// 更新 HUD 数据 (仅在步骤切换时执行状态更新逻辑)
const stepData = workflowSteps[currentStepIndex];
// 平滑插值计算当前显示时间 (0 到当前步骤的 timeObj)
const prevTime = currentStepIndex === 0 ? 0 : workflowSteps[currentStepIndex-1].timeObj;
const stepProgress = (currentLoopTime % DURATION_PER_STEP) / DURATION_PER_STEP;
const displayTime = Math.floor(prevTime + (stepData.timeObj - prevTime) * stepProgress);
uiElements.time.textContent = `${displayTime} MIN`;
uiElements.curStep.textContent = `${currentStepIndex + 1}. ${stepData.name}`;
if (stepData.type === 'danger' || stepData.type === 'warning') {
uiElements.sys.textContent = "异常痛点检出";
uiElements.sys.className = "text-red";
uiElements.risk.textContent = stepData.risk;
uiElements.risk.className = stepData.risk === 'CRITICAL' ? "text-red warning-flash" : "text-amber";
if (stepData.alert) {
uiElements.alertMsg.textContent = `[警告] ${stepData.alert}`;
uiElements.alertMsg.style.display = "inline";
}
} else {
uiElements.sys.textContent = "运行正常";
uiElements.sys.className = "text-cyan";
uiElements.risk.textContent = stepData.risk;
uiElements.risk.className = "text-cyan";
uiElements.alertMsg.style.display = "none";
}
// 触发中心图形状态变化
// 为了避免每一帧都重置,我们根据当前所处的 action 保持状态
const currentAction = stepData.action;
// 简单处理:如果是当前步骤第一帧左右,触发动作更新 (避免过度调用 DOM)
if (!stepData._actionTriggered || stepData._lastIndex !== currentStepIndex) {
triggerAction(currentAction);
workflowSteps.forEach(s => s._lastIndex = currentStepIndex);
stepData._actionTriggered = true;
}
requestAnimationFrame(renderFrame);
}
// 页面加载完成后自动启动动画
document.addEventListener("DOMContentLoaded", () => {
requestAnimationFrame(renderFrame);
});
</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: #0a0a0f;
--grid-line: rgba(43, 56, 78, 0.3);
--color-normal: #00F0FF; /* 正常运行:青色 */
--color-warning: #FFB000; /* 警告/人工作业:橙黄 */
--color-danger: #FF2A2A; /* 停电/危险:亮红 */
--color-success: #00FF66; /* 恢复:荧光绿 */
--color-text-main: #E2E8F0;
--color-text-dim: #64748B;
--font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
--font-sans: system-ui, -apple-system, sans-serif;
/* 状态变量 (通过JS控制挂载在body上) */
--theme-color: var(--color-normal);
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: var(--bg-base);
font-family: var(--font-sans);
color: var(--color-text-main);
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
/* 监控网格背景 */
.grid-bg {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background-image:
linear-gradient(var(--grid-line) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
background-size: 50px 50px;
background-position: center center;
z-index: 0;
opacity: 0.6;
}
/* 扫描线叠加层 */
.scanlines {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: linear-gradient(
to bottom,
rgba(255,255,255,0),
rgba(255,255,255,0) 50%,
rgba(0,0,0,0.2) 50%,
rgba(0,0,0,0.2)
);
background-size: 100% 4px;
z-index: 100;
pointer-events: none;
opacity: 0.3;
}
/* ---------------- UI 面板布局 (边缘停靠,避免遮挡中央) ---------------- */
.hud {
position: absolute;
z-index: 10;
pointer-events: none;
}
.panel {
background: rgba(10, 10, 15, 0.85);
border: 1px solid rgba(100, 116, 139, 0.3);
backdrop-filter: blur(8px);
padding: 16px;
}
/* 左上:标题与痛点摘要 */
.top-left {
top: 24px; left: 24px;
width: 280px;
border-left: 3px solid var(--theme-color);
transition: border-color 0.3s;
}
.title { font-family: var(--font-mono); font-size: 14px; font-weight: bold; color: var(--theme-color); margin-bottom: 8px; letter-spacing: 1px; transition: color 0.3s;}
.desc { font-size: 12px; color: var(--color-text-dim); line-height: 1.6; }
.highlight { color: var(--color-warning); font-weight: bold; }
/* 右上:停电时长计时器 */
.top-right {
top: 24px; right: 24px;
text-align: right;
border-right: 3px solid var(--color-danger);
display: flex;
flex-direction: column;
align-items: flex-end;
}
.timer-label { font-size: 12px; color: var(--color-text-dim); font-family: var(--font-mono); margin-bottom: 4px; }
.timer-value { font-size: 32px; font-weight: bold; color: var(--color-danger); font-family: var(--font-mono); line-height: 1; }
.timer-unit { font-size: 14px; color: var(--color-text-dim); }
/* 左下:风险告警监控 */
.bottom-left {
bottom: 24px; left: 24px;
width: 200px;
border-left: 3px solid var(--color-warning);
}
.risk-item { display: flex; justify-content: space-between; font-size: 12px; font-family: var(--font-mono); margin-bottom: 6px; opacity: 0.4; transition: all 0.3s;}
.risk-item.active { opacity: 1; color: var(--color-danger); text-shadow: 0 0 8px rgba(255,42,42,0.5); }
.risk-indicator { width: 8px; height: 8px; border-radius: 50%; background: currentColor; box-shadow: 0 0 5px currentColor; }
/* 底部中:12步流程时间轴 */
.bottom-center {
bottom: 24px; left: 50%;
transform: translateX(-50%);
width: 80%;
max-width: 800px;
display: flex;
justify-content: space-between;
padding: 12px 20px;
border-top: 1px solid var(--theme-color);
transition: border-color 0.3s;
}
.step {
display: flex; flex-direction: column; align-items: center; gap: 6px;
opacity: 0.3; transition: all 0.3s;
position: relative;
}
.step.active { opacity: 1; transform: scale(1.1); }
.step.done { opacity: 0.6; }
.step-dot { width: 6px; height: 6px; background: var(--color-text-dim); border-radius: 50%; transition: all 0.3s; }
.step.active .step-dot { background: var(--theme-color); box-shadow: 0 0 8px var(--theme-color); }
.step.done .step-dot { background: var(--color-success); }
.step-label { font-size: 10px; font-family: var(--font-mono); white-space: nowrap; color: var(--color-text-main); writing-mode: vertical-rl; transform: rotate(180deg); letter-spacing: 2px;}
/* ---------------- 核心动画区域 ---------------- */
.svg-container {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
z-index: 5;
}
svg {
width: 100%;
height: 100%;
max-width: 1000px;
max-height: 800px;
overflow: visible;
}
/* SVG 元素状态样式 */
.power-line { stroke: var(--theme-color); transition: stroke 0.5s, filter 0.5s; }
.energy-flow { stroke: #fff; stroke-dasharray: 10 20; animation: flow 1s linear infinite; opacity: 1; transition: opacity 0.3s; }
@keyframes flow { to { stroke-dashoffset: -30; } }
/* 故障/停电状态覆盖 */
body[data-status="outage"] .power-line { stroke: var(--color-danger); filter: none; }
body[data-status="outage"] .energy-flow { opacity: 0; animation: none; }
body[data-status="outage"] { --theme-color: var(--color-danger); }
/* 作业中状态覆盖 */
body[data-status="working"] .power-line { stroke: var(--color-text-dim); filter: none; }
body[data-status="working"] .energy-flow { opacity: 0; }
body[data-status="working"] { --theme-color: var(--color-warning); }
/* 人工攀爬节点 */
#worker-node { transition: transform 0.5s ease-in-out, opacity 0.3s; opacity: 0; }
#worker-node.visible { opacity: 1; }
/* 危险区域光晕圈 */
.danger-zone { opacity: 0; transform-origin: center; transition: opacity 0.3s; pointer-events: none; }
.danger-zone.active { opacity: 1; animation: pulseDanger 2s ease-out infinite; }
@keyframes pulseDanger {
0% { transform: scale(0.5); opacity: 0.8; stroke-width: 4px; }
100% { transform: scale(2.5); opacity: 0; stroke-width: 1px; }
}
/* 工具不趁手/费时动画指示器 */
.tool-indicator { opacity: 0; transition: opacity 0.3s; }
.tool-indicator.active { opacity: 1; animation: rotateTool 3s linear infinite; }
@keyframes rotateTool {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="grid-bg"></div>
<div class="scanlines"></div>
<!-- UI 层 -->
<div class="hud panel top-left">
<div class="title" id="ui-title">传统电工作业监控</div>
<div class="desc">
全流程共 <span class="highlight">12个环节</span>,高度依赖人工。<br>
暴露三大核心痛点:<br>
1. <span style="color:var(--color-danger)">停电范围大</span>,严重影响供电可靠性。<br>
2. <span style="color:var(--color-warning)">人工作业耗时长</span>,工具不趁手。<br>
3. 存在极高 <span style="color:var(--color-danger)">高坠与触电危险</span>。
</div>
</div>
<div class="hud panel top-right">
<div class="timer-label">单次故障平均停电时长</div>
<div>
<span class="timer-value" id="ui-timer">000</span>
<span class="timer-unit">MIN</span>
</div>
</div>
<div class="hud panel bottom-left">
<div class="title" style="color: var(--color-warning); border:none; margin-bottom:12px;">风险实时监测</div>
<div class="risk-item" id="risk-outage">
<span>大规模停电</span><div class="risk-indicator"></div>
</div>
<div class="risk-item" id="risk-fall">
<span>高空坠落风险</span><div class="risk-indicator"></div>
</div>
<div class="risk-item" id="risk-shock">
<span>感应电触电风险</span><div class="risk-indicator"></div>
</div>
<div class="risk-item" id="risk-time">
<span>绑扎耗时过长</span><div class="risk-indicator"></div>
</div>
</div>
<div class="hud panel bottom-center" id="step-container">
<!-- 步骤通过 JS 动态生成 -->
</div>
<!-- SVG 动画层 -->
<div class="svg-container">
<svg viewBox="0 0 1000 800" preserveAspectRatio="xMidYMid meet">
<defs>
<filter id="neon-glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="6" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="danger-glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="8" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<!-- 远景电网线条 (背景修饰) -->
<path d="M 0 500 Q 250 550 500 500 T 1000 500" fill="none" stroke="rgba(100, 116, 139, 0.2)" stroke-width="2"/>
<path d="M 0 520 Q 250 570 500 520 T 1000 520" fill="none" stroke="rgba(100, 116, 139, 0.2)" stroke-width="2"/>
<!-- 中心电线杆主体 -->
<g transform="translate(500, 750)">
<!-- 杆体 -->
<polygon points="-15,0 15,0 8,-450 -8,-450" fill="#1E293B" stroke="#334155" stroke-width="2"/>
<line x1="-8" y1="-450" x2="-15" y2="0" stroke="#0F172A" stroke-width="2"/>
<!-- 横担 1 -->
<rect x="-120" y="-380" width="240" height="15" fill="#334155" rx="2"/>
<path d="M -100,-380 L -80,-365 M -60,-380 L -40,-365 M 100,-380 L 80,-365 M 60,-380 L 40,-365" stroke="#1E293B" stroke-width="2"/>
<!-- 横担 2 (顶部主作业区) -->
<rect x="-180" y="-440" width="360" height="15" fill="#334155" rx="2"/>
<path d="M -160,-440 L -140,-425 M 160,-440 L 140,-425" stroke="#1E293B" stroke-width="2"/>
<!-- 绝缘子 -->
<g class="insulator" transform="translate(-160, -460)">
<rect x="-10" y="0" width="20" height="20" fill="#94A3B8" rx="2"/>
<path d="M -15,5 L 15,5 M -15,10 L 15,10 M -15,15 L 15,15" stroke="#475569" stroke-width="2"/>
</g>
<g class="insulator" transform="translate(160, -460)">
<rect x="-10" y="0" width="20" height="20" fill="#94A3B8" rx="2"/>
<path d="M -15,5 L 15,5 M -15,10 L 15,10 M -15,15 L 15,15" stroke="#475569" stroke-width="2"/>
</g>
<!-- 中间作业绝缘子 (重点关注) -->
<g class="insulator" id="target-insulator" transform="translate(0, -460)">
<rect x="-12" y="-5" width="24" height="25" fill="#CBD5E1" rx="2"/>
<path d="M -18,2 L 18,2 M -18,8 L 18,8 M -18,14 L 18,14" stroke="#64748B" stroke-width="3"/>
</g>
</g>
<!-- 输电线 (受状态控制) -->
<!-- 左侧线 -->
<path class="power-line" d="M -100 400 Q 200 450 340 290" fill="none" stroke-width="4" filter="url(#neon-glow)"/>
<path class="energy-flow" d="M -100 400 Q 200 450 340 290" fill="none" stroke-width="2"/>
<!-- 右侧线 -->
<path class="power-line" d="M 660 290 Q 800 450 1100 400" fill="none" stroke-width="4" filter="url(#neon-glow)"/>
<path class="energy-flow" d="M 660 290 Q 800 450 1100 400" fill="none" stroke-width="2"/>
<!-- 危险光晕圈 (登杆作业时触发) -->
<g transform="translate(500, 290)">
<circle class="danger-zone" cx="0" cy="0" r="40" fill="none" stroke="var(--color-danger)" stroke-width="2"/>
<circle class="danger-zone" cx="0" cy="0" r="40" fill="none" stroke="var(--color-warning)" stroke-width="2" style="animation-delay: 1s;"/>
</g>
<!-- 抽象作业人员节点 (带安全带的标识) -->
<g id="worker-node" transform="translate(500, 750)">
<!-- 身体 -->
<circle cx="0" cy="0" r="14" fill="var(--color-warning)" filter="url(#danger-glow)"/>
<!-- 安全绳示意 -->
<path d="M -14,0 Q -30,20 -10,40" fill="none" stroke="var(--color-text-main)" stroke-width="2" stroke-dasharray="4 2"/>
<!-- 内部标识 -->
<text x="0" y="4" font-size="12" fill="#000" font-family="var(--font-mono)" text-anchor="middle" font-weight="bold">W</text>
<!-- 耗时操作/工具不趁手标识 -->
<g class="tool-indicator">
<circle cx="0" cy="0" r="24" fill="none" stroke="var(--color-warning)" stroke-width="2" stroke-dasharray="10 10"/>
<path d="M 18,-18 L 26,-26 M -18,18 L -26,26" stroke="var(--color-warning)" stroke-width="3"/>
</g>
</g>
<!-- 全局红色警戒线 (停电时显示) -->
<g id="outage-overlay" style="opacity: 0; transition: opacity 0.5s; pointer-events:none;">
<rect x="0" y="0" width="100%" height="100%" fill="rgba(255, 42, 42, 0.05)"/>
<text x="500" y="150" font-size="24" fill="rgba(255, 42, 42, 0.4)" font-family="var(--font-mono)" text-anchor="middle" font-weight="bold" letter-spacing="10px">SYSTEM OUTAGE / AREA OF EFFECT</text>
</g>
</svg>
</div>
<!-- 控制脚本 -->
<script>
document.addEventListener('DOMContentLoaded', () => {
// 定义 12 个步骤
const STEPS = [
{ name: "现场勘查", type: "prep", duration: 1000 },
{ name: "开票", type: "prep", duration: 1000 },
{ name: "工作许可", type: "prep", duration: 1000 },
{ name: "停电", type: "outage", duration: 1000 },
{ name: "转检修", type: "outage", duration: 1000 },
{ name: "登杆", type: "work-climb", duration: 2000 },
{ name: "更换绝缘子", type: "work-heavy", duration: 3000 },
{ name: "绑扎线缠绕", type: "work-heavy", duration: 3000 },
{ name: "紧固", type: "work-heavy", duration: 1500 },
{ name: "转运行", type: "restore", duration: 1000 },
{ name: "恢复供电", type: "restore", duration: 1000 },
{ name: "工作终结", type: "end", duration: 1500 }
];
const totalDuration = STEPS.reduce((sum, step) => sum + step.duration, 0);
// DOM 元素引用
const stepContainer = document.getElementById('step-container');
const timerEl = document.getElementById('ui-timer');
const workerNode = document.getElementById('worker-node');
const dangerZones = document.querySelectorAll('.danger-zone');
const toolIndicator = document.querySelector('.tool-indicator');
const outageOverlay = document.getElementById('outage-overlay');
const uiTitle = document.getElementById('ui-title');
// 风险指示器
const riskOutage = document.getElementById('risk-outage');
const riskFall = document.getElementById('risk-fall');
const riskShock = document.getElementById('risk-shock');
const riskTime = document.getElementById('risk-time');
// 渲染底部步骤条
STEPS.forEach((step, idx) => {
const el = document.createElement('div');
el.className = 'step';
el.id = `step-${idx}`;
el.innerHTML = `
<div class="step-dot"></div>
<div class="step-label">${idx + 1}.${step.name}</div>
`;
stepContainer.appendChild(el);
});
const stepEls = document.querySelectorAll('.step');
// 动画状态管理
let startTime = null;
function updateAnimation(timestamp) {
if (!startTime) startTime = timestamp;
// 计算循环进度
const elapsed = (timestamp - startTime) % (totalDuration + 2000); // 留2秒休息期
// 重置状态
let currentStepIdx = -1;
let timeAccumulator = 0;
// 查找当前处于哪个步骤
for (let i = 0; i < STEPS.length; i++) {
if (elapsed >= timeAccumulator && elapsed < timeAccumulator + STEPS[i].duration) {
currentStepIdx = i;
break;
}
timeAccumulator += STEPS[i].duration;
}
// 如果在休息期,视为已结束
if (currentStepIdx === -1) {
currentStepIdx = STEPS.length - 1;
if(elapsed > totalDuration) {
resetVisuals();
requestAnimationFrame(updateAnimation);
return;
}
}
const currentStep = STEPS[currentStepIdx];
const stepProgress = (elapsed - timeAccumulator) / currentStep.duration;
// 更新UI步骤条
stepEls.forEach((el, idx) => {
el.className = 'step';
if (idx < currentStepIdx) el.classList.add('done');
if (idx === currentStepIdx) el.classList.add('active');
});
// 更新计时器 (模拟停电 120 分钟)
// 停电从第4步开始,到第11步结束
let displayTime = 0;
if (currentStepIdx >= 3 && currentStepIdx <= 10) {
// 计算在这段期间的整体进度
const outageStart = STEPS.slice(0, 3).reduce((s, x) => s + x.duration, 0);
const outageEnd = STEPS.slice(0, 11).reduce((s, x) => s + x.duration, 0);
const outageElapsed = Math.max(0, elapsed - outageStart);
const outageRatio = Math.min(1, outageElapsed / (outageEnd - outageStart));
displayTime = Math.floor(outageRatio * 120);
} else if (currentStepIdx > 10) {
displayTime = 120;
}
timerEl.textContent = displayTime.toString().padStart(3, '0');
// 核心状态机及视觉调度
applyVisualState(currentStep.type, stepProgress, currentStepIdx);
requestAnimationFrame(updateAnimation);
}
function applyVisualState(type, progress, idx) {
const body = document.body;
// 默认复位所有风险
riskOutage.classList.remove('active');
riskFall.classList.remove('active');
riskShock.classList.remove('active');
riskTime.classList.remove('active');
switch(type) {
case 'prep':
body.setAttribute('data-status', 'normal');
workerNode.classList.remove('visible');
workerNode.style.transform = `translate(500px, 750px)`; // 在地面
outageOverlay.style.opacity = "0";
uiTitle.textContent = "准备阶段:现场勘察与开票";
break;
case 'outage':
body.setAttribute('data-status', 'outage');
riskOutage.classList.add('active');
outageOverlay.style.opacity = "1";
uiTitle.textContent = "停电转检修:大面积停电开始";
break;
case 'work-climb':
body.setAttribute('data-status', 'working');
riskOutage.classList.add('active');
riskFall.classList.add('active');
workerNode.classList.add('visible');
outageOverlay.style.opacity = "0.5";
// 攀爬动画:从底 (y=750) 爬到顶 (y=290)
const currentY = 750 - (progress * 460);
workerNode.style.transform = `translate(500px, ${currentY}px)`;
uiTitle.textContent = "高危作业:人工登杆";
dangerZones.forEach(z => z.classList.remove('active'));
toolIndicator.classList.remove('active');
break;
case 'work-heavy':
body.setAttribute('data-status', 'working');
riskOutage.classList.add('active');
riskFall.classList.add('active');
riskShock.classList.add('active');
riskTime.classList.add('active');
workerNode.classList.add('visible');
workerNode.style.transform = `translate(500px, 290px)`; // 固定在顶部
dangerZones.forEach(z => z.classList.add('active'));
toolIndicator.classList.add('active');
const opName = STEPS[idx].name;
uiTitle.textContent = `痛点阶段:${opName}耗时极长`;
break;
case 'restore':
body.setAttribute('data-status', 'normal');
outageOverlay.style.opacity = "0";
dangerZones.forEach(z => z.classList.remove('active'));
toolIndicator.classList.remove('active');
// 下降并消失
if (progress < 0.5) {
const descY = 290 + (progress * 2 * 460);
workerNode.style.transform = `translate(500px, ${descY}px)`;
} else {
workerNode.classList.remove('visible');
}
uiTitle.textContent = "操作终结:恢复供电";
break;
case 'end':
body.setAttribute('data-status', 'normal');
workerNode.classList.remove('visible');
uiTitle.textContent = "全流程结束 (单次故障耗时120分)";
break;
}
}
function resetVisuals() {
document.body.setAttribute('data-status', 'normal');
workerNode.classList.remove('visible');
dangerZones.forEach(z => z.classList.remove('active'));
toolIndicator.classList.remove('active');
outageOverlay.style.opacity = "0";
timerEl.textContent = "000";
stepEls.forEach(el => el.className = 'step');
}
// 启动动画循环
requestAnimationFrame(updateAnimation);
});
</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-dark: #050508;
--grid-color: rgba(64, 75, 95, 0.15);
--ui-cyan: #00E5FF;
--ui-amber: #FFB300;
--ui-red: #FF3366;
--ui-gray: #4A5568;
--ui-text: #E2E8F0;
--ui-text-dim: #94A3B8;
--font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
--font-sans: system-ui, -apple-system, sans-serif;
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: var(--bg-dark);
background-image:
linear-gradient(var(--grid-color) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
background-size: 40px 40px;
font-family: var(--font-sans);
overflow: hidden;
color: var(--ui-text);
}
/* ================= HUD UI 布局 (严格边缘分布) ================= */
.hud-layer {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 100;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.panel {
background: rgba(5, 5, 8, 0.85);
border: 1px solid rgba(148, 163, 184, 0.2);
backdrop-filter: blur(8px);
padding: 16px 20px;
}
.hud-top {
display: flex;
justify-content: space-between;
padding: 24px;
}
.panel-left {
width: 320px;
border-left: 3px solid var(--ui-red);
}
.panel-right {
width: 240px;
text-align: right;
border-right: 3px solid var(--ui-amber);
display: flex;
flex-direction: column;
align-items: flex-end;
}
.hud-bottom {
padding: 24px;
display: flex;
justify-content: center;
}
.panel-timeline {
width: 80%;
max-width: 900px;
display: flex;
justify-content: space-between;
border-top: 1px solid var(--ui-cyan);
padding-top: 12px;
}
.title { font-family: var(--font-mono); font-size: 14px; font-weight: bold; color: var(--ui-red); margin-bottom: 8px; letter-spacing: 1px; }
.desc { font-size: 12px; color: var(--ui-text-dim); line-height: 1.6; }
.highlight-red { color: var(--ui-red); font-weight: bold; }
.highlight-amber { color: var(--ui-amber); font-weight: bold; }
.stat-label { font-size: 12px; color: var(--ui-text-dim); font-family: var(--font-mono); margin-bottom: 4px; }
.stat-value { font-size: 36px; font-weight: bold; font-family: var(--font-mono); line-height: 1; transition: color 0.3s; }
.stat-unit { font-size: 14px; color: var(--ui-text-dim); }
.timeline-step {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
opacity: 0.3;
transition: all 0.3s;
}
.timeline-step.active { opacity: 1; transform: scale(1.1); }
.timeline-step.done { opacity: 0.6; }
.step-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ui-text-dim); }
.timeline-step.active .step-dot { background: var(--ui-cyan); box-shadow: 0 0 10px var(--ui-cyan); }
.timeline-step.active.danger .step-dot { background: var(--ui-red); box-shadow: 0 0 10px var(--ui-red); }
.step-text { font-family: var(--font-mono); font-size: 10px; writing-mode: vertical-rl; transform: rotate(180deg); color: var(--ui-text); letter-spacing: 1px; }
.risk-alert {
position: absolute;
top: 140px;
left: 24px;
background: rgba(255, 51, 102, 0.1);
border: 1px solid var(--ui-red);
padding: 8px 12px;
font-size: 12px;
font-family: var(--font-mono);
color: var(--ui-red);
opacity: 0;
transition: opacity 0.3s;
display: flex;
align-items: center;
gap: 8px;
}
.risk-alert.show { opacity: 1; animation: blinkAlert 1s infinite alternate; }
@keyframes blinkAlert { from { box-shadow: 0 0 0px transparent; } to { box-shadow: 0 0 15px rgba(255, 51, 102, 0.4); } }
/* ================= 核心 SVG 动画区 ================= */
.svg-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
svg {
width: 100%;
height: 100%;
max-width: 1200px;
max-height: 900px;
overflow: visible;
}
/* 状态与滤镜 */
.glow-cyan { filter: drop-shadow(0 0 8px var(--ui-cyan)); }
.glow-red { filter: drop-shadow(0 0 12px var(--ui-red)); }
.power-line { stroke: var(--ui-cyan); transition: stroke 0.5s; }
.power-line.outage { stroke: var(--ui-gray); opacity: 0.5; filter: none; }
.energy-flow { stroke-dasharray: 10 20; animation: dashFlow 1s linear infinite; }
.energy-flow.outage { opacity: 0; animation: none; }
@keyframes dashFlow { to { stroke-dashoffset: -30; } }
/* ================= 人物动画系统 (CSS 控制) ================= */
#worker-group {
transform: translate(380px, 700px); /* 初始地面位置 */
transition: transform 0s linear;
}
/* 躯干摆动 */
#worker-body { transform-origin: center bottom; }
/* 四肢关节 */
.limb { transform-origin: top center; }
/* 攀爬动画 */
.is-climbing #worker-group { animation: climbTranslate 4s linear forwards; }
.is-climbing .arm-left { animation: climbArmL 0.8s infinite alternate; }
.is-climbing .arm-right { animation: climbArmR 0.8s infinite alternate; }
.is-climbing .leg-left { animation: climbLegL 0.8s infinite alternate; }
.is-climbing .leg-right { animation: climbLegR 0.8s infinite alternate; }
@keyframes climbTranslate {
0% { transform: translate(380px, 700px); }
100% { transform: translate(380px, 260px); }
}
@keyframes climbArmL { 0% { transform: rotate(-30deg); } 100% { transform: rotate(10deg); } }
@keyframes climbArmR { 0% { transform: rotate(10deg); } 100% { transform: rotate(-30deg); } }
@keyframes climbLegL { 0% { transform: rotate(-10deg) translateY(0); } 100% { transform: rotate(30deg) translateY(-10px); } }
@keyframes climbLegR { 0% { transform: rotate(30deg) translateY(-10px); } 100% { transform: rotate(-10deg) translateY(0); } }
/* 顶部高危作业动画 (挣扎、费时) */
.is-working #worker-group { transform: translate(380px, 260px); }
.is-working #worker-body { animation: bodyStruggle 1s infinite alternate ease-in-out; }
.is-working .arm-left { animation: workArmL 0.5s infinite alternate; }
.is-working .arm-right { transform: rotate(-45deg); animation: workArmR 0.3s infinite alternate; }
.is-working .leg-left { transform: rotate(10deg); }
.is-working .leg-right { transform: rotate(-10deg); }
@keyframes bodyStruggle { 0% { transform: rotate(-5deg); } 100% { transform: rotate(5deg); } }
@keyframes workArmL { 0% { transform: rotate(-80deg); } 100% { transform: rotate(-60deg); } }
@keyframes workArmR { 0% { transform: rotate(-120deg); } 100% { transform: rotate(-20deg); } } /* 幅度大,表示耗力缠绕 */
/* 下降动画 */
.is-descending #worker-group { animation: descendTranslate 2s linear forwards; }
.is-descending .arm-left { animation: climbArmL 0.5s infinite alternate reverse; }
.is-descending .arm-right { animation: climbArmR 0.5s infinite alternate reverse; }
.is-descending .leg-left { animation: climbLegL 0.5s infinite alternate reverse; }
.is-descending .leg-right { animation: climbLegR 0.5s infinite alternate reverse; }
@keyframes descendTranslate {
0% { transform: translate(380px, 260px); }
100% { transform: translate(380px, 700px); }
}
/* 危险特效 */
.hazard-ring { opacity: 0; transform-origin: 380px 240px; pointer-events: none; }
.is-working .hazard-ring { animation: pulseDanger 2s infinite cubic-bezier(0.2, 0.8, 0.2, 1); }
@keyframes pulseDanger {
0% { transform: scale(0.2); opacity: 1; stroke-width: 4px; }
100% { transform: scale(2.5); opacity: 0; stroke-width: 1px; }
}
.arc-flash { opacity: 0; pointer-events: none; }
.is-working .arc-flash { animation: flashArc 3s infinite; }
@keyframes flashArc {
0%, 90%, 100% { opacity: 0; }
92%, 96% { opacity: 1; filter: drop-shadow(0 0 10px var(--ui-cyan)); }
94%, 98% { opacity: 0.2; }
}
</style>
</head>
<body>
<!-- 边缘分布的 UI 信息层 -->
<div class="hud-layer">
<div class="hud-top">
<div class="panel panel-left">
<div class="title">传统作业痛点仿真分析</div>
<div class="desc">
正在执行 <span style="color:var(--ui-cyan)">12节点</span> 人工作业流程:<br>
1. <span class="highlight-red">停电范围大</span>,严重影响供电可靠性。<br>
2. <span class="highlight-amber">高坠/触电风险高</span>,人工登杆缺乏预警。<br>
3. 工具不趁手,<span class="highlight-red">绑扎缠绕耗时长</span>,劳动强度极大。
</div>
</div>
<div class="panel panel-right">
<div class="stat-label">单次故障平均停电</div>
<div>
<span class="stat-value" id="ui-timer" style="color: var(--ui-cyan);">0</span>
<span class="stat-unit">MIN</span>
</div>
<div class="stat-label" style="margin-top: 12px;">当前阶段状态</div>
<div class="stat-label" id="ui-status" style="color: var(--ui-cyan); font-weight: bold; font-size: 14px;">准备中</div>
</div>
</div>
<div class="risk-alert" id="risk-alert">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
<span id="risk-text">高空作业/触电警告</span>
</div>
<div class="hud-bottom">
<div class="panel-timeline" id="timeline">
<!-- JS 动态生成 12 步 -->
</div>
</div>
</div>
<!-- 居中核心 SVG 动画区 -->
<div class="svg-canvas">
<svg viewBox="0 0 800 800" preserveAspectRatio="xMidYMid meet" id="main-svg">
<!-- 场景背景与基建 -->
<g id="environment">
<!-- 地平线 -->
<line x1="100" y1="700" x2="700" y2="700" stroke="#334155" stroke-width="2"/>
<!-- 电线杆 -->
<polygon points="385,700 415,700 408,150 392,150" fill="#1E293B" stroke="#0F172A" stroke-width="2"/>
<line x1="392" y1="150" x2="385" y2="700" stroke="#0F172A" stroke-width="1"/>
<!-- 横担及支架 -->
<rect x="250" y="200" width="300" height="16" fill="#334155" rx="2" stroke="#0F172A"/>
<line x1="280" y1="216" x2="400" y2="280" stroke="#334155" stroke-width="8"/>
<line x1="520" y1="216" x2="400" y2="280" stroke="#334155" stroke-width="8"/>
<!-- 绝缘子 -->
<path d="M270,170 L290,170 L285,200 L275,200 Z" fill="#94A3B8"/>
<path d="M390,170 L410,170 L405,200 L395,200 Z" fill="#94A3B8"/>
<path d="M510,170 L530,170 L525,200 L515,200 Z" fill="#94A3B8"/>
</g>
<!-- 输电线 -->
<g id="power-lines">
<!-- 左侧导线 -->
<path id="wire-l" class="power-line glow-cyan" d="M -50,160 Q 150,220 280,170" fill="none" stroke-width="4"/>
<path id="flow-l" class="energy-flow glow-cyan" d="M -50,160 Q 150,220 280,170" fill="none" stroke="#fff" stroke-width="2"/>
<!-- 中央导线 -->
<path id="wire-c" class="power-line glow-cyan" d="M 280,170 Q 340,190 400,170" fill="none" stroke-width="4"/>
<!-- 右侧导线 -->
<path id="wire-r" class="power-line glow-cyan" d="M 400,170 Q 650,220 850,160" fill="none" stroke-width="4"/>
<path id="flow-r" class="energy-flow glow-cyan" d="M 400,170 Q 650,220 850,160" fill="none" stroke="#fff" stroke-width="2"/>
</g>
<!-- 危险光晕圈 (登杆作业时显示) -->
<circle class="hazard-ring" cx="380" cy="240" r="100" fill="none" stroke="var(--ui-red)"/>
<circle class="hazard-ring" cx="380" cy="240" r="100" fill="none" stroke="var(--ui-amber)" style="animation-delay: 1s;"/>
<!-- 感应电弧 (触电风险) -->
<path class="arc-flash" d="M 380,170 L 370,200 L 390,210 L 375,240" fill="none" stroke="#FFF" stroke-width="3" filter="drop-shadow(0 0 5px var(--ui-cyan))"/>
<path class="arc-flash" d="M 410,170 L 420,190 L 405,210 L 415,230" fill="none" stroke="#FFF" stroke-width="2" style="animation-delay: 0.5s;"/>
<!-- 人物角色 (动态绑定 CSS 类) -->
<g id="worker-group">
<g id="worker-body">
<!-- 安全带连结杆 -->
<path d="M 0,10 Q 20,20 30,0" fill="none" stroke="#94A3B8" stroke-width="2" stroke-dasharray="4 2"/>
<!-- 身体与装备 -->
<rect x="-8" y="0" width="16" height="30" fill="#FF9F0A" rx="4"/> <!-- 工作服 -->
<rect x="-10" y="15" width="20" height="5" fill="#334155"/> <!-- 安全腰带 -->
<!-- 左腿 -->
<g transform="translate(-4, 30)">
<line class="limb leg-left" x1="0" y1="0" x2="-5" y2="25" stroke="#FF9F0A" stroke-width="6" stroke-linecap="round"/>
</g>
<!-- 右腿 -->
<g transform="translate(4, 30)">
<line class="limb leg-right" x1="0" y1="0" x2="5" y2="25" stroke="#FF9F0A" stroke-width="6" stroke-linecap="round"/>
</g>
<!-- 左臂 -->
<g transform="translate(-8, 5)">
<line class="limb arm-left" x1="0" y1="0" x2="-15" y2="20" stroke="#FF9F0A" stroke-width="5" stroke-linecap="round"/>
</g>
<!-- 右臂 (持手持工具) -->
<g transform="translate(8, 5)">
<g class="limb arm-right">
<line x1="0" y1="0" x2="15" y2="20" stroke="#FF9F0A" stroke-width="5" stroke-linecap="round"/>
<!-- 钳子/工具 -->
<path d="M 15,20 L 25,25 M 15,20 L 22,28" stroke="#E2E8F0" stroke-width="2"/>
</g>
</g>
<!-- 头部与安全帽 -->
<circle cx="0" cy="-8" r="8" fill="#FCD34D"/> <!-- 脸 -->
<path d="M -12,-10 Q 0,-25 12,-10 Z" fill="#F59E0B"/> <!-- 安全帽 -->
<line x1="-12" y1="-10" x2="12" y2="-10" stroke="#D97706" stroke-width="2"/>
</g>
</g>
</svg>
</div>
<!-- 逻辑控制脚本 -->
<script>
document.addEventListener('DOMContentLoaded', () => {
// 定义 12 个操作步骤及时间分配 (总计模拟一整个完整循环)
// 痛点集中在 6-9 步,重点分配时间占比以突出 "耗时长"
const STEPS = [
{ name: "现场勘查", type: "prep", duration: 1500 },
{ name: "开票", type: "prep", duration: 1500 },
{ name: "工作许可", type: "prep", duration: 1500 },
{ name: "停电", type: "outage", duration: 1500 },
{ name: "转检修", type: "outage", duration: 1500 },
{ name: "登杆", type: "climb", duration: 4000 },
{ name: "更换绝缘子", type: "work", duration: 4000 },
{ name: "绑扎线缠绕", type: "work", duration: 5000 },
{ name: "紧固", type: "work", duration: 3000 },
{ name: "转运行", type: "descend", duration: 2500 },
{ name: "恢复供电", type: "restore", duration: 1500 },
{ name: "工作终结", type: "done", duration: 2000 }
];
const timelineEl = document.getElementById('timeline');
const uiTimer = document.getElementById('ui-timer');
const uiStatus = document.getElementById('ui-status');
const riskAlert = document.getElementById('risk-alert');
const riskText = document.getElementById('risk-text');
const workerGroup = document.getElementById('main-svg');
const powerLines = document.querySelectorAll('.power-line, .energy-flow');
// 渲染底部时间轴
STEPS.forEach((step, idx) => {
const el = document.createElement('div');
el.className = 'timeline-step';
el.id = `step-${idx}`;
el.innerHTML = `
<div class="step-dot"></div>
<div class="step-text">${idx + 1}.${step.name}</div>
`;
timelineEl.appendChild(el);
});
const stepEls = document.querySelectorAll('.timeline-step');
const totalCycleTime = STEPS.reduce((a, b) => a + b.duration, 0) + 1000; // 留白1秒
let startTime = null;
let currentStepIdx = -1;
function animate(timestamp) {
if (!startTime) startTime = timestamp;
// 计算循环时间
const elapsed = (timestamp - startTime) % totalCycleTime;
// 查找当前阶段
let accTime = 0;
let newStepIdx = STEPS.length - 1;
for (let i = 0; i < STEPS.length; i++) {
if (elapsed >= accTime && elapsed < accTime + STEPS[i].duration) {
newStepIdx = i;
break;
}
accTime += STEPS[i].duration;
}
// 如果处于重置期
if (elapsed > totalCycleTime - 1000) {
newStepIdx = -1;
}
// 阶段发生变化时,触发状态更新
if (newStepIdx !== currentStepIdx && newStepIdx >= 0) {
currentStepIdx = newStepIdx;
const step = STEPS[currentStepIdx];
updateVisuals(currentStepIdx, step);
}
// 计算停电计时器显示 (映射 120 分钟)
// 停电从第4步(idx 3)开始,第11步(idx 10)恢复
let simTime = 0;
if (currentStepIdx >= 3 && currentStepIdx < 11) {
const outageStart = STEPS.slice(0, 3).reduce((a,b)=>a+b.duration, 0);
const outageEnd = STEPS.slice(0, 11).reduce((a,b)=>a+b.duration, 0);
const outageElapsed = elapsed - outageStart;
const ratio = Math.min(1, Math.max(0, outageElapsed / (outageEnd - outageStart)));
// 在 "绑扎线缠绕" 阶段让时间走得飞快,突出痛点
if(step.type === 'work') {
simTime = Math.floor(ratio * 120);
} else {
simTime = Math.floor(ratio * 120);
}
uiTimer.textContent = simTime;
uiTimer.style.color = simTime > 80 ? 'var(--ui-red)' : 'var(--ui-amber)';
} else if (currentStepIdx >= 11) {
uiTimer.textContent = 120;
uiTimer.style.color = 'var(--ui-text)';
} else {
uiTimer.textContent = 0;
uiTimer.style.color = 'var(--ui-cyan)';
}
requestAnimationFrame(animate);
}
function updateVisuals(idx, step) {
// 更新 UI 步骤条
stepEls.forEach((el, i) => {
el.className = 'timeline-step';
if (i < idx) el.classList.add('done');
if (i === idx) {
el.classList.add('active');
if (step.type === 'outage' || step.type === 'climb' || step.type === 'work') {
el.classList.add('danger');
}
}
});
uiStatus.textContent = `${idx + 1}. ${step.name}`;
// 核心状态机逻辑 - 通过挂载 className 到 SVG root 来控制内部元素的 CSS 动画
workerGroup.className.baseVal = '';
riskAlert.classList.remove('show');
switch(step.type) {
case 'prep':
uiStatus.style.color = 'var(--ui-cyan)';
powerLines.forEach(l => l.classList.remove('outage'));
break;
case 'outage':
uiStatus.style.color = 'var(--ui-red)';
powerLines.forEach(l => l.classList.add('outage'));
riskAlert.classList.add('show');
riskText.textContent = "停电转检修:大面积停电发生";
break;
case 'climb':
uiStatus.style.color = 'var(--ui-amber)';
workerGroup.className.baseVal = 'is-climbing';
riskAlert.classList.add('show');
riskText.textContent = "高危作业:人工登杆 存在高坠风险";
break;
case 'work':
uiStatus.style.color = 'var(--ui-red)';
workerGroup.className.baseVal = 'is-working';
riskAlert.classList.add('show');
riskText.textContent = "痛点:绑扎耗时长,工具不趁手,触电风险高";
break;
case 'descend':
uiStatus.style.color = 'var(--ui-amber)';
workerGroup.className.baseVal = 'is-descending';
break;
case 'restore':
case 'done':
uiStatus.style.color = 'var(--ui-cyan)';
powerLines.forEach(l => l.classList.remove('outage'));
break;
}
}
// 启动引擎
requestAnimationFrame(animate);
});
</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-color: #0b1120;
--panel-bg: rgba(15, 23, 42, 0.95);
--grid-line: rgba(51, 65, 85, 0.3);
/* 状态色彩 */
--color-safe: #10B981; /* 运行正常:翠绿 */
--color-warn: #F59E0B; /* 警告/攀爬:明黄 */
--color-danger: #EF4444; /* 停电/高危:鲜红 */
--color-accent: #3B82F6; /* UI高亮:亮蓝 */
/* 字体色彩 (确保极高可读性) */
--text-main: #F8FAFC; /* 主文本:纯白偏蓝 */
--text-muted: #94A3B8; /* 次文本:浅灰 */
/* 字体设置 - 选用最易读的无衬线黑体 */
--font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;
--font-mono: 'SFMono-Regular', Consolas, Monaco, monospace;
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: var(--bg-color);
background-image:
linear-gradient(var(--grid-line) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
background-size: 50px 50px;
font-family: var(--font-family);
color: var(--text-main);
overflow: hidden;
}
/* ==================== 布局与面板 (HUD) ==================== */
.hud-container {
position: absolute;
inset: 0;
pointer-events: none; /* 让鼠标事件穿透到动画区 */
z-index: 100;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.panel {
background: var(--panel-bg);
border: 1px solid rgba(148, 163, 184, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
backdrop-filter: blur(8px);
border-radius: 8px;
pointer-events: auto;
}
/* 顶部区域 */
.hud-top {
display: flex;
justify-content: space-between;
padding: 24px;
align-items: flex-start;
}
/* 左上:痛点分析面板 */
.panel-left {
width: 360px;
padding: 24px;
border-left: 6px solid var(--color-danger);
}
.panel-title {
font-size: 22px;
font-weight: bold;
color: var(--text-main);
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 10px;
}
.panel-title::before {
content: '';
display: block;
width: 12px;
height: 12px;
background: var(--color-danger);
border-radius: 50%;
box-shadow: 0 0 10px var(--color-danger);
}
.pain-points {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 12px;
}
.pain-points li {
font-size: 15px;
line-height: 1.6;
color: var(--text-muted);
background: rgba(255,255,255,0.03);
padding: 10px 14px;
border-radius: 6px;
border-left: 3px solid transparent;
}
.pain-points li.highlight-danger {
border-left-color: var(--color-danger);
color: var(--text-main);
}
.pain-points li.highlight-warn {
border-left-color: var(--color-warn);
color: var(--text-main);
}
.keyword-red { color: #FFA3A3; font-weight: bold; }
.keyword-yellow { color: #FDE047; font-weight: bold; }
/* 右上:停电计时器面板 */
.panel-right {
width: 280px;
padding: 20px 24px;
text-align: right;
border-right: 6px solid var(--color-warn);
display: flex;
flex-direction: column;
align-items: flex-end;
}
.timer-label {
font-size: 16px;
color: var(--text-muted);
margin-bottom: 8px;
font-weight: 500;
}
.timer-display {
display: flex;
align-items: baseline;
gap: 8px;
}
.timer-value {
font-size: 64px;
font-weight: bold;
font-family: var(--font-mono);
color: var(--color-safe);
line-height: 1;
text-shadow: 0 0 20px rgba(16, 185, 129, 0.4);
transition: color 0.3s, text-shadow 0.3s;
}
.timer-value.outage {
color: var(--color-danger);
text-shadow: 0 0 20px rgba(239, 68, 68, 0.6);
}
.timer-unit {
font-size: 20px;
color: var(--text-muted);
font-weight: bold;
}
/* 底部:进度与流程监控 */
.hud-bottom {
padding: 24px;
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.current-action-display {
background: rgba(15, 23, 42, 0.9);
border: 2px solid var(--color-accent);
padding: 12px 32px;
border-radius: 30px;
font-size: 24px;
font-weight: bold;
color: var(--color-accent);
box-shadow: 0 0 20px rgba(59, 130, 246, 0.3);
transition: all 0.3s;
}
.timeline {
display: flex;
width: 90%;
max-width: 1200px;
background: var(--panel-bg);
padding: 16px 24px;
border-radius: 12px;
justify-content: space-between;
align-items: center;
border: 1px solid rgba(148, 163, 184, 0.2);
position: relative;
}
/* 进度条背景线 */
.timeline::before {
content: '';
position: absolute;
top: 50%;
left: 40px;
right: 40px;
height: 4px;
background: rgba(148, 163, 184, 0.2);
z-index: 0;
transform: translateY(-50%);
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
z-index: 1;
opacity: 0.4;
transition: opacity 0.3s, transform 0.3s;
}
.step-item.active {
opacity: 1;
transform: scale(1.15);
}
.step-item.done {
opacity: 0.8;
}
.step-dot {
width: 16px;
height: 16px;
border-radius: 50%;
background: #334155;
border: 3px solid var(--panel-bg);
transition: all 0.3s;
}
.step-item.active .step-dot {
background: var(--color-accent);
box-shadow: 0 0 15px var(--color-accent);
}
.step-item.active.danger-step .step-dot {
background: var(--color-danger);
box-shadow: 0 0 15px var(--color-danger);
}
.step-text {
font-size: 14px;
font-weight: bold;
color: var(--text-main);
white-space: nowrap;
}
/* 悬浮警报框 (杆上作业时显示) */
.floating-alert {
position: absolute;
top: 150px;
left: 50%;
transform: translateX(-50%);
background: rgba(239, 68, 68, 0.15);
border: 2px solid var(--color-danger);
padding: 12px 24px;
border-radius: 8px;
display: flex;
align-items: center;
gap: 12px;
opacity: 0;
transition: opacity 0.3s;
}
.floating-alert.show {
opacity: 1;
animation: alertPulse 1.5s infinite alternate;
}
.alert-icon {
font-size: 24px;
}
.alert-text {
font-size: 18px;
font-weight: bold;
color: #FFA3A3;
}
@keyframes alertPulse {
0% { box-shadow: 0 0 10px rgba(239, 68, 68, 0.4); }
100% { box-shadow: 0 0 30px rgba(239, 68, 68, 0.8); }
}
/* ==================== SVG 动画核心区 ==================== */
.svg-container {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
svg {
width: 100%;
height: 100%;
max-width: 1400px;
max-height: 1000px;
overflow: visible;
}
/* 环境与电网 */
.env-ground { stroke: #334155; stroke-width: 3; }
.pole-base { fill: #1E293B; stroke: #0F172A; stroke-width: 2; }
.crossarm { fill: #334155; stroke: #0F172A; stroke-width: 2; }
.insulator { fill: #94A3B8; stroke: #475569; stroke-width: 2; }
/* 输电线 (动态颜色) */
.power-line {
stroke: var(--color-safe);
stroke-width: 5;
fill: none;
transition: stroke 0.5s;
filter: drop-shadow(0 0 8px var(--color-safe));
}
/* 电流动画 */
.energy-flow {
stroke: #FFF;
stroke-width: 2;
stroke-dasharray: 15 30;
fill: none;
animation: flowAnim 1s linear infinite;
}
@keyframes flowAnim {
to { stroke-dashoffset: -45; }
}
/* 停电状态 */
body[data-status="outage"] .power-line {
stroke: #475569;
filter: none;
}
body[data-status="outage"] .energy-flow {
display: none;
}
/* 人员动作 (CSS Keyframes) */
#worker {
transform: translate(500px, 750px); /* 默认地面位置 */
transition: transform 0.5s;
}
.worker-body { fill: var(--color-warn); }
.worker-belt { fill: #334155; stroke: #0F172A; stroke-width: 1.5; }
.worker-head { fill: #FDE047; }
.worker-limb { stroke: var(--color-warn); stroke-width: 8; stroke-linecap: round; }
/* 设置关节变换中心 */
#arm-l, #arm-r { transform-origin: top center; }
#leg-l, #leg-r { transform-origin: top center; }
#worker-torso { transform-origin: center bottom; }
/* 动作类:攀爬中 */
.anim-climbing #worker { animation: climbMove 3s linear forwards; }
.anim-climbing #arm-l { animation: climbArmL 0.6s infinite alternate; }
.anim-climbing #arm-r { animation: climbArmR 0.6s infinite alternate; }
.anim-climbing #leg-l { animation: climbLegL 0.6s infinite alternate; }
.anim-climbing #leg-r { animation: climbLegR 0.6s infinite alternate; }
@keyframes climbMove {
0% { transform: translate(500px, 750px); }
100% { transform: translate(500px, 320px); } /* 爬到横担下方 */
}
@keyframes climbArmL { 0% { transform: rotate(-40deg); } 100% { transform: rotate(10deg); } }
@keyframes climbArmR { 0% { transform: rotate(10deg); } 100% { transform: rotate(-40deg); } }
@keyframes climbLegL { 0% { transform: rotate(-15deg); } 100% { transform: rotate(30deg); } }
@keyframes climbLegR { 0% { transform: rotate(30deg); } 100% { transform: rotate(-15deg); } }
/* 动作类:杆上作业 (费力、耗时) */
.anim-working #worker { transform: translate(500px, 320px); }
.anim-working #worker-torso { animation: workStruggle 1.5s infinite alternate ease-in-out; }
.anim-working #arm-l { transform: rotate(-70deg); animation: workToolL 0.4s infinite alternate; }
.anim-working #arm-r { transform: rotate(-110deg); animation: workToolR 0.3s infinite alternate; }
.anim-working #leg-l { transform: rotate(20deg); }
.anim-working #leg-r { transform: rotate(-10deg); }
@keyframes workStruggle { 0% { transform: rotate(-5deg); } 100% { transform: rotate(8deg); } }
@keyframes workToolL { 0% { transform: rotate(-70deg); } 100% { transform: rotate(-50deg); } }
@keyframes workToolR { 0% { transform: rotate(-110deg); } 100% { transform: rotate(-80deg); } } /* 幅度大,体现工具不趁手 */
/* 动作类:下降 */
.anim-descending #worker { animation: descendMove 2s linear forwards; }
.anim-descending #arm-l { animation: climbArmL 0.5s infinite alternate reverse; }
.anim-descending #arm-r { animation: climbArmR 0.5s infinite alternate reverse; }
.anim-descending #leg-l { animation: climbLegL 0.5s infinite alternate reverse; }
.anim-descending #leg-r { animation: climbLegR 0.5s infinite alternate reverse; }
@keyframes descendMove {
0% { transform: translate(500px, 320px); }
100% { transform: translate(500px, 750px); }
}
/* 危险特效:高空坠落与触电红圈 */
.danger-ring {
opacity: 0;
transform-origin: 500px 300px;
pointer-events: none;
fill: none;
stroke-width: 4;
}
body[data-status="danger"] .danger-ring.ring-red {
stroke: var(--color-danger);
animation: ringExpand 2s infinite cubic-bezier(0.1, 0.8, 0.3, 1);
}
body[data-status="danger"] .danger-ring.ring-warn {
stroke: var(--color-warn);
animation: ringExpand 2s infinite cubic-bezier(0.1, 0.8, 0.3, 1);
animation-delay: 1s;
}
@keyframes ringExpand {
0% { transform: scale(0.2); opacity: 1; stroke-width: 8; }
100% { transform: scale(3.5); opacity: 0; stroke-width: 1; }
}
/* 工具费时指示器 (沙漏或旋转图标) */
.tool-spinner {
opacity: 0;
transform-origin: 500px 220px;
transition: opacity 0.3s;
}
body[data-status="working-heavy"] .tool-spinner {
opacity: 1;
animation: spin 3s linear infinite;
}
@keyframes spin { 100% { transform: rotate(360deg); } }
</style>
</head>
<body data-status="normal">
<!-- ==================== UI 覆盖层 ==================== -->
<div class="hud-container">
<!-- 顶部左右面板 -->
<div class="hud-top">
<!-- 痛点面板 -->
<div class="panel panel-left">
<div class="panel-title">传统作业痛点诊断</div>
<ul class="pain-points">
<li id="pain-1">作业依赖拉闸断电,<span class="keyword-red">停电范围大</span>,严重影响供电可靠性。</li>
<li id="pain-2">人工登杆与高空操作,存在极高<span class="keyword-red">高坠与触电风险</span>,缺乏实时预警。</li>
<li id="pain-3">手工绑扎线缠绕,<span class="keyword-yellow">工具不趁手</span>且<span class="keyword-red">耗时长</span>,劳动强度极大。</li>
</ul>
</div>
<!-- 计时面板 -->
<div class="panel panel-right">
<div class="timer-label">单次故障平均停电</div>
<div class="timer-display">
<div class="timer-value" id="ui-timer">000</div>
<div class="timer-unit">分钟</div>
</div>
<div style="font-size: 14px; color: var(--text-muted); margin-top: 12px; font-weight: bold;">12 节点全流程仿真进行中...</div>
</div>
</div>
<!-- 居中高危警报 (仅在杆上作业显示) -->
<div class="floating-alert" id="hazard-alert">
<span class="alert-icon">⚠️</span>
<span class="alert-text">高坠 / 触电高危区域!人工绑扎耗时激增!</span>
</div>
<!-- 底部流程控制 -->
<div class="hud-bottom">
<div class="current-action-display" id="ui-current-action">1. 现场勘查</div>
<div class="timeline" id="timeline-container">
<!-- 12个步骤将由 JS 动态生成 -->
</div>
</div>
</div>
<!-- ==================== SVG 核心动画 ==================== -->
<div class="svg-container">
<svg viewBox="0 0 1000 900" preserveAspectRatio="xMidYMid meet" id="main-scene">
<!-- 场景背景网格与地平线 -->
<line class="env-ground" x1="100" y1="750" x2="900" y2="750" />
<!-- 电线杆主体 -->
<g id="pole-structure">
<!-- 杆体 -->
<polygon class="pole-base" points="485,750 515,750 508,180 492,180" />
<line x1="500" y1="180" x2="500" y2="750" stroke="#0F172A" stroke-width="2" stroke-dasharray="10 10"/>
<!-- 主横担 -->
<rect class="crossarm" x="300" y="240" width="400" height="24" rx="4" />
<!-- 支撑架 -->
<line x1="340" y1="264" x2="500" y2="350" stroke="#334155" stroke-width="12" />
<line x1="660" y1="264" x2="500" y2="350" stroke="#334155" stroke-width="12" />
<!-- 绝缘子 -->
<path class="insulator" d="M 330,190 L 370,190 L 360,240 L 340,240 Z" />
<path class="insulator" d="M 480,190 L 520,190 L 510,240 L 490,240 Z" />
<path class="insulator" d="M 630,190 L 670,190 L 660,240 L 640,240 Z" />
</g>
<!-- 输电线路 (动态受控) -->
<g id="power-lines">
<!-- 左线 -->
<path class="power-line" d="M -50,180 Q 200,280 350,190" />
<path class="energy-flow" d="M -50,180 Q 200,280 350,190" />
<!-- 中线 -->
<path class="power-line" d="M 350,190 Q 425,220 500,190" />
<!-- 右线 -->
<path class="power-line" d="M 500,190 Q 750,280 1050,180" />
<path class="energy-flow" d="M 500,190 Q 750,280 1050,180" />
</g>
<!-- 危险光晕圈 (高空作业时触发) -->
<circle class="danger-ring ring-red" cx="500" cy="240" r="80" />
<circle class="danger-ring ring-warn" cx="500" cy="240" r="80" />
<!-- 耗时工具指示器 -->
<g class="tool-spinner">
<circle cx="500" cy="220" r="60" fill="none" stroke="var(--color-warn)" stroke-width="4" stroke-dasharray="20 15" />
<path d="M 490,170 L 510,170 L 500,190 Z" fill="var(--color-warn)"/>
<path d="M 490,270 L 510,270 L 500,250 Z" fill="var(--color-warn)"/>
<text x="500" y="150" fill="var(--color-warn)" font-size="20" font-weight="bold" text-anchor="middle" font-family="var(--font-family)">费时费力</text>
</g>
<!-- 作业人员 (通过 className 切换动作) -->
<g id="worker" class="anim-idle">
<g transform="translate(0, -60)"> <!-- 调整人物整体重心 -->
<!-- 安全带绑扎点 -->
<path d="M 0,40 Q 30,50 45,-10" fill="none" stroke="#94A3B8" stroke-width="3" stroke-dasharray="6 4"/>
<!-- 身体躯干 -->
<g id="worker-torso">
<rect class="worker-body" x="-14" y="0" width="28" height="50" rx="8"/>
<rect class="worker-belt" x="-16" y="25" width="32" height="10" rx="2"/> <!-- 安全腰带 -->
<!-- 左臂 -->
<g transform="translate(-14, 10)">
<line id="arm-l" class="worker-limb" x1="0" y1="0" x2="-20" y2="30" />
</g>
<!-- 右臂 -->
<g transform="translate(14, 10)">
<line id="arm-r" class="worker-limb" x1="0" y1="0" x2="20" y2="30" />
</g>
<!-- 左腿 -->
<g transform="translate(-8, 50)">
<line id="leg-l" class="worker-limb" x1="0" y1="0" x2="-10" y2="40" />
</g>
<!-- 右腿 -->
<g transform="translate(8, 50)">
<line id="leg-r" class="worker-limb" x1="0" y1="0" x2="10" y2="40" />
</g>
<!-- 头部与安全帽 -->
<circle cx="0" cy="-12" r="14" fill="#FCD34D" /> <!-- 面部 -->
<path d="M -18,-15 Q 0,-40 18,-15 Z" class="worker-head" /> <!-- 头盔 -->
<line x1="-20" y1="-15" x2="20" y2="-15" stroke="#CA8A04" stroke-width="3" stroke-linecap="round"/>
</g>
</g>
</g>
</svg>
</div>
<!-- ==================== 逻辑控制脚本 ==================== -->
<script>
document.addEventListener('DOMContentLoaded', () => {
// 定义 12 个操作步骤及时间分配 (毫秒)
// 为了凸显痛点,将第6,7,8步的时间拉长
const STEPS = [
{ id: 1, name: "现场勘查", type: "prep", duration: 1500 },
{ id: 2, name: "开票", type: "prep", duration: 1000 },
{ id: 3, name: "工作许可", type: "prep", duration: 1000 },
{ id: 4, name: "停电", type: "outage", duration: 1500 },
{ id: 5, name: "转检修", type: "outage", duration: 1000 },
{ id: 6, name: "登杆", type: "climb", duration: 3500 },
{ id: 7, name: "更换绝缘子", type: "work", duration: 3000 },
{ id: 8, name: "绑扎线缠绕", type: "work-heavy", duration: 4500 },
{ id: 9, name: "紧固", type: "work", duration: 2000 },
{ id: 10, name: "转运行", type: "descend", duration: 2500 },
{ id: 11, name: "恢复供电", type: "restore", duration: 1500 },
{ id: 12, name: "工作终结", type: "done", duration: 2000 }
];
const totalDuration = STEPS.reduce((sum, step) => sum + step.duration, 0) + 1500; // 留白1.5秒
// DOM 元素引用
const timelineContainer = document.getElementById('timeline-container');
const uiTimer = document.getElementById('ui-timer');
const uiCurrentAction = document.getElementById('ui-current-action');
const hazardAlert = document.getElementById('hazard-alert');
const workerEl = document.getElementById('worker');
const painItems = [
document.getElementById('pain-1'),
document.getElementById('pain-2'),
document.getElementById('pain-3')
];
// 1. 初始化底部 12 步时间轴 (确保字体大而清晰)
STEPS.forEach((step, index) => {
const stepEl = document.createElement('div');
stepEl.className = 'step-item';
stepEl.id = `step-node-${index}`;
// 判断是否是高危/痛点步骤来给不同的颜色标记
let isDanger = ['outage', 'climb', 'work', 'work-heavy'].includes(step.type);
if (isDanger) stepEl.classList.add('danger-step');
stepEl.innerHTML = `
<div class="step-dot"></div>
<div class="step-text">${step.id}</div>
`;
timelineContainer.appendChild(stepEl);
});
const stepNodes = document.querySelectorAll('.step-item');
// 2. 动画引擎
let startTime = null;
let currentStepIdx = -1;
function renderLoop(timestamp) {
if (!startTime) startTime = timestamp;
const elapsed = (timestamp - startTime) % totalDuration;
// 确定当前处于哪个阶段
let accTime = 0;
let newStepIdx = STEPS.length - 1;
for (let i = 0; i < STEPS.length; i++) {
if (elapsed >= accTime && elapsed < accTime + STEPS[i].duration) {
newStepIdx = i;
break;
}
accTime += STEPS[i].duration;
}
if (elapsed > totalDuration - 1500) {
newStepIdx = -1; // 休息重置期
}
// 阶段切换时更新 UI 状态
if (newStepIdx !== currentStepIdx && newStepIdx >= 0) {
currentStepIdx = newStepIdx;
const step = STEPS[currentStepIdx];
updateSystemState(currentStepIdx, step);
}
// 计算 "120分钟" 停电倒计时映射
// 停电始于步骤4(idx 3),终于步骤11(idx 10)
let displayTime = 0;
if (currentStepIdx >= 3 && currentStepIdx < 11) {
const outageStart = STEPS.slice(0, 3).reduce((s, st) => s + st.duration, 0);
const outageEnd = STEPS.slice(0, 11).reduce((s, st) => s + st.duration, 0);
const timeInOutage = elapsed - outageStart;
// 制造一种在线路绑扎阶段时间飞逝的焦灼感
const progressRatio = timeInOutage / (outageEnd - outageStart);
// 使用缓动函数让绑扎阶段时间飙升
const easedRatio = Math.pow(progressRatio, 1.2);
displayTime = Math.min(120, Math.floor(easedRatio * 120));
uiTimer.classList.add('outage');
} else if (currentStepIdx >= 11) {
displayTime = 120;
uiTimer.classList.remove('outage');
} else {
displayTime = 0;
uiTimer.classList.remove('outage');
}
uiTimer.textContent = displayTime.toString().padStart(3, '0');
requestAnimationFrame(renderLoop);
}
function updateSystemState(idx, step) {
// 更新主标题栏
uiCurrentAction.textContent = `${step.id}. ${step.name}`;
// 节点点亮逻辑
stepNodes.forEach((node, i) => {
node.className = 'step-item';
if (['outage', 'climb', 'work', 'work-heavy'].includes(STEPS[i].type)) {
node.classList.add('danger-step');
}
if (i < idx) node.classList.add('done');
if (i === idx) node.classList.add('active');
});
// 重置所有痛点高亮
painItems.forEach(p => { p.classList.remove('highlight-danger'); p.classList.remove('highlight-warn'); });
const body = document.body;
workerEl.className.baseVal = '';
hazardAlert.classList.remove('show');
// 状态机处理
switch(step.type) {
case 'prep':
body.setAttribute('data-status', 'normal');
uiCurrentAction.style.borderColor = "var(--color-safe)";
uiCurrentAction.style.color = "var(--color-safe)";
uiCurrentAction.style.boxShadow = "0 0 20px rgba(16, 185, 129, 0.3)";
break;
case 'outage':
body.setAttribute('data-status', 'outage');
uiCurrentAction.style.borderColor = "var(--color-danger)";
uiCurrentAction.style.color = "var(--color-danger)";
uiCurrentAction.style.boxShadow = "0 0 20px rgba(239, 68, 68, 0.3)";
painItems[0].classList.add('highlight-danger');
break;
case 'climb':
body.setAttribute('data-status', 'danger');
workerEl.className.baseVal = 'anim-climbing';
uiCurrentAction.style.borderColor = "var(--color-warn)";
uiCurrentAction.style.color = "var(--color-warn)";
uiCurrentAction.style.boxShadow = "0 0 20px rgba(245, 158, 11, 0.3)";
painItems[1].classList.add('highlight-warn');
hazardAlert.classList.add('show');
hazardAlert.querySelector('.alert-text').textContent = "正在登杆:存在高处坠落危险!";
break;
case 'work':
case 'work-heavy':
// 如果是绑扎阶段,特别强调耗时和工具不趁手
if(step.type === 'work-heavy') {
body.setAttribute('data-status', 'working-heavy');
painItems[2].classList.add('highlight-danger');
hazardAlert.querySelector('.alert-text').textContent = "痛点爆发:手工绑扎极度耗时,工具不趁手!";
} else {
body.setAttribute('data-status', 'danger');
painItems[1].classList.add('highlight-danger');
hazardAlert.querySelector('.alert-text').textContent = "杆上作业:存在感应电触电高危风险!";
}
workerEl.className.baseVal = 'anim-working';
uiCurrentAction.style.borderColor = "var(--color-danger)";
uiCurrentAction.style.color = "var(--color-danger)";
uiCurrentAction.style.boxShadow = "0 0 20px rgba(239, 68, 68, 0.5)";
hazardAlert.classList.add('show');
break;
case 'descend':
body.setAttribute('data-status', 'outage');
workerEl.className.baseVal = 'anim-descending';
uiCurrentAction.style.borderColor = "var(--color-warn)";
uiCurrentAction.style.color = "var(--color-warn)";
uiCurrentAction.style.boxShadow = "0 0 20px rgba(245, 158, 11, 0.3)";
break;
case 'restore':
case 'done':
body.setAttribute('data-status', 'normal');
workerEl.className.baseVal = 'anim-idle';
uiCurrentAction.style.borderColor = "var(--color-safe)";
uiCurrentAction.style.color = "var(--color-safe)";
uiCurrentAction.style.boxShadow = "0 0 20px rgba(16, 185, 129, 0.3)";
break;
}
}
// 启动自动播放引擎
requestAnimationFrame(renderLoop);
});
</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-color: #080A0F;
--grid-color: rgba(45, 55, 72, 0.2);
--text-main: #FFFFFF;
--text-muted: #94A3B8;
--color-safe: #00F0FF;
--color-warning: #FFAB00;
--color-danger: #FF2A2A;
--color-power: #39FF14;
--panel-bg: rgba(12, 16, 25, 0.85);
font-family: 'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif;
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
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;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
color: var(--text-main);
}
/* ================= 边缘 HUD 面板 (确保高对比度与高清晰度,绝对不遮挡中心) ================= */
.hud-layer {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 100;
}
.panel {
position: absolute;
background: var(--panel-bg);
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
padding: 16px;
border-radius: 6px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}
/* 字体清晰度保障:使用 14px,字重适中,极高对比度 */
.panel-text {
font-size: 14px;
line-height: 1.6;
color: var(--text-main);
text-shadow: 0 1px 2px rgba(0,0,0,0.8);
}
.panel-title {
font-size: 14px;
font-weight: bold;
color: var(--color-danger);
margin-bottom: 8px;
border-bottom: 1px solid rgba(255,42,42,0.3);
padding-bottom: 4px;
letter-spacing: 1px;
}
.top-left {
top: 24px;
left: 24px;
width: 280px;
border-left: 4px solid var(--color-danger);
}
.top-right {
top: 24px;
right: 24px;
text-align: right;
border-right: 4px solid var(--color-warning);
min-width: 200px;
}
/* 动态时间显示 */
.timer-display {
font-family: 'Courier New', Courier, monospace;
font-size: 36px;
font-weight: bold;
color: var(--color-danger);
text-shadow: 0 0 10px rgba(255, 42, 42, 0.5);
display: flex;
align-items: baseline;
justify-content: flex-end;
gap: 4px;
}
.timer-unit {
font-size: 14px;
color: var(--text-muted);
}
.bottom-center {
bottom: 24px;
left: 50%;
transform: translateX(-50%);
width: 90%;
max-width: 1000px;
display: flex;
justify-content: space-between;
padding: 12px 24px;
background: linear-gradient(180deg, rgba(12,16,25,0.6) 0%, rgba(12,16,25,0.95) 100%);
border-top: 2px solid var(--color-safe);
}
/* 进度条指示器 */
.step-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
opacity: 0.4;
transition: all 0.3s ease;
}
.step-item.active {
opacity: 1;
transform: translateY(-5px);
}
.step-item.done {
opacity: 0.7;
}
.step-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--text-muted);
transition: all 0.3s ease;
}
.step-item.active .step-dot {
background: var(--color-warning);
box-shadow: 0 0 12px var(--color-warning);
}
.step-item.active.danger-step .step-dot {
background: var(--color-danger);
box-shadow: 0 0 12px var(--color-danger);
}
.step-name {
font-size: 12px;
font-weight: bold;
writing-mode: vertical-rl;
letter-spacing: 2px;
color: var(--text-main);
}
/* 风险警告横幅 */
.alert-banner {
position: absolute;
top: 100px;
left: 50%;
transform: translateX(-50%);
background: rgba(255, 42, 42, 0.15);
border: 1px solid var(--color-danger);
color: var(--color-danger);
padding: 8px 24px;
font-size: 14px;
font-weight: bold;
border-radius: 20px;
opacity: 0;
transition: opacity 0.3s;
backdrop-filter: blur(4px);
display: flex;
align-items: center;
gap: 8px;
}
.alert-banner.show {
opacity: 1;
animation: pulseBanner 1s infinite alternate;
}
@keyframes pulseBanner {
from { box-shadow: 0 0 5px rgba(255,42,42,0.2); }
to { box-shadow: 0 0 20px rgba(255,42,42,0.6); }
}
/* ================= 核心 SVG 动画区域 ================= */
.svg-container {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
svg {
width: 100%;
height: 100%;
max-width: 1200px;
max-height: 900px;
overflow: visible;
}
/* SVG 元素样式 */
.line-power { stroke: var(--color-power); transition: stroke 0.5s; }
.line-power.outage { stroke: var(--text-muted); opacity: 0.3; }
.energy-dash { stroke-dasharray: 8 16; animation: flow 1s linear infinite; }
.energy-dash.outage { opacity: 0; animation: none; }
@keyframes flow { to { stroke-dashoffset: -24; } }
/* ================= 人工作业动画控制 ================= */
#worker {
transform: translate(520px, 700px); /* 初始在地面 */
transition: transform 0s linear;
}
/* 各个关节的变换原点 */
#body-group { transform-origin: center bottom; }
.arm-l { transform-origin: top right; }
.arm-r { transform-origin: top left; }
.leg-l, .leg-r { transform-origin: top center; }
/* 状态 1: 登杆 (平移 + 四肢交替) */
.state-climb #worker { animation: climbMove 3s linear forwards; }
.state-climb .arm-l { animation: climbArmL 0.6s infinite alternate; }
.state-climb .arm-r { animation: climbArmR 0.6s infinite alternate; }
.state-climb .leg-l { animation: climbLegL 0.6s infinite alternate; }
.state-climb .leg-r { animation: climbLegR 0.6s infinite alternate; }
@keyframes climbMove {
0% { transform: translate(520px, 700px); }
100% { transform: translate(520px, 250px); }
}
@keyframes climbArmL { 0% { transform: rotate(-40deg); } 100% { transform: rotate(10deg); } }
@keyframes climbArmR { 0% { transform: rotate(10deg); } 100% { transform: rotate(-40deg); } }
@keyframes climbLegL { 0% { transform: rotate(-20deg) translateY(0); } 100% { transform: rotate(30deg) translateY(-15px); } }
@keyframes climbLegR { 0% { transform: rotate(30deg) translateY(-15px); } 100% { transform: rotate(-20deg) translateY(0); } }
/* 状态 2: 杆上作业 (剧烈手部动作表示绑扎耗时/工具不趁手) */
.state-work #worker { transform: translate(520px, 250px); }
.state-work #body-group { animation: workBody 0.8s infinite alternate ease-in-out; }
.state-work .arm-l { animation: workArmL 0.3s infinite alternate; }
.state-work .arm-r { animation: workArmR 0.4s infinite alternate; }
.state-work .leg-l { transform: rotate(15deg); }
.state-work .leg-r { transform: rotate(-15deg); }
@keyframes workBody { 0% { transform: rotate(-3deg); } 100% { transform: rotate(3deg); } }
@keyframes workArmL { 0% { transform: rotate(-90deg); } 100% { transform: rotate(-45deg); } }
@keyframes workArmR { 0% { transform: rotate(-135deg); } 100% { transform: rotate(-30deg); } }
/* 状态 3: 下降 */
.state-descend #worker { animation: descendMove 2s linear forwards; }
.state-descend .arm-l { animation: climbArmL 0.5s infinite alternate reverse; }
.state-descend .arm-r { animation: climbArmR 0.5s infinite alternate reverse; }
.state-descend .leg-l { animation: climbLegL 0.5s infinite alternate reverse; }
.state-descend .leg-r { animation: climbLegR 0.5s infinite alternate reverse; }
@keyframes descendMove {
0% { transform: translate(520px, 250px); }
100% { transform: translate(520px, 700px); }
}
/* 危险特效:坠落光圈与触电电弧 */
.hazard-fall {
opacity: 0;
pointer-events: none;
transform-origin: 500px 230px;
}
.state-work .hazard-fall, .state-climb .hazard-fall {
animation: expandDanger 1.5s infinite ease-out;
}
@keyframes expandDanger {
0% { transform: scale(0.5); opacity: 1; stroke-width: 4px; }
100% { transform: scale(3); opacity: 0; stroke-width: 1px; }
}
.hazard-shock { opacity: 0; pointer-events: none; }
.state-work .hazard-shock { animation: flashShock 2.5s infinite; }
@keyframes flashShock {
0%, 90%, 100% { opacity: 0; }
92%, 96% { opacity: 1; filter: drop-shadow(0 0 12px var(--color-safe)); }
94%, 98% { opacity: 0.3; }
}
/* 时间流逝特效 (作业时沙漏) */
.time-loss { opacity: 0; transform-origin: 560px 200px; }
.state-work .time-loss { animation: spinTime 3s linear infinite, fadeTime 0.5s forwards; }
@keyframes spinTime { 100% { transform: rotate(360deg); } }
@keyframes fadeTime { to { opacity: 1; } }
</style>
</head>
<body>
<!-- HUD 信息层 -->
<div class="hud-layer">
<!-- 左侧:痛点分析 -->
<div class="panel top-left">
<div class="panel-title">12节点传统作业痛点诊断</div>
<div class="panel-text">
当前作业依赖人工与大面积停电:<br>
1. <span style="color:var(--color-danger); font-weight:bold;">停电影响大:</span>用户感知极差。<br>
2. <span style="color:var(--color-warning); font-weight:bold;">高坠/触电风险:</span>杆上作业缺乏防护与预警。<br>
3. <span style="color:var(--color-danger); font-weight:bold;">绑扎耗时长:</span>工具不趁手,纯手工缠绕极度费时。
</div>
</div>
<!-- 右侧:动态时间监控 -->
<div class="panel top-right">
<div class="panel-title" style="border-bottom: 1px solid rgba(255,171,0,0.3); color: var(--color-warning);">单次故障平均停电</div>
<div class="timer-display">
<span id="ui-time">0</span>
<span class="timer-unit">MIN</span>
</div>
<div class="panel-text" style="color:var(--text-muted); font-size:12px; margin-top:4px;">
当前状态: <span id="ui-status" style="color:var(--color-safe); font-weight:bold;">正常运行</span>
</div>
</div>
<!-- 顶部居中:高危告警 -->
<div class="alert-banner" id="alert-banner">
⚠️ <span id="alert-text">警告:高危作业进行中</span>
</div>
<!-- 底部:12步时间轴 -->
<div class="panel bottom-center" id="timeline">
<!-- 由 JS 渲染 -->
</div>
</div>
<!-- 核心动画画布 -->
<div class="svg-container">
<svg viewBox="0 0 1000 800" preserveAspectRatio="xMidYMid meet" id="svg-canvas">
<!-- 背景环境 -->
<g id="env">
<!-- 地面 -->
<line x1="100" y1="700" x2="900" y2="700" stroke="#1E293B" stroke-width="4"/>
<line x1="200" y1="720" x2="800" y2="720" stroke="#0F172A" stroke-width="2"/>
<!-- 主电线杆 -->
<polygon points="485,700 515,700 508,120 492,120" fill="#1E293B" stroke="#0F172A" stroke-width="2"/>
<!-- 顶部横担 -->
<rect x="350" y="160" width="300" height="20" fill="#334155" rx="3" stroke="#0F172A"/>
<path d="M 380,180 L 500,280 M 620,180 L 500,280" stroke="#334155" stroke-width="8" stroke-linecap="round"/>
<!-- 绝缘子 -->
<!-- 左 -->
<rect x="370" y="120" width="20" height="40" fill="#94A3B8" rx="4"/>
<path d="M 360,130 L 400,130 M 360,145 L 400,145" stroke="#475569" stroke-width="4" stroke-linecap="round"/>
<!-- 中 -->
<rect x="490" y="120" width="20" height="40" fill="#94A3B8" rx="4"/>
<path d="M 480,130 L 520,130 M 480,145 L 520,145" stroke="#475569" stroke-width="4" stroke-linecap="round"/>
<!-- 右 -->
<rect x="610" y="120" width="20" height="40" fill="#94A3B8" rx="4"/>
<path d="M 600,130 L 640,130 M 600,145 L 640,145" stroke="#475569" stroke-width="4" stroke-linecap="round"/>
</g>
<!-- 输电线路 -->
<g id="wires">
<!-- 左相 -->
<path class="line-power wire-l" d="M -50,120 Q 200,180 380,120" fill="none" stroke-width="5"/>
<path class="energy-dash wire-l" d="M -50,120 Q 200,180 380,120" fill="none" stroke="#FFF" stroke-width="2"/>
<!-- 中相 (跨接) -->
<path class="line-power wire-c" d="M 380,120 Q 500,140 620,120" fill="none" stroke-width="5"/>
<!-- 右相 -->
<path class="line-power wire-r" d="M 620,120 Q 800,180 1050,120" fill="none" stroke-width="5"/>
<path class="energy-dash wire-r" d="M 620,120 Q 800,180 1050,120" fill="none" stroke="#FFF" stroke-width="2"/>
</g>
<!-- 危险指示特效 -->
<!-- 坠落红圈 -->
<circle class="hazard-fall" cx="500" cy="230" r="60" fill="none" stroke="var(--color-danger)"/>
<circle class="hazard-fall" cx="500" cy="230" r="60" fill="none" stroke="var(--color-warning)" style="animation-delay: 0.75s;"/>
<!-- 触电电弧 -->
<path class="hazard-shock" d="M 490,120 L 470,160 L 510,180 L 490,220" fill="none" stroke="#FFF" stroke-width="4"/>
<path class="hazard-shock" d="M 510,120 L 530,150 L 490,170 L 520,210" fill="none" stroke="#FFF" stroke-width="3" style="animation-delay: 0.3s;"/>
<!-- 耗时沙漏图标 -->
<g class="time-loss" stroke="var(--color-warning)" stroke-width="2" fill="none">
<path d="M 550,180 L 570,180 L 560,195 Z" fill="rgba(255,171,0,0.5)"/>
<path d="M 550,210 L 570,210 L 560,195 Z"/>
<path d="M 545,180 L 575,180 M 545,210 L 575,210"/>
<text x="580" y="200" fill="var(--color-warning)" font-size="12" font-family="monospace" stroke="none">TIME LOSS</text>
</g>
<!-- 作业人员骨骼与外观 -->
<g id="worker">
<g id="body-group">
<!-- 安全带连线 (虚拟展示) -->
<path d="M -15,10 Q -40,15 -15,40" fill="none" stroke="#64748B" stroke-width="2" stroke-dasharray="4 4"/>
<!-- 躯干 -->
<rect x="-12" y="0" width="24" height="40" fill="#FF5722" rx="6"/>
<rect x="-14" y="20" width="28" height="6" fill="#1E293B"/> <!-- 安全腰带 -->
<!-- 头部 -->
<circle cx="0" cy="-10" r="10" fill="#FDE047"/>
<path d="M -15,-12 Q 0,-30 15,-12 Z" fill="#EAB308"/> <!-- 安全帽 -->
<line x1="-15" y1="-12" x2="15" y2="-12" stroke="#CA8A04" stroke-width="2"/>
<!-- 左臂 -->
<g transform="translate(-12, 6)">
<line class="arm-l" x1="0" y1="0" x2="-20" y2="25" stroke="#FF5722" stroke-width="8" stroke-linecap="round"/>
</g>
<!-- 右臂 (拿着扳手/绑扎线) -->
<g transform="translate(12, 6)">
<g class="arm-r">
<line x1="0" y1="0" x2="20" y2="25" stroke="#FF5722" stroke-width="8" stroke-linecap="round"/>
<path d="M 20,25 L 30,30 M 20,25 L 25,35" stroke="#E2E8F0" stroke-width="3"/> <!-- 工具 -->
</g>
</g>
<!-- 左腿 -->
<g transform="translate(-6, 40)">
<line class="leg-l" x1="0" y1="0" x2="-10" y2="35" stroke="#FF5722" stroke-width="9" stroke-linecap="round"/>
</g>
<!-- 右腿 -->
<g transform="translate(6, 40)">
<line class="leg-r" x1="0" y1="0" x2="10" y2="35" stroke="#FF5722" stroke-width="9" stroke-linecap="round"/>
</g>
</g>
</g>
</svg>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// 定义 12 个操作步骤及动画持续时间 (为了演示,将 120 分钟映射到约 20 秒循环)
// 重点痛点:步骤 7, 8, 9 分配更长时间
const STEPS = [
{ name: "现场勘查", type: "ground", duration: 1500 },
{ name: "开票", type: "ground", duration: 1000 },
{ name: "工作许可", type: "ground", duration: 1000 },
{ name: "停电", type: "outage", duration: 1500, alert: "大面积停电区域扩大" },
{ name: "转检修", type: "outage", duration: 1000 },
{ name: "登杆", type: "climb", duration: 3000, alert: "高处作业:坠落风险" },
{ name: "更换绝缘子", type: "work", duration: 2500, alert: "触电风险 / 作业困难" },
{ name: "绑扎线缠绕", type: "work", duration: 4500, alert: "痛点:纯手工缠绕极度耗时" },
{ name: "紧固", type: "work", duration: 2000, alert: "工具不趁手" },
{ name: "转运行", type: "descend", duration: 2000 },
{ name: "恢复供电", type: "power-on", duration: 1500 },
{ name: "工作终结", type: "ground", duration: 1500 }
];
const timelineEl = document.getElementById('timeline');
const uiTime = document.getElementById('ui-time');
const uiStatus = document.getElementById('ui-status');
const svgCanvas = document.getElementById('svg-canvas');
const wires = document.querySelectorAll('.line-power, .energy-dash');
const alertBanner = document.getElementById('alert-banner');
const alertText = document.getElementById('alert-text');
// 渲染时间轴
STEPS.forEach((step, index) => {
const el = document.createElement('div');
el.className = 'step-item';
el.id = `step-ui-${index}`;
el.innerHTML = `
<div class="step-dot"></div>
<div class="step-name">${index + 1}.${step.name}</div>
`;
timelineEl.appendChild(el);
});
const stepEls = document.querySelectorAll('.step-item');
const TOTAL_LOOP_TIME = STEPS.reduce((a, b) => a + b.duration, 0) + 1500; // 留白1.5秒
// 动画核心引擎
let startTimestamp = null;
let lastStepIdx = -1;
let currentDisplayTime = 0; // 用于模拟 120 分钟
function render(timestamp) {
if (!startTimestamp) startTimestamp = timestamp;
const elapsed = (timestamp - startTimestamp) % TOTAL_LOOP_TIME;
// 查找当前步骤
let accTime = 0;
let currentIdx = -1;
for (let i = 0; i < STEPS.length; i++) {
if (elapsed >= accTime && elapsed < accTime + STEPS[i].duration) {
currentIdx = i;
break;
}
accTime += STEPS[i].duration;
}
if (elapsed >= accTime) {
currentIdx = STEPS.length - 1; // 保持在最后一步直到循环
}
// 计算模拟的时间进度 (仅在停电步骤 3 到 10 期间累加到 120)
// 使时间在 "绑扎线缠绕" (idx 7) 时飞速增加,表现出耗时长
let targetTime = 0;
if (currentIdx >= 3 && currentIdx <= 10) {
const outageStart = STEPS.slice(0, 3).reduce((a, b) => a + b.duration, 0);
const outageEnd = STEPS.slice(0, 11).reduce((a, b) => a + b.duration, 0);
const outageElapsed = Math.max(0, elapsed - outageStart);
// 非线性时间映射
const rawRatio = outageElapsed / (outageEnd - outageStart);
// 添加一个 ease-in-out 曲线或者直接使用简单的分段逻辑
let timeRatio = rawRatio;
if (currentIdx === 7) { // 绑扎期间时间激增
timeRatio = rawRatio * 1.2;
}
targetTime = Math.min(120, Math.max(0, timeRatio * 120));
} else if (currentIdx > 10) {
targetTime = 120;
}
// 平滑过渡显示时间
currentDisplayTime += (targetTime - currentDisplayTime) * 0.1;
uiTime.textContent = Math.round(currentDisplayTime).toString().padStart(3, '0');
// 颜色警告:时间越长越红
if (currentDisplayTime > 90) {
uiTime.style.color = 'var(--color-danger)';
} else if (currentDisplayTime > 45) {
uiTime.style.color = 'var(--color-warning)';
} else {
uiTime.style.color = 'var(--color-safe)';
}
// 阶段切换触发视觉更新
if (currentIdx !== lastStepIdx && currentIdx >= 0) {
lastStepIdx = currentIdx;
const step = STEPS[currentIdx];
// 更新时间轴 UI
stepEls.forEach((el, i) => {
el.className = 'step-item';
if (i < currentIdx) el.classList.add('done');
if (i === currentIdx) {
el.classList.add('active');
if (step.type === 'outage' || step.type === 'work' || step.type === 'climb') {
el.classList.add('danger-step');
}
}
});
uiStatus.textContent = `${currentIdx + 1}. ${step.name}`;
// 清除旧状态
svgCanvas.className.baseVal = '';
alertBanner.classList.remove('show');
// 状态机处理
if (step.type === 'outage' || step.type === 'climb' || step.type === 'work' || step.type === 'descend') {
wires.forEach(w => w.classList.add('outage'));
uiStatus.style.color = 'var(--color-danger)';
} else {
wires.forEach(w => w.classList.remove('outage'));
uiStatus.style.color = 'var(--color-safe)';
}
// 绑定对应 CSS 动画类名到 SVG 根元素
if (step.type === 'climb') {
svgCanvas.className.baseVal = 'state-climb';
} else if (step.type === 'work') {
svgCanvas.className.baseVal = 'state-work';
} else if (step.type === 'descend') {
svgCanvas.className.baseVal = 'state-descend';
}
// 警告横幅
if (step.alert) {
alertText.textContent = `警告: ${step.alert}`;
alertBanner.classList.add('show');
}
}
// 循环结束重置
if (elapsed > TOTAL_LOOP_TIME - 100) {
lastStepIdx = -1;
currentDisplayTime = 0;
}
requestAnimationFrame(render);
}
// 启动动画
requestAnimationFrame(render);
});
</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>TRIZ 技术系统功能模型与痛点分析</title>
<style>
:root {
/* 深度工业诊断色板 */
--bg-base: #060913;
--grid-color: rgba(30, 58, 138, 0.2);
--hud-border: rgba(56, 189, 248, 0.3);
--hud-bg: rgba(6, 9, 19, 0.85);
/* 状态语义色 */
--func-useful: #10B981; /* 正常有用 - 翠绿 */
--func-insufficient: #F59E0B; /* 不足功能 - 警告黄 */
--func-harmful: #EF4444; /* 有害功能 - 警报红 */
--node-target: #3B82F6; /* 作用对象 - 亮蓝 */
--node-system: #6366F1; /* 系统组件 - 靛蓝 */
--node-super: #8B5CF6; /* 超系统 - 紫色 */
--text-main: #F8FAFC;
--text-dim: #94A3B8;
--font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
--font-sans: 'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif;
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: var(--bg-base);
background-image:
radial-gradient(circle at 50% 50%, rgba(30,58,138,0.1) 0%, transparent 60%),
linear-gradient(var(--grid-color) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
background-size: 100% 100%, 40px 40px, 40px 40px;
font-family: var(--font-sans);
color: var(--text-main);
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
/* ==================== 边缘 HUD 布局 (严控字号与位置,防止遮挡) ==================== */
.hud-layer {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 100;
}
.panel {
position: absolute;
background: var(--hud-bg);
border: 1px solid var(--hud-border);
backdrop-filter: blur(8px);
padding: 16px;
border-radius: 4px;
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
font-size: 12px;
line-height: 1.5;
}
.panel-title {
font-size: 13px;
font-weight: bold;
color: var(--node-target);
margin-bottom: 10px;
border-bottom: 1px solid rgba(56,189,248,0.3);
padding-bottom: 4px;
letter-spacing: 1px;
text-transform: uppercase;
}
.top-left { top: 20px; left: 20px; width: 260px; border-left: 3px solid var(--node-target); }
.top-right { top: 20px; right: 20px; width: 300px; border-right: 3px solid var(--func-harmful); }
.bottom-left { bottom: 20px; left: 20px; width: 220px; }
.bottom-right { bottom: 20px; right: 20px; text-align: right; }
.data-row { display: flex; justify-content: space-between; margin-bottom: 6px; }
.text-dim { color: var(--text-dim); }
.text-harmful { color: var(--func-harmful); font-weight: bold; }
.text-insufficient { color: var(--func-insufficient); font-weight: bold; }
.text-useful { color: var(--func-useful); }
/* 图例样式 */
.legend-item { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; font-family: var(--font-mono); font-size: 11px;}
.legend-line { width: 20px; height: 2px; }
.legend-node { width: 10px; height: 10px; border-radius: 50%; }
/* 核心痛点高亮列表 */
.core-problem-list { margin: 0; padding-left: 16px; color: var(--func-harmful); }
.core-problem-list li { margin-bottom: 6px; opacity: 0.4; transition: opacity 0.3s; }
.core-problem-list li.active { opacity: 1; text-shadow: 0 0 8px rgba(239, 68, 68, 0.6); }
/* 系统状态打字机 */
.sys-status { font-family: var(--font-mono); font-size: 14px; color: var(--node-target); }
.blink-cursor { animation: blink 1s step-end infinite; }
@keyframes blink { 50% { opacity: 0; } }
/* ==================== 核心 SVG 动画区 ==================== */
.svg-container {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
svg {
width: 100%;
height: 100%;
max-width: 1200px;
max-height: 900px;
overflow: visible;
}
/* SVG 节点基础样式 */
.node-circle { transition: all 0.5s; stroke-width: 2; }
.node-text { font-family: var(--font-sans); font-size: 14px; fill: var(--text-main); text-anchor: middle; font-weight: bold; pointer-events: none; text-shadow: 0 2px 4px rgba(0,0,0,0.8); }
.node-sub { font-size: 10px; fill: var(--text-dim); text-anchor: middle; pointer-events: none; }
/* SVG 连线基础样式 */
.link-path { fill: none; stroke-width: 2; transition: all 0.5s; opacity: 0.2; }
.link-label { font-size: 12px; font-family: var(--font-sans); fill: var(--text-dim); text-anchor: middle; opacity: 0; transition: opacity 0.5s; }
/* 动画粒子流 */
.link-flow { fill: none; stroke-width: 2; stroke-dasharray: 6 12; opacity: 0; pointer-events: none; }
/* ---------------- 阶段状态:功能模型分析扫描 ---------------- */
/* 正常有用功能 */
.state-analysis .link-useful { stroke: var(--func-useful); opacity: 0.6; }
.state-analysis .link-useful + .link-flow { stroke: var(--func-useful); opacity: 1; animation: flowAnim 1s linear infinite; }
.state-analysis .label-useful { opacity: 1; fill: var(--func-useful); }
/* 不足功能 (警告黄) */
.state-analysis .link-insufficient { stroke: var(--func-insufficient); stroke-dasharray: 4 4; opacity: 0.8; }
.state-analysis .link-insufficient + .link-flow { stroke: var(--func-insufficient); opacity: 1; animation: flowAnim 2s linear infinite reverse; } /* 慢速反馈 */
.state-analysis .label-insufficient { opacity: 1; fill: var(--func-insufficient); }
/* 有害功能 (危险红) */
.state-analysis .link-harmful { stroke: var(--func-harmful); opacity: 0.8; }
.state-analysis .link-harmful + .link-flow { stroke: var(--func-harmful); opacity: 1; animation: flowAnim 0.5s linear infinite; stroke-width: 3;}
.state-analysis .label-harmful { opacity: 1; fill: var(--func-harmful); font-weight: bold; }
/* ---------------- 阶段状态:痛点爆发 (红色警报) ---------------- */
.state-alert .link-harmful { stroke: var(--func-harmful); stroke-width: 4; filter: drop-shadow(0 0 8px var(--func-harmful)); }
.state-alert .link-insufficient { stroke: var(--func-insufficient); stroke-width: 3; filter: drop-shadow(0 0 6px var(--func-insufficient)); }
.state-alert .node-worker, .state-alert .node-wire {
animation: nodePulseRed 1.5s infinite alternate;
}
/* 脉冲与流动动画 */
@keyframes flowAnim { to { stroke-dashoffset: -36; } }
@keyframes nodePulseRed {
0% { filter: drop-shadow(0 0 5px var(--func-harmful)); stroke: var(--func-harmful); }
100% { filter: drop-shadow(0 0 20px var(--func-harmful)); stroke: var(--func-harmful); }
}
@keyframes nodePulseAmber {
0% { filter: drop-shadow(0 0 5px var(--func-insufficient)); stroke: var(--func-insufficient); }
100% { filter: drop-shadow(0 0 20px var(--func-insufficient)); stroke: var(--func-insufficient); }
}
/* 错误波纹涟漪 */
.error-ripple { fill: none; stroke: var(--func-harmful); stroke-width: 2; opacity: 0; transform-origin: center; pointer-events: none;}
.state-alert .error-ripple.active { animation: rippleEffect 2s infinite ease-out; }
@keyframes rippleEffect {
0% { transform: scale(1); opacity: 0.8; }
100% { transform: scale(3.5); opacity: 0; }
}
</style>
</head>
<body>
<!-- HUD 边缘信息层 -->
<div class="hud-layer">
<!-- 左上:技术系统定义 -->
<div class="panel top-left">
<div class="panel-title">技术系统定义</div>
<div class="data-row"><span class="text-dim">系统名称</span></div>
<div style="font-weight: bold; margin-bottom: 10px; color: var(--text-main);">传统停电检修绑扎作业系统</div>
<div class="data-row"><span class="text-dim">总功能</span></div>
<div style="margin-bottom: 10px; color: var(--node-target);">固定导线 (防松脱)</div>
<div class="data-row"><span class="text-dim">唯一作用对象</span></div>
<div style="font-family: var(--font-mono); color: var(--node-target); font-size: 14px; font-weight:bold;">导线 [TARGET]</div>
</div>
<!-- 左下:图例 -->
<div class="panel bottom-left">
<div class="panel-title">功能模型图例</div>
<div class="legend-item"><div class="legend-node" style="background: var(--node-target);"></div> 作用对象</div>
<div class="legend-item"><div class="legend-node" style="background: var(--node-system);"></div> 系统组件</div>
<div class="legend-item"><div class="legend-node" style="background: var(--node-super);"></div> 超系统组件</div>
<div style="height: 1px; background: rgba(255,255,255,0.1); margin: 6px 0;"></div>
<div class="legend-item"><div class="legend-line" style="background: var(--func-useful);"></div> 正常有用功能</div>
<div class="legend-item"><div class="legend-line" style="border-top: 2px dashed var(--func-insufficient);"></div> 不足功能</div>
<div class="legend-item"><div class="legend-line" style="background: var(--func-harmful);"></div> 有害功能</div>
</div>
<!-- 右上:核心痛点监控 (TRIZ 矛盾提取) -->
<div class="panel top-right">
<div class="panel-title" style="border-color: var(--func-harmful); color: var(--func-harmful);">核心问题与矛盾总结</div>
<ul class="core-problem-list" id="problem-list">
<li id="prob-1">【不足】绑扎线固定导线可靠性差</li>
<li id="prob-2">【有害】作业人员登高存在高坠风险</li>
<li id="prob-3">【有害】接近高压导线存在触电风险</li>
<li id="prob-4">【有害】断路器切断电源导致负荷停电</li>
</ul>
</div>
<!-- 右下:动态分析状态 -->
<div class="panel bottom-right" style="background: transparent; border: none; box-shadow: none;">
<div class="text-dim" style="font-size: 10px; margin-bottom: 4px;">ANALYSIS STATUS</div>
<div class="sys-status" id="sys-status">INITIALIZING SYSTEM<span class="blink-cursor">_</span></div>
</div>
</div>
<!-- 核心功能模型动画区 -->
<div class="svg-container">
<svg viewBox="0 0 1000 700" preserveAspectRatio="xMidYMid meet" id="triz-svg">
<defs>
<!-- 滤镜定义 -->
<filter id="glow-useful"><feGaussianBlur stdDeviation="3" result="blur" /><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<filter id="glow-harmful"><feGaussianBlur stdDeviation="5" result="blur" /><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<filter id="glow-insufficient"><feGaussianBlur stdDeviation="4" result="blur" /><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<!-- 箭头标记定义 -->
<marker id="arrow-useful" viewBox="0 0 10 10" refX="28" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--func-useful)" />
</marker>
<marker id="arrow-harmful" viewBox="0 0 10 10" refX="28" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--func-harmful)" />
</marker>
<marker id="arrow-insufficient" viewBox="0 0 10 10" refX="28" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--func-insufficient)" />
</marker>
</defs>
<!-- ================== 连线层 (功能作用流) ================== -->
<g id="links">
<!-- 1. 作业人员 -> 登高 -> 杆塔 (有害) -->
<path d="M 250 400 Q 300 550 450 550" class="link-path link-harmful" marker-end="url(#arrow-harmful)"/>
<path d="M 250 400 Q 300 550 450 550" class="link-flow"/>
<text x="320" y="520" class="link-label label-harmful">产生高坠风险</text>
<!-- 2. 作业人员 -> 接近 -> 高压导线 (有害) -->
<path d="M 250 400 Q 350 350 460 350" class="link-path link-harmful" marker-end="url(#arrow-harmful)"/>
<path d="M 250 400 Q 350 350 460 350" class="link-flow"/>
<text x="350" y="365" class="link-label label-harmful">产生触电风险</text>
<!-- 3. 作业人员 -> 缠绕 -> 绑扎线 (有用) -->
<path d="M 250 400 Q 500 450 710 400" class="link-path link-useful" marker-end="url(#arrow-useful)"/>
<path d="M 250 400 Q 500 450 710 400" class="link-flow"/>
<text x="500" y="440" class="link-label label-useful">缠绕操作</text>
<!-- 4. 绑扎线 -> 固定 -> 导线 (不足) -->
<path d="M 750 400 Q 650 350 540 350" class="link-path link-insufficient" marker-end="url(#arrow-insufficient)"/>
<path d="M 750 400 Q 650 350 540 350" class="link-flow"/>
<text x="650" y="375" class="link-label label-insufficient">易松动脱落</text>
<!-- 5. 绝缘操作杆 -> 传递力 -> 绑扎线 (不足) -->
<path d="M 750 550 Q 750 475 750 440" class="link-path link-insufficient" marker-end="url(#arrow-insufficient)"/>
<path d="M 750 550 Q 750 475 750 440" class="link-flow"/>
<text x="800" y="500" class="link-label label-insufficient">反馈差/精度低</text>
<!-- 6. 安全工器具 -> 防护 -> 作业人员 (不足) -->
<path d="M 250 250 Q 250 325 250 360" class="link-path link-insufficient" marker-end="url(#arrow-insufficient)"/>
<path d="M 250 250 Q 250 325 250 360" class="link-flow"/>
<text x="180" y="310" class="link-label label-insufficient">防护繁琐</text>
<!-- 7. 接地线 -> 释放 -> 导线感应电 (有用) -->
<path d="M 500 200 Q 500 275 500 310" class="link-path link-useful" marker-end="url(#arrow-useful)"/>
<path d="M 500 200 Q 500 275 500 310" class="link-flow"/>
<text x="540" y="260" class="link-label label-useful">释放感应电</text>
<!-- 8. 断路器 -> 切断 -> 电源 (有害/损失) -->
<path d="M 850 200 Q 750 200 640 200" class="link-path link-harmful" marker-end="url(#arrow-harmful)"/>
<path d="M 850 200 Q 750 200 640 200" class="link-flow"/>
<text x="750" y="190" class="link-label label-harmful">导致停电损失</text>
<!-- 关联:电源 -> 负荷 -->
<path d="M 560 200 L 440 200" class="link-path link-useful" marker-end="url(#arrow-useful)"/>
<text x="500" y="190" class="link-label label-useful">供电</text>
</g>
<!-- ================== 节点层 ================== -->
<g id="nodes">
<!-- 核心:导线 (唯一作用对象) -->
<g transform="translate(500, 350)">
<!-- 错误波纹爆发位置 -->
<circle class="error-ripple" cx="0" cy="0" r="30" id="ripple-wire"/>
<circle cx="0" cy="0" r="35" fill="rgba(59, 130, 246, 0.2)" stroke="var(--node-target)" class="node-circle node-wire"/>
<circle cx="0" cy="0" r="25" fill="var(--node-target)" />
<text y="5" class="node-text" style="fill:#fff;">导线</text>
<text y="55" class="node-sub">作用对象</text>
</g>
<!-- 系统组件 -->
<!-- 作业人员 -->
<g transform="translate(250, 400)">
<circle class="error-ripple" cx="0" cy="0" r="30" id="ripple-worker"/>
<circle cx="0" cy="0" r="35" fill="rgba(99, 102, 241, 0.2)" stroke="var(--node-system)" class="node-circle node-worker"/>
<text y="5" class="node-text">作业人员</text>
<text y="55" class="node-sub">系统组件</text>
</g>
<!-- 绑扎线 -->
<g transform="translate(750, 400)">
<circle class="error-ripple" cx="0" cy="0" r="30" id="ripple-binding"/>
<circle cx="0" cy="0" r="35" fill="rgba(99, 102, 241, 0.2)" stroke="var(--node-system)" class="node-circle"/>
<text y="5" class="node-text">绑扎线</text>
<text y="55" class="node-sub">系统组件</text>
</g>
<!-- 绝缘操作杆 -->
<g transform="translate(750, 550)">
<circle cx="0" cy="0" r="30" fill="rgba(99, 102, 241, 0.1)" stroke="var(--node-system)" class="node-circle"/>
<text y="5" class="node-text">绝缘操作杆</text>
</g>
<!-- 安全工器具 -->
<g transform="translate(250, 250)">
<circle cx="0" cy="0" r="30" fill="rgba(99, 102, 241, 0.1)" stroke="var(--node-system)" class="node-circle"/>
<text y="5" class="node-text">安全工器具</text>
</g>
<!-- 接地线 -->
<g transform="translate(500, 200)">
<circle cx="0" cy="0" r="30" fill="rgba(99, 102, 241, 0.1)" stroke="var(--node-system)" class="node-circle"/>
<text y="5" class="node-text">接地线</text>
</g>
<!-- 超系统组件 -->
<!-- 杆塔/绝缘子 -->
<g transform="translate(500, 550)">
<circle cx="0" cy="0" r="35" fill="rgba(139, 92, 246, 0.2)" stroke="var(--node-super)" class="node-circle"/>
<text y="-5" class="node-text">杆塔 /</text>
<text y="15" class="node-text">绝缘子</text>
<text y="55" class="node-sub">超系统</text>
</g>
<!-- 高压电源 -->
<g transform="translate(600, 200)">
<circle cx="0" cy="0" r="35" fill="rgba(139, 92, 246, 0.2)" stroke="var(--node-super)" class="node-circle"/>
<text y="5" class="node-text">高压电源</text>
<text y="-45" class="node-sub">超系统</text>
</g>
<!-- 断路器 -->
<g transform="translate(850, 200)">
<circle class="error-ripple" cx="0" cy="0" r="30" id="ripple-breaker"/>
<circle cx="0" cy="0" r="35" fill="rgba(139, 92, 246, 0.2)" stroke="var(--node-super)" class="node-circle"/>
<text y="5" class="node-text">断路器</text>
<text y="-45" class="node-sub">超系统</text>
</g>
<!-- 供电负荷 -->
<g transform="translate(400, 200)">
<circle cx="0" cy="0" r="35" fill="rgba(139, 92, 246, 0.2)" stroke="var(--node-super)" class="node-circle"/>
<text y="5" class="node-text">供电负荷</text>
<text y="-45" class="node-sub">超系统</text>
</g>
</g>
</svg>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const svgContainer = document.getElementById('triz-svg');
const statusText = document.getElementById('sys-status');
const probItems = [
document.getElementById('prob-1'),
document.getElementById('prob-2'),
document.getElementById('prob-3'),
document.getElementById('prob-4')
];
const ripples = [
document.getElementById('ripple-binding'), // 绑扎线不足
document.getElementById('ripple-worker'), // 高坠
document.getElementById('ripple-wire'), // 触电
document.getElementById('ripple-breaker') // 断路器停电
];
// 动画状态机配置 (毫秒)
const STATES = [
{ class: '', msg: 'SYSTEM STANDBY...', duration: 2000 },
{ class: 'state-analysis', msg: 'MAPPING FUNCTIONAL INTERACTIONS...', duration: 5000 },
{ class: 'state-analysis state-alert', msg: 'DETECTING HARMFUL CONTRADICTIONS!', duration: 6000 }
];
let currentState = 0;
let lastTime = 0;
function animate(timestamp) {
if (!lastTime) lastTime = timestamp;
const elapsed = timestamp - lastTime;
const stateDef = STATES[currentState];
if (elapsed > stateDef.duration) {
// 切换到下一个状态
currentState = (currentState + 1) % STATES.length;
lastTime = timestamp;
const nextStateDef = STATES[currentState];
svgContainer.setAttribute('class', nextStateDef.class);
statusText.innerHTML = `${nextStateDef.msg}<span class="blink-cursor">_</span>`;
// 根据状态控制告警涟漪和右侧痛点列表的高亮
if (nextStateDef.class.includes('state-alert')) {
statusText.style.color = "var(--func-harmful)";
// 错开高亮显示四大核心痛点
probItems.forEach((item, i) => {
setTimeout(() => {
item.classList.add('active');
if(ripples[i]) ripples[i].classList.add('active');
}, i * 800);
});
} else {
statusText.style.color = "var(--node-target)";
probItems.forEach(item => item.classList.remove('active'));
ripples.forEach(r => r.classList.remove('active'));
}
}
requestAnimationFrame(animate);
}
// 初始化触发
svgContainer.setAttribute('class', STATES[0].class);
requestAnimationFrame(animate);
});
</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>传统停电绑扎作业系统 - TRIZ 核心矛盾与痛点动画</title>
<style>
:root {
/* 工业科技风配色 */
--bg-dark: #030712;
--grid-line: rgba(30, 58, 138, 0.3);
/* TRIZ 功能状态色 */
--color-useful: #10B981; /* 正常有用:翠绿 */
--color-insufficient: #F59E0B; /* 不足功能:明黄 */
--color-harmful: #EF4444; /* 有害功能:亮红 */
--color-target: #06B6D4; /* 作用对象:青色 */
--color-system: #6366F1; /* 系统组件:靛蓝 */
--text-main: #F8FAFC;
--text-dim: #94A3B8;
--font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
--font-mono: 'SFMono-Regular', Consolas, monospace;
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: var(--bg-dark);
background-image:
radial-gradient(circle at center, rgba(30,58,138,0.15) 0%, transparent 70%),
linear-gradient(var(--grid-line) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
background-size: 100% 100%, 50px 50px, 50px 50px;
font-family: var(--font-family);
color: var(--text-main);
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
/* ==================== 边缘信息面板 (HUD) ==================== */
.hud-container {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 100;
}
.panel {
position: absolute;
background: rgba(3, 7, 18, 0.85);
border: 1px solid rgba(148, 163, 184, 0.2);
backdrop-filter: blur(10px);
border-radius: 8px;
padding: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.8);
pointer-events: auto;
}
.panel-title {
font-size: 16px;
font-weight: bold;
color: var(--color-target);
margin-bottom: 12px;
border-bottom: 2px solid rgba(6, 182, 212, 0.3);
padding-bottom: 8px;
letter-spacing: 1px;
display: flex;
justify-content: space-between;
}
.panel-left {
top: 24px;
left: 24px;
width: 340px;
border-left: 4px solid var(--color-target);
}
.panel-right {
top: 24px;
right: 24px;
width: 320px;
border-right: 4px solid var(--color-harmful);
}
.panel-bottom {
bottom: 24px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 24px;
border-top: 2px solid var(--color-system);
padding: 12px 32px;
}
/* 列表样式 */
.triz-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 10px;
}
.triz-item {
font-size: 13px;
line-height: 1.5;
padding: 8px 12px;
background: rgba(255,255,255,0.03);
border-radius: 6px;
border-left: 3px solid transparent;
transition: all 0.3s ease;
opacity: 0.4;
}
.triz-item.active {
opacity: 1;
background: rgba(255,255,255,0.1);
transform: translateX(5px);
}
.triz-item.useful.active { border-left-color: var(--color-useful); box-shadow: 0 0 15px rgba(16,185,129,0.2); }
.triz-item.insufficient.active { border-left-color: var(--color-insufficient); box-shadow: 0 0 15px rgba(245,158,11,0.2); }
.triz-item.harmful.active { border-left-color: var(--color-harmful); box-shadow: 0 0 15px rgba(239,68,68,0.3); }
.tag-useful { color: var(--color-useful); font-weight: bold; font-family: var(--font-mono); font-size: 12px;}
.tag-insufficient { color: var(--color-insufficient); font-weight: bold; font-family: var(--font-mono); font-size: 12px;}
.tag-harmful { color: var(--color-harmful); font-weight: bold; font-family: var(--font-mono); font-size: 12px;}
/* 图例指示器 */
.legend-box { display: flex; align-items: center; gap: 8px; font-size: 12px; font-weight: bold;}
.indicator { width: 12px; height: 12px; border-radius: 50%; }
/* 动态打字机状态条 */
.status-bar {
position: absolute;
top: -40px;
left: 50%;
transform: translateX(-50%);
font-family: var(--font-mono);
font-size: 18px;
font-weight: bold;
color: var(--color-harmful);
background: rgba(239,68,68,0.15);
padding: 8px 24px;
border-radius: 20px;
border: 1px solid var(--color-harmful);
text-shadow: 0 0 10px rgba(239,68,68,0.5);
white-space: nowrap;
}
/* ==================== SVG 核心动画区 ==================== */
.svg-container {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
svg {
width: 100%;
height: 100%;
max-width: 1400px;
max-height: 900px;
overflow: visible;
}
/* 基础组件样式 */
.pole { fill: #1E293B; stroke: #334155; stroke-width: 2; }
.crossarm { fill: #334155; stroke: #0F172A; stroke-width: 2; }
.insulator { fill: #94A3B8; stroke: #475569; }
.wire { fill: none; stroke: var(--color-target); stroke-width: 6; filter: drop-shadow(0 0 6px var(--color-target)); }
/* 抽象节点样式 */
.node-rect { fill: rgba(15, 23, 42, 0.8); stroke: var(--text-dim); stroke-width: 2; rx: 6; transition: all 0.3s; }
.node-text { fill: var(--text-main); font-size: 16px; font-weight: bold; text-anchor: middle; font-family: var(--font-family); }
.node-subtext { fill: var(--text-dim); font-size: 12px; text-anchor: middle; font-family: var(--font-mono); }
/* 人员动作 (纯CSS) */
.worker-body { fill: #F59E0B; }
.worker-head { fill: #FDE047; }
.worker-limb { stroke: #F59E0B; stroke-width: 6; stroke-linecap: round; }
#worker-group { transform-origin: 600px 400px; }
.is-working #worker-group { animation: struggle 1s infinite alternate ease-in-out; }
.is-working .worker-arm-r { animation: bindAction 0.4s infinite alternate; transform-origin: top left; }
.is-working .worker-arm-l { animation: bindAction 0.5s infinite alternate reverse; transform-origin: top right; }
@keyframes struggle { 0% { transform: rotate(-2deg); } 100% { transform: rotate(2deg); } }
@keyframes bindAction { 0% { transform: rotate(-20deg); } 100% { transform: rotate(40deg); } }
/* ==================== TRIZ 相互作用连线 (动态流向) ==================== */
.triz-link { fill: none; stroke-width: 3; transition: all 0.4s; opacity: 0.2; }
.triz-flow { fill: none; stroke-width: 3; stroke-dasharray: 8 16; opacity: 0; transition: all 0.4s; pointer-events: none;}
/* 各种功能流向的激活状态 */
/* 1. 正常有用 (翠绿) */
.link-useful.active { stroke: var(--color-useful); opacity: 0.6; }
.flow-useful.active { stroke: var(--color-useful); opacity: 1; animation: flowAnim 1s linear infinite; filter: drop-shadow(0 0 8px var(--color-useful));}
/* 2. 不足功能 (明黄虚线) */
.link-insufficient.active { stroke: var(--color-insufficient); opacity: 0.8; stroke-dasharray: 6 6; }
.flow-insufficient.active { stroke: var(--color-insufficient); opacity: 1; animation: flowAnim 2s linear infinite reverse; filter: drop-shadow(0 0 8px var(--color-insufficient)); }
/* 3. 有害功能 (亮红粗线) */
.link-harmful.active { stroke: var(--color-harmful); opacity: 0.8; stroke-width: 5; }
.flow-harmful.active { stroke: var(--color-harmful); opacity: 1; animation: flowAnim 0.6s linear infinite; stroke-width: 5; filter: drop-shadow(0 0 10px var(--color-harmful)); }
@keyframes flowAnim { to { stroke-dashoffset: -48; } }
/* 标签文字 */
.link-label { font-size: 14px; font-weight: bold; font-family: var(--font-family); text-anchor: middle; opacity: 0; transition: opacity 0.3s; }
.link-label.active { opacity: 1; }
.label-useful { fill: var(--color-useful); }
.label-insufficient { fill: var(--color-insufficient); }
.label-harmful { fill: var(--color-harmful); }
/* 危险特效 (红圈闪烁与触电) */
.hazard-ring { opacity: 0; fill: none; stroke-width: 4; stroke: var(--color-harmful); transform-origin: center; pointer-events: none;}
.hazard-ring.active { animation: ringPulse 1.5s infinite cubic-bezier(0.1, 0.8, 0.3, 1); }
@keyframes ringPulse { 0% { transform: scale(0.5); opacity: 1; } 100% { transform: scale(3.5); opacity: 0; } }
.arc-flash { opacity: 0; fill: none; stroke: #FFF; stroke-width: 3; filter: drop-shadow(0 0 8px var(--color-target)); pointer-events: none; }
.arc-flash.active { animation: arcFlash 0.5s infinite; }
@keyframes arcFlash { 0%, 100% { opacity: 0; } 50% { opacity: 1; } 75% { opacity: 0.5; } }
/* 停电变暗特效 */
#scene-dimmer { opacity: 0; transition: opacity 0.5s; pointer-events: none; fill: rgba(0,0,0,0.6); }
#scene-dimmer.active { opacity: 1; }
</style>
</head>
<body>
<!-- HUD 面板层 -->
<div class="hud-container">
<!-- 左侧:系统内部组件与作用定义 -->
<div class="panel panel-left">
<div class="panel-title">
<span>组件相互作用分析</span>
<span style="font-family: var(--font-mono); font-size: 12px; color: var(--text-dim);">[SYSTEM_COMPONENTS]</span>
</div>
<ul class="triz-list" id="interaction-list">
<li class="triz-item harmful" data-step="1">
<span class="tag-harmful">[有害]</span> 作业人员 → 登高 → 杆塔<br>
<span style="color:var(--text-dim); font-size:11px;">导致极高的高处坠落危险</span>
</li>
<li class="triz-item harmful" data-step="2">
<span class="tag-harmful">[有害]</span> 作业人员 → 接近 → 高压导线<br>
<span style="color:var(--text-dim); font-size:11px;">极易触发感应电或直接触电</span>
</li>
<li class="triz-item useful" data-step="3">
<span class="tag-useful">[有用]</span> 作业人员 → 缠绕 → 绑扎线<br>
<span style="color:var(--text-dim); font-size:11px;">实现导线的物理挂载</span>
</li>
<li class="triz-item insufficient" data-step="4">
<span class="tag-insufficient">[不足]</span> 绑扎线 → 固定 → 导线<br>
<span style="color:var(--text-dim); font-size:11px;">传统铁丝/铝线极易松动、脱落</span>
</li>
<li class="triz-item harmful" data-step="5">
<span class="tag-harmful">[有害]</span> 断路器 → 切断 → 电源<br>
<span style="color:var(--text-dim); font-size:11px;">为保障安全导致下游全线停电</span>
</li>
</ul>
</div>
<!-- 右侧:核心矛盾总结 -->
<div class="panel panel-right">
<div class="panel-title" style="border-color: rgba(239,68,68,0.5); color: var(--color-harmful);">
<span>核心矛盾与痛点总结</span>
<span style="font-family: var(--font-mono); font-size: 12px; color: var(--text-dim);">[CONTRADICTIONS]</span>
</div>
<ul class="triz-list">
<li class="triz-item active" style="border-color: var(--color-insufficient); opacity: 0.9;">
<strong>1. 可靠性与耗时的矛盾</strong><br>
<span style="color:var(--text-dim); font-size:12px;">要求绑扎紧固防松脱,却导致纯手工缠绕极度耗时、工具不趁手。</span>
</li>
<li class="triz-item active" style="border-color: var(--color-harmful); opacity: 0.9; margin-top: 10px;">
<strong>2. 作业与安全的矛盾</strong><br>
<span style="color:var(--text-dim); font-size:12px;">依赖人工登高接近导线,衍生出高坠与触电两大核心威胁。</span>
</li>
<li class="triz-item active" style="border-color: var(--color-harmful); opacity: 0.9; margin-top: 10px;">
<strong>3. 检修与供电的矛盾</strong><br>
<span style="color:var(--text-dim); font-size:12px;">为切断风险切断断路器,直接造成巨大的下游停电经济损失。</span>
</li>
</ul>
</div>
<!-- 底部图例 -->
<div class="panel-bottom">
<div class="legend-box"><div class="indicator" style="background: var(--color-target); box-shadow: 0 0 10px var(--color-target);"></div> 作用对象 (导线)</div>
<div class="legend-box"><div class="indicator" style="background: var(--color-useful);"></div> 正常有用功能</div>
<div class="legend-box"><div class="indicator" style="border: 2px dashed var(--color-insufficient);"></div> 不足功能 (可靠性差)</div>
<div class="legend-box"><div class="indicator" style="background: var(--color-harmful); box-shadow: 0 0 10px var(--color-harmful);"></div> 有害功能 (风险/损失)</div>
</div>
<!-- 动态状态条 -->
<div class="status-bar" id="status-bar">系统初始化监控...</div>
</div>
<!-- 核心动画 SVG -->
<div class="svg-container">
<svg viewBox="0 0 1400 900" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- 发光滤镜 -->
<filter id="glow-target"><feGaussianBlur stdDeviation="4" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<!-- 箭头 Marker -->
<marker id="arrow-useful" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--color-useful)" />
</marker>
<marker id="arrow-insufficient" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--color-insufficient)" />
</marker>
<marker id="arrow-harmful" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--color-harmful)" />
</marker>
</defs>
<!-- ================= 具象物理场景层 ================= -->
<g id="physical-scene">
<!-- 地面 -->
<line x1="200" y1="800" x2="1200" y2="800" stroke="#1E293B" stroke-width="4"/>
<!-- 杆塔主体 -->
<polygon class="pole" points="585,800 615,800 608,150 592,150" />
<line x1="600" y1="150" x2="600" y2="800" stroke="#0F172A" stroke-width="2" stroke-dasharray="10 5"/>
<!-- 横担与支撑 -->
<rect class="crossarm" x="400" y="250" width="400" height="24" rx="4" />
<line x1="440" y1="274" x2="600" y2="350" stroke="#334155" stroke-width="12" stroke-linecap="round"/>
<line x1="760" y1="274" x2="600" y2="350" stroke="#334155" stroke-width="12" stroke-linecap="round"/>
<!-- 绝缘子 -->
<path class="insulator" d="M 430,200 L 470,200 L 460,250 L 440,250 Z" />
<path class="insulator" d="M 580,200 L 620,200 L 610,250 L 590,250 Z" />
<path class="insulator" d="M 730,200 L 770,200 L 760,250 L 740,250 Z" />
<!-- 高压导线 (唯一作用对象) -->
<path id="target-wire" class="wire" d="M 100,180 Q 300,280 450,200 T 750,200 T 1300,180" />
<!-- 绑扎线 (固定点细节) -->
<path d="M 590,195 Q 600,210 610,195 M 590,205 Q 600,220 610,205" fill="none" stroke="#94A3B8" stroke-width="3"/>
</g>
<!-- 停电遮罩 -->
<rect id="scene-dimmer" x="0" y="0" width="1400" height="900" />
<!-- ================= 作业人员具象模型 ================= -->
<g id="worker-group" class="is-working" transform="translate(0,-60)">
<!-- 脚踩位置 -->
<path d="M 590,460 L 610,460" stroke="#475569" stroke-width="4"/>
<!-- 身体 -->
<rect class="worker-body" x="585" y="340" width="30" height="50" rx="8"/>
<rect x="583" y="370" width="34" height="8" fill="#1E293B"/> <!-- 安全带 -->
<!-- 头部 -->
<circle cx="600" cy="325" r="14" class="worker-head"/>
<path d="M 582,320 Q 600,295 618,320 Z" fill="#EAB308"/>
<!-- 左臂 -->
<g transform="translate(585, 350)">
<line class="worker-limb worker-arm-l" x1="0" y1="0" x2="-25" y2="35"/>
</g>
<!-- 右臂向上操作绑扎线 -->
<g transform="translate(615, 350)">
<line class="worker-limb worker-arm-r" x1="0" y1="0" x2="10" y2="-40"/>
</g>
<!-- 双腿 -->
<line class="worker-limb" x1="595" y1="390" x2="595" y2="455"/>
<line class="worker-limb" x1="605" y1="390" x2="605" y2="455"/>
</g>
<!-- ================= 抽象超系统节点 ================= -->
<g id="abstract-nodes">
<!-- 断路器 -->
<g transform="translate(250, 650)">
<rect class="node-rect" x="-70" y="-30" width="140" height="60" />
<text class="node-text" y="-5">断路器</text>
<text class="node-subtext" y="15">超系统</text>
</g>
<!-- 高压电源 -->
<g transform="translate(250, 450)">
<rect class="node-rect" x="-70" y="-30" width="140" height="60" />
<text class="node-text" y="-5">高压电源</text>
<text class="node-subtext" y="15">超系统</text>
</g>
<!-- 供电负荷 -->
<g transform="translate(1150, 650)">
<rect class="node-rect" x="-70" y="-30" width="140" height="60" />
<text class="node-text" y="-5">下游负荷</text>
<text class="node-subtext" y="15">超系统 / 用户</text>
</g>
</g>
<!-- ================= TRIZ 相互作用流 (连线与动画) ================= -->
<g id="triz-interactions">
<!-- 1. 作业人员 -> 杆塔 (有害:高坠) -->
<g id="link-group-1">
<path d="M 640 400 Q 750 450 630 650" class="triz-link link-harmful" marker-end="url(#arrow-harmful)"/>
<path d="M 640 400 Q 750 450 630 650" class="triz-flow flow-harmful" />
<circle cx="620" cy="650" r="40" class="hazard-ring" id="ring-fall"/>
<text x="730" y="550" class="link-label label-harmful">产生高空坠落危险</text>
</g>
<!-- 2. 作业人员 -> 导线 (有害:触电) -->
<g id="link-group-2">
<path d="M 570 320 Q 500 250 500 200" class="triz-link link-harmful" marker-end="url(#arrow-harmful)"/>
<path d="M 570 320 Q 500 250 500 200" class="triz-flow flow-harmful" />
<path d="M 500 200 L 480 250 L 520 270 L 500 320" class="arc-flash" id="arc-shock"/>
<text x="460" y="270" class="link-label label-harmful">产生触电风险</text>
</g>
<!-- 3. 作业人员 -> 绑扎线 (有用:缠绕) -->
<g id="link-group-3">
<path d="M 630 310 Q 650 250 620 200" class="triz-link link-useful" marker-end="url(#arrow-useful)"/>
<path d="M 630 310 Q 650 250 620 200" class="triz-flow flow-useful" />
<text x="680" y="260" class="link-label label-useful">纯手工缠绕操作</text>
</g>
<!-- 4. 绑扎线 -> 导线 (不足:松动) -->
<g id="link-group-4">
<path d="M 610 180 Q 650 150 680 185" class="triz-link link-insufficient" marker-end="url(#arrow-insufficient)"/>
<path d="M 610 180 Q 650 150 680 185" class="triz-flow flow-insufficient" />
<text x="650" y="140" class="link-label label-insufficient">固定极易松动、脱落</text>
</g>
<!-- 5. 断路器 -> 电源 -> 负荷 (有害:全线停电) -->
<g id="link-group-5">
<path d="M 250 620 L 250 480" class="triz-link link-harmful" marker-end="url(#arrow-harmful)"/>
<path d="M 250 620 L 250 480" class="triz-flow flow-harmful" />
<path d="M 320 450 Q 700 350 1150 620" class="triz-link link-harmful" marker-end="url(#arrow-harmful)"/>
<path d="M 320 450 Q 700 350 1150 620" class="triz-flow flow-harmful" />
<circle cx="1150" cy="650" r="50" class="hazard-ring" id="ring-outage"/>
<text x="700" y="450" class="link-label label-harmful" style="font-size:18px;">切断电源:导致严重停电经济损失</text>
</g>
</g>
</svg>
</div>
<!-- 控制脚本 -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const listItems = document.querySelectorAll('.triz-item[data-step]');
const statusBar = document.getElementById('status-bar');
const sceneDimmer = document.getElementById('scene-dimmer');
// 各个动画组的引用
const linkGroups = {
1: { link: '#link-group-1 .triz-link', flow: '#link-group-1 .triz-flow', label: '#link-group-1 .link-label', extra: '#ring-fall' },
2: { link: '#link-group-2 .triz-link', flow: '#link-group-2 .triz-flow', label: '#link-group-2 .link-label', extra: '#arc-shock' },
3: { link: '#link-group-3 .triz-link', flow: '#link-group-3 .triz-flow', label: '#link-group-3 .link-label' },
4: { link: '#link-group-4 .triz-link', flow: '#link-group-4 .triz-flow', label: '#link-group-4 .link-label' },
5: { link: '#link-group-5 .triz-link', flow: '#link-group-5 .triz-flow', label: '#link-group-5 .link-label', extra: '#ring-outage' }
};
const STEP_DURATION = 3500; // 每步展示 3.5 秒
let currentStep = 1;
const totalSteps = 5;
function clearAll() {
listItems.forEach(item => item.classList.remove('active'));
Object.values(linkGroups).forEach(group => {
document.querySelectorAll(group.link).forEach(el => el.classList.remove('active'));
document.querySelectorAll(group.flow).forEach(el => el.classList.remove('active'));
document.querySelectorAll(group.label).forEach(el => el.classList.remove('active'));
if (group.extra) {
document.querySelectorAll(group.extra).forEach(el => el.classList.remove('active'));
}
});
sceneDimmer.classList.remove('active');
}
function activateStep(step) {
clearAll();
// 激活左侧列表
const listItem = document.querySelector(`.triz-item[data-step="${step}"]`);
if (listItem) listItem.classList.add('active');
// 激活 SVG 连线与特效
const group = linkGroups[step];
if (group) {
document.querySelectorAll(group.link).forEach(el => el.classList.add('active'));
document.querySelectorAll(group.flow).forEach(el => el.classList.add('active'));
document.querySelectorAll(group.label).forEach(el => el.classList.add('active'));
if (group.extra) {
document.querySelectorAll(group.extra).forEach(el => el.classList.add('active'));
}
}
// 更新顶部状态栏文字
if (step === 1) statusBar.innerHTML = "⚠️ 检出有害功能:【高处坠落风险】";
else if (step === 2) statusBar.innerHTML = "⚡ 检出有害功能:【高压触电危险】";
else if (step === 3) statusBar.innerHTML = "✅ 检出有用功能:【物理固定导线】 (极度耗时)";
else if (step === 4) statusBar.innerHTML = "❌ 检出不足功能:【可靠性差,易松动】";
else if (step === 5) {
statusBar.innerHTML = "📉 检出超系统有害相互作用:【大面积停电损失】";
sceneDimmer.classList.add('active'); // 模拟全场景停电变暗
}
}
// 状态机循环
function runStateMachine() {
activateStep(currentStep);
currentStep++;
if (currentStep > totalSteps) {
currentStep = 1; // 循环
}
}
// 启动
runStateMachine();
setInterval(runStateMachine, STEP_DURATION);
});
</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>楔形自锁效应 (Wedge Self-Locking Effect) - 原理仿真</title>
<style>
:root {
/* 深度工程蓝图配色 */
--bg-base: #040814;
--grid-main: rgba(0, 240, 255, 0.1);
--grid-sub: rgba(0, 240, 255, 0.03);
/* 组件色彩 */
--color-casing: #1E293B; /* 外壳装甲色 */
--color-wedge: #334155; /* 楔形块金属色 */
--color-wire: #E2E8F0; /* 导线银白色 */
/* 数据与力学特征色 */
--color-neon-cyan: #00F0FF; /* 结构与维度线 */
--color-force-down: #FF3B30; /* 下拉力/重力 (危险红) */
--color-force-clamp: #10B981; /* 夹紧力 (安全绿) */
--color-force-fric: #F59E0B; /* 摩擦力 (警告黄) */
/* 排版规范 */
--font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
--anim-duration: 8s;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body, html {
width: 100%;
height: 100%;
background-color: var(--bg-base);
/* 工程网格背景 */
background-image:
linear-gradient(var(--grid-main) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-main) 1px, transparent 1px),
linear-gradient(var(--grid-sub) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-sub) 1px, transparent 1px);
background-size: 100px 100px, 100px 100px, 20px 20px, 20px 20px;
font-family: var(--font-sans);
color: #fff;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
/* ==================== 边缘 HUD 数据面板 (严控字号防遮挡) ==================== */
.hud-layer {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 100;
}
.panel {
position: absolute;
background: rgba(4, 8, 20, 0.85);
border: 1px solid rgba(0, 240, 255, 0.2);
backdrop-filter: blur(8px);
padding: 16px;
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
font-family: var(--font-mono);
}
.panel-top-left { top: 24px; left: 24px; width: 280px; border-left: 3px solid var(--color-neon-cyan); }
.panel-top-right { top: 24px; right: 24px; width: 260px; text-align: right; border-right: 3px solid var(--color-force-clamp); }
.panel-bottom-left { bottom: 24px; left: 24px; width: 300px; }
.panel-bottom-right { bottom: 24px; right: 24px; text-align: right; }
.panel-title {
font-size: 12px;
font-weight: bold;
color: var(--color-neon-cyan);
margin-bottom: 8px;
text-transform: uppercase;
letter-spacing: 1px;
border-bottom: 1px solid rgba(0, 240, 255, 0.3);
padding-bottom: 4px;
}
.data-row {
display: flex;
justify-content: space-between;
font-size: 11px;
margin-bottom: 6px;
color: #94A3B8;
}
.data-val { font-weight: bold; font-size: 12px; }
.val-cyan { color: var(--color-neon-cyan); }
.val-green { color: var(--color-force-clamp); }
.val-red { color: var(--color-force-down); }
.val-yellow { color: var(--color-force-fric); }
.desc-text {
font-size: 10px;
line-height: 1.6;
color: #64748B;
font-family: var(--font-sans);
margin-top: 8px;
}
.eq-block {
background: rgba(255,255,255,0.05);
padding: 6px;
border-radius: 4px;
font-size: 11px;
color: #CBD5E1;
margin-top: 6px;
text-align: center;
}
/* ==================== SVG 核心动画区 ==================== */
.svg-canvas {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
svg {
width: 100%;
height: 100%;
max-width: 1200px;
max-height: 900px;
overflow: visible;
}
/* ----- 静态结构样式 ----- */
.casing { fill: var(--color-casing); stroke: var(--color-neon-cyan); stroke-width: 2; }
.casing-hatch { stroke: rgba(0, 240, 255, 0.1); stroke-width: 1; }
.dimension-line { stroke: var(--color-neon-cyan); stroke-width: 1; stroke-dasharray: 4 4; opacity: 0.5; }
.dimension-text { fill: var(--color-neon-cyan); font-family: var(--font-mono); font-size: 14px; opacity: 0.8; }
/* ----- 动态物理实体 ----- */
.wedge { fill: var(--color-wedge); stroke: #475569; stroke-width: 2; }
.friction-pad { fill: #0F172A; stroke: #000; }
.wire { fill: var(--color-wire); stroke: #94A3B8; stroke-width: 2; }
.wire-glow { fill: none; stroke: var(--color-wire); stroke-width: 8; opacity: 0.2; filter: blur(4px); }
/* ----- 动画定义 (纯CSS物理模拟) ----- */
/* 系统整体下降缓冲 */
#dynamic-system {
animation: systemDrop var(--anim-duration) cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@keyframes systemDrop {
0%, 15% { transform: translateY(-80px); }
35%, 85% { transform: translateY(0px); }
95%, 100% { transform: translateY(-80px); }
}
/* 导线独立位移 (受重力先下沉,推动楔块) */
#wire-group {
animation: wireDrop var(--anim-duration) cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@keyframes wireDrop {
0%, 10% { transform: translateY(-20px); }
35%, 85% { transform: translateY(0px); }
90%, 100% { transform: translateY(-20px); }
}
/* 左楔块: 沿导轨斜面向下向内滑动 (假设斜角15度, dx = dy * tan(15)) */
#wedge-l {
animation: wedgeSlideL var(--anim-duration) cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@keyframes wedgeSlideL {
0%, 15% { transform: translate(-16px, -60px); }
35%, 85% { transform: translate(0px, 0px); }
95%, 100% { transform: translate(-16px, -60px); }
}
/* 右楔块: 沿导轨斜面向下向内滑动 */
#wedge-r {
animation: wedgeSlideR var(--anim-duration) cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@keyframes wedgeSlideR {
0%, 15% { transform: translate(16px, -60px); }
35%, 85% { transform: translate(0px, 0px); }
95%, 100% { transform: translate(16px, -60px); }
}
/* ----- 力学矢量箭头动画 ----- */
.force-vector { stroke-width: 3; fill: none; opacity: 0; }
.force-label { font-family: var(--font-mono); font-size: 16px; font-weight: bold; opacity: 0; }
/* 导线向下重力/拉力 */
.anim-force-down { animation: showForceDown var(--anim-duration) infinite; stroke: var(--color-force-down); }
.anim-label-down { animation: showForceDown var(--anim-duration) infinite; fill: var(--color-force-down); }
@keyframes showForceDown {
0%, 20% { opacity: 0; transform: scaleY(0.5); }
30%, 80% { opacity: 1; transform: scaleY(1); }
85%, 100% { opacity: 0; transform: scaleY(0.5); }
}
/* 侧向夹紧力 (放大效应) */
.anim-force-clamp { animation: showForceClamp var(--anim-duration) infinite; stroke: var(--color-force-clamp); }
.anim-label-clamp { animation: showForceClamp var(--anim-duration) infinite; fill: var(--color-force-clamp); }
@keyframes showForceClamp {
0%, 30% { opacity: 0; transform: scaleX(0.2); }
40%, 80% { opacity: 1; transform: scaleX(1); }
85%, 100% { opacity: 0; transform: scaleX(0.2); }
}
/* 斜面摩擦力与支持力 */
.anim-force-fric { animation: showForceFric var(--anim-duration) infinite; stroke: var(--color-force-fric); }
.anim-label-fric { animation: showForceFric var(--anim-duration) infinite; fill: var(--color-force-fric); font-size: 14px;}
@keyframes showForceFric {
0%, 35% { opacity: 0; }
45%, 80% { opacity: 1; }
85%, 100% { opacity: 0; }
}
/* 自锁成功状态光晕 */
.lock-glow {
fill: none;
stroke: var(--color-force-clamp);
stroke-width: 4;
opacity: 0;
filter: blur(6px);
animation: lockSuccess var(--anim-duration) infinite;
}
@keyframes lockSuccess {
0%, 38% { opacity: 0; }
45%, 80% { opacity: 0.6; }
85%, 100% { opacity: 0; }
}
</style>
</head>
<body>
<!-- 边缘物理量监控面板 -->
<div class="hud-layer">
<!-- 左上:效应定义 -->
<div class="panel panel-top-left">
<div class="panel-title">Wedge Self-Locking Effect</div>
<div class="data-row">
<span>物理机制</span>
<span class="val-cyan">摩擦自锁</span>
</div>
<div class="data-row">
<span>能量转化</span>
<span class="val-cyan">轴向拉力 → 径向夹紧力</span>
</div>
<div class="desc-text">
利用斜面原理,当输入的外力(导线自重或拉力)驱使楔形块沿斜面向下运动时,斜面迫使楔块向内收缩,将较小的下拉力按三角函数几何放大为巨大的侧向夹紧力。
</div>
</div>
<!-- 右上:实时遥测数据 -->
<div class="panel panel-top-right">
<div class="panel-title" style="border-color: var(--color-force-clamp); color: var(--color-force-clamp);">实时动态遥测</div>
<div class="data-row">
<span>系统状态</span>
<span class="data-val" id="ui-status" style="color: #94A3B8;">待机 / 松弛</span>
</div>
<div class="data-row">
<span>轴向下拉力 (F_y)</span>
<span class="data-val val-red" id="ui-force-down">0 N</span>
</div>
<div class="data-row">
<span>侧向夹紧力 (F_c)</span>
<span class="data-val val-green" id="ui-force-clamp">0 N</span>
</div>
<div class="data-row">
<span>机械放大倍率 (M.A.)</span>
<span class="data-val val-cyan" id="ui-ratio">1.0 x</span>
</div>
</div>
<!-- 左下:力学条件 -->
<div class="panel panel-bottom-left">
<div class="panel-title">自锁临界条件分析</div>
<div class="data-row">
<span>斜面夹角 (α)</span>
<span class="data-val val-cyan">15°</span>
</div>
<div class="data-row">
<span>静摩擦系数 (μ)</span>
<span class="data-val val-yellow">0.35</span>
</div>
<div class="eq-block">
自锁条件: μ ≥ tan(α)<br>
0.35 ≥ tan(15°) ≈ 0.268 [<span style="color:var(--color-force-clamp)">满足</span>]
</div>
<div class="desc-text" style="text-align: center;">
无论下拉力多大,系统绝不滑脱,越拉越紧。
</div>
</div>
</div>
<!-- 核心 SVG 动画 -->
<div class="svg-canvas">
<svg viewBox="0 0 1000 1000" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- 箭头标记 -->
<marker id="arrow-down" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="8" markerHeight="8" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--color-force-down)" />
</marker>
<marker id="arrow-clamp" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="8" markerHeight="8" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--color-force-clamp)" />
</marker>
<marker id="arrow-fric" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--color-force-fric)" />
</marker>
<!-- 剖面斜线填充 -->
<pattern id="hatch" width="10" height="10" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="0" y2="10" stroke="rgba(0, 240, 255, 0.15)" stroke-width="2" />
</pattern>
<!-- 金属材质渐变 -->
<linearGradient id="metal-grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#334155" />
<stop offset="50%" stop-color="#64748B" />
<stop offset="100%" stop-color="#334155" />
</linearGradient>
</defs>
<!-- 坐标辅助线 (工业感背景) -->
<line x1="500" y1="100" x2="500" y2="900" stroke="rgba(255,255,255,0.1)" stroke-dasharray="10 10" />
<line x1="100" y1="500" x2="900" y2="500" stroke="rgba(255,255,255,0.1)" stroke-dasharray="10 10" />
<circle cx="500" cy="500" r="300" fill="none" stroke="rgba(0, 240, 255, 0.05)" stroke-width="1" />
<!-- V型外壳 (固定参考系) -->
<g transform="translate(500, 500)">
<!-- 左半外壳 (内侧倾角 15度) -->
<path class="casing" d="M -160,-250 L -180,-250 L -180,250 L -40,250 L -100,-250 Z" />
<path d="M -160,-250 L -180,-250 L -180,250 L -40,250 L -100,-250 Z" fill="url(#hatch)"/>
<!-- 右半外壳 (内侧倾角 15度) -->
<path class="casing" d="M 160,-250 L 180,-250 L 180,250 L 40,250 L 100,-250 Z" />
<path d="M 160,-250 L 180,-250 L 180,250 L 40,250 L 100,-250 Z" fill="url(#hatch)"/>
<!-- 角度标注 -->
<line class="dimension-line" x1="-100" y1="-250" x2="-100" y2="-150" />
<path d="M -100,-180 A 70 70 0 0 0 -80,-185" fill="none" stroke="var(--color-neon-cyan)" stroke-width="1" />
<text x="-95" y="-195" class="dimension-text">α=15°</text>
</g>
<!-- 动态系统组 (受外力驱动整体下沉) -->
<g id="dynamic-system" transform="translate(500, 500)">
<!-- 左侧楔块 -->
<g id="wedge-l">
<path class="wedge" fill="url(#metal-grad)" d="M -90,-200 L -30,-200 L -30,200 L -90,-200 Z" />
<!-- 摩擦橡胶垫/防滑齿 -->
<rect class="friction-pad" x="-30" y="-150" width="8" height="300" rx="2" />
<path d="M -22,-140 L -28,-135 M -22,-130 L -28,-125 M -22,-120 L -28,-115" stroke="#475569" stroke-width="2"/>
<path d="M -22,140 L -28,135 M -22,130 L -28,125 M -22,120 L -28,115" stroke="#475569" stroke-width="2"/>
<!-- 力学分析向量 (左侧) -->
<!-- 支持力 F_N -->
<line class="force-vector anim-force-fric" x1="-80" y1="0" x2="-30" y2="15" marker-end="url(#arrow-fric)" />
<text x="-90" y="30" class="force-label anim-label-fric">F_N</text>
<!-- 摩擦力 f -->
<line class="force-vector anim-force-fric" x1="-60" y1="0" x2="-80" y2="-75" marker-end="url(#arrow-fric)" />
<text x="-95" y="-85" class="force-label anim-label-fric">f</text>
</g>
<!-- 右侧楔块 -->
<g id="wedge-r">
<path class="wedge" fill="url(#metal-grad)" d="M 90,-200 L 30,-200 L 30,200 L 90,-200 Z" />
<!-- 摩擦橡胶垫/防滑齿 -->
<rect class="friction-pad" x="22" y="-150" width="8" height="300" rx="2" />
<path d="M 22,-140 L 28,-135 M 22,-130 L 28,-125 M 22,-120 L 28,-115" stroke="#475569" stroke-width="2"/>
<path d="M 22,140 L 28,135 M 22,130 L 28,125 M 22,120 L 28,115" stroke="#475569" stroke-width="2"/>
</g>
<!-- 导线与输入力 (居中) -->
<g id="wire-group">
<circle cx="0" cy="0" r="30" class="wire-glow" />
<circle cx="0" cy="0" r="28" class="wire" />
<circle cx="0" cy="0" r="10" fill="none" stroke="#64748B" stroke-dasharray="4 2" />
<circle cx="0" cy="0" r="20" fill="none" stroke="#64748B" stroke-dasharray="6 4" />
<!-- 向下的主驱动力 (F_pull / 导线重力) -->
<g transform="translate(0, 35)">
<line class="force-vector anim-force-down" x1="0" y1="0" x2="0" y2="120" marker-end="url(#arrow-down)" style="transform-origin: top center;" />
<text x="15" y="100" class="force-label anim-label-down">F_pull</text>
</g>
</g>
<!-- 夹紧力动画 (F_c) -->
<g transform="translate(0, 0)">
<!-- 左侧向右的夹紧力 -->
<line class="force-vector anim-force-clamp" x1="-150" y1="0" x2="-40" y2="0" marker-end="url(#arrow-clamp)" style="transform-origin: left center;" />
<text x="-130" y="-15" class="force-label anim-label-clamp">F_clamp</text>
<!-- 右侧向左的夹紧力 -->
<line class="force-vector anim-force-clamp" x1="150" y1="0" x2="40" y2="0" marker-end="url(#arrow-clamp)" style="transform-origin: right center;" />
<text x="70" y="-15" class="force-label anim-label-clamp">F_clamp</text>
</g>
<!-- 成功锁紧的光晕提示 -->
<rect x="-35" y="-180" width="70" height="360" rx="4" class="lock-glow" />
</g>
</svg>
</div>
<!-- 数据同步逻辑:将物理数值与 CSS 动画时间轴同步 -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const elStatus = document.getElementById('ui-status');
const elForceDown = document.getElementById('ui-force-down');
const elForceClamp = document.getElementById('ui-force-clamp');
const elRatio = document.getElementById('ui-ratio');
const animDuration = 8000; // 8秒周期,与 CSS 保持一致
function updateTelemetry(timestamp) {
// 计算当前动画在周期内的进度 0.0 ~ 1.0
const progress = (timestamp % animDuration) / animDuration;
// 基于 CSS Keyframes 的定义同步数值
// 0-15%: 开始下沉
// 15-35%: 锁紧过程 (力急剧增加)
// 35-85%: 保持锁紧状态
// 85-100%: 释放复位
let forceDown = 0;
let forceClamp = 0;
let status = "待机 / 松弛";
let statusColor = "#94A3B8";
if (progress < 0.15 || progress > 0.90) {
// 待机阶段
forceDown = 0;
forceClamp = 0;
} else if (progress >= 0.15 && progress < 0.35) {
// 动态增加阶段
const localP = (progress - 0.15) / 0.20; // 0 to 1
// 使用缓动曲线模拟
const easeOut = 1 - Math.pow(1 - localP, 3);
forceDown = easeOut * 250;
forceClamp = easeOut * 920;
status = "楔块介入收缩...";
statusColor = "var(--color-force-fric)";
} else if (progress >= 0.35 && progress <= 0.85) {
// 自锁保持阶段 (加入微小扰动显得真实)
forceDown = 250 + Math.random() * 5;
forceClamp = 920 + Math.random() * 15;
status = "自锁生效 [LOCKED]";
statusColor = "var(--color-force-clamp)";
} else {
// 释放阶段
const localP = (progress - 0.85) / 0.05;
forceDown = 250 * (1 - localP);
forceClamp = 920 * (1 - localP);
status = "释放脱离...";
statusColor = "var(--color-force-down)";
}
// 渲染数值
elForceDown.textContent = Math.floor(forceDown) + " N";
elForceClamp.textContent = Math.floor(forceClamp) + " N";
elStatus.textContent = status;
elStatus.style.color = statusColor;
// 计算实际放大倍率
if (forceDown > 10) {
const ratio = forceClamp / forceDown;
elRatio.textContent = ratio.toFixed(1) + " x";
} else {
elRatio.textContent = "1.0 x";
}
requestAnimationFrame(updateTelemetry);
}
// 启动同步循环
requestAnimationFrame(updateTelemetry);
});
</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>楔形自锁效应原理可视化 (Wedge Self-Locking Effect)</title>
<style>
:root {
/* 工业科研级配色 */
--bg-color: #050508;
--grid-line: rgba(30, 64, 175, 0.2);
--blueprint-blue: #1E3A8A;
--tech-cyan: #00E5FF;
--accent-orange: #FF6D00;
--force-green: #00E676;
--force-red: #FF1744;
--housing-dark: #0F172A;
--wedge-color: #1E293B;
--text-main: #F8FAFC;
--text-dim: #64748B;
--font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--anim-duration: 8s;
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: var(--bg-color);
background-image:
linear-gradient(var(--grid-line) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
background-size: 40px 40px;
font-family: var(--font-sans);
color: var(--text-main);
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
/* ================= HUD 信息面板 (边缘停靠、极简小字号、防遮挡) ================= */
.hud-layer {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 100;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.panel {
position: absolute;
background: rgba(5, 5, 8, 0.85);
border: 1px solid rgba(0, 229, 255, 0.2);
backdrop-filter: blur(8px);
padding: 16px 20px;
border-radius: 4px;
}
/* 左上:原理描述 */
.panel-top-left {
top: 24px;
left: 24px;
max-width: 320px;
border-left: 3px solid var(--tech-cyan);
}
.title {
font-family: var(--font-mono);
font-size: 14px;
font-weight: bold;
color: var(--tech-cyan);
margin-bottom: 8px;
letter-spacing: 1px;
text-transform: uppercase;
}
.desc {
font-size: 12px;
color: var(--text-dim);
line-height: 1.6;
}
.highlight { color: var(--accent-orange); font-weight: bold; }
/* 右上:力学实时参数监控 */
.panel-top-right {
top: 24px;
right: 24px;
width: 240px;
border-right: 3px solid var(--force-green);
text-align: right;
}
.data-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
font-size: 12px;
font-family: var(--font-mono);
}
.data-value {
font-size: 20px;
font-weight: bold;
transition: color 0.2s;
}
/* 底部居中:公式定理 */
.panel-bottom {
bottom: 24px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 32px;
border-top: 2px solid var(--blueprint-blue);
padding: 12px 32px;
font-family: var(--font-mono);
font-size: 12px;
color: var(--text-dim);
}
/* ================= 核心 SVG 动画区域 ================= */
.svg-container {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
svg {
width: 100%;
height: 100%;
max-width: 1000px;
max-height: 800px;
overflow: visible;
}
/* --- 动画状态类 --- */
/* 1. 楔形块滑动 (斜向移动) */
.anim-wedge-left { animation: slideLeft var(--anim-duration) ease-in-out infinite; }
.anim-wedge-right { animation: slideRight var(--anim-duration) ease-in-out infinite; }
@keyframes slideLeft {
0%, 15% { transform: translate(-25px, -200px); }
20% { transform: translate(-25px, -200px); }
40%, 75% { transform: translate(0px, 0px); }
85%, 100% { transform: translate(-25px, -200px); }
}
@keyframes slideRight {
0%, 15% { transform: translate(25px, -200px); }
20% { transform: translate(25px, -200px); }
40%, 75% { transform: translate(0px, 0px); }
85%, 100% { transform: translate(25px, -200px); }
}
/* 2. 导线垂直下沉 */
.anim-wire { animation: dropWire var(--anim-duration) ease-in-out infinite; }
@keyframes dropWire {
0%, 10% { transform: translate(0px, -350px); }
20% { transform: translate(0px, -200px); } /* 接触楔形块 */
40%, 75% { transform: translate(0px, 0px); } /* 共同下沉自锁 */
85%, 100% { transform: translate(0px, -350px); }
}
/* 3. 力学矢量箭头显隐 */
.anim-force { animation: showForces var(--anim-duration) infinite; }
@keyframes showForces {
0%, 42% { opacity: 0; transform: scale(0.8); }
48%, 70% { opacity: 1; transform: scale(1); }
75%, 100% { opacity: 0; transform: scale(0.8); }
}
/* 4. 力流特效线 */
.force-flow { stroke-dasharray: 8 8; animation: flowDash 0.5s linear infinite; }
@keyframes flowDash { to { stroke-dashoffset: -16; } }
/* 5. 支撑弹簧压缩 */
.anim-spring-l { animation: compressSpringL var(--anim-duration) ease-in-out infinite; transform-origin: center bottom; }
.anim-spring-r { animation: compressSpringR var(--anim-duration) ease-in-out infinite; transform-origin: center bottom; }
@keyframes compressSpringL {
0%, 20% { transform: scaleY(1) translate(-25px, 0); }
40%, 75% { transform: scaleY(0.4) translate(0px, 0); }
85%, 100% { transform: scaleY(1) translate(-25px, 0); }
}
@keyframes compressSpringR {
0%, 20% { transform: scaleY(1) translate(25px, 0); }
40%, 75% { transform: scaleY(0.4) translate(0px, 0); }
85%, 100% { transform: scaleY(1) translate(25px, 0); }
}
</style>
</head>
<body>
<!-- UI 面板 -->
<div class="hud-layer">
<div class="panel panel-top-left">
<div class="title">TRIZ 创新原理:楔形自锁效应</div>
<div class="desc">
系统利用导线自身的<span class="highlight">重力/下拔力</span>作为驱动源。当导线向下移动时,迫使两侧楔形块沿导轨斜面滑动。<br><br>
通过斜面的几何特性,垂直向下的位移被转化为巨大的<span style="color:var(--force-green); font-weight:bold;">侧向夹紧力</span>。拉力越大,夹得越紧,实现了在不增加复杂机械结构前提下的“最终理想解 (IFR)”。
</div>
</div>
<div class="panel panel-top-right">
<div class="title" style="color: var(--text-main); border-color: var(--force-green);">动态力学监测</div>
<div class="data-row">
<span style="color: var(--text-dim);">系统状态</span>
<span id="ui-status" style="color: var(--tech-cyan);">待机 / 挂载中</span>
</div>
<div style="height: 1px; background: rgba(255,255,255,0.1); margin: 12px 0;"></div>
<div class="data-row">
<span style="color: var(--text-dim);">导线垂直载荷 (Fy)</span>
<span class="data-value" id="ui-force-y" style="color: var(--accent-orange);">0 N</span>
</div>
<div class="data-row">
<span style="color: var(--text-dim);">单侧夹紧力 (Fx)</span>
<span class="data-value" id="ui-force-x" style="color: var(--force-green);">0 N</span>
</div>
<div class="data-row" style="margin-top: 12px; font-size: 10px;">
<span style="color: var(--text-dim);">力放大倍数 (MA)</span>
<span id="ui-ma" style="color: var(--text-main);">--</span>
</div>
</div>
<div class="panel panel-bottom">
<span>几何斜角: θ = 7.125°</span>
<span>自锁条件: tan(θ) ≤ μ</span>
<span style="color: var(--force-green);">力学方程: F_clamp = F_y / [2(tanθ + μ)]</span>
</div>
</div>
<!-- 核心 SVG 动画 -->
<div class="svg-container">
<svg viewBox="0 0 1000 800" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- 发光滤镜 -->
<filter id="glow-cyan"><feGaussianBlur stdDeviation="4" result="blur" /><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<filter id="glow-orange"><feGaussianBlur stdDeviation="6" result="blur" /><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<filter id="glow-green"><feGaussianBlur stdDeviation="5" result="blur" /><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<!-- 箭头标识 -->
<marker id="arrow-red" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--force-red)" />
</marker>
<marker id="arrow-green" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--force-green)" />
</marker>
<marker id="arrow-blue" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#3B82F6" />
</marker>
<!-- 防滑齿纹理 (用于楔形块内侧) -->
<pattern id="teeth-pattern" width="10" height="8" patternUnits="userSpaceOnUse">
<path d="M 0 0 L 10 4 L 0 8 Z" fill="#334155" />
</pattern>
<pattern id="teeth-pattern-r" width="10" height="8" patternUnits="userSpaceOnUse">
<path d="M 10 0 L 0 4 L 10 8 Z" fill="#334155" />
</pattern>
</defs>
<!-- 坐标系辅助线 (增强科技感) -->
<g opacity="0.2">
<line x1="500" y1="50" x2="500" y2="750" stroke="#94A3B8" stroke-dasharray="4 4"/>
<line x1="250" y1="450" x2="750" y2="450" stroke="#94A3B8" stroke-dasharray="4 4"/>
</g>
<!-- 系统整体坐标变换 (中心对齐) -->
<g transform="translate(500, 450)">
<!-- 1. 外部底座与导轨 (V型槽) -->
<!-- 斜率设计:1/8 (约7.1度) -->
<g id="housing">
<!-- 左侧外壳 -->
<polygon points="-160,-250 -81.25,-250 -43.75,50 -160,50" fill="var(--housing-dark)" stroke="var(--tech-cyan)" stroke-width="3" filter="url(#glow-cyan)"/>
<line x1="-81.25" y1="-250" x2="-43.75" y2="50" stroke="#38BDF8" stroke-width="2"/> <!-- 导轨滑动面 -->
<!-- 右侧外壳 -->
<polygon points="160,-250 81.25,-250 43.75,50 160,50" fill="var(--housing-dark)" stroke="var(--tech-cyan)" stroke-width="3" filter="url(#glow-cyan)"/>
<line x1="81.25" y1="-250" x2="43.75" y2="50" stroke="#38BDF8" stroke-width="2"/>
<!-- 底部连接桥 -->
<rect x="-160" y="50" width="320" height="40" fill="var(--housing-dark)" stroke="var(--tech-cyan)" stroke-width="3"/>
<text x="0" y="75" font-family="var(--font-mono)" font-size="12" fill="var(--tech-cyan)" text-anchor="middle" letter-spacing="2">BASE_FIXTURE_STRUCT</text>
</g>
<!-- 2. 复位支撑弹簧 (抽象液压推杆) -->
<g class="anim-spring-l">
<rect x="-65" y="50" width="10" height="150" fill="#475569" transform="translate(0, -150)"/>
<path d="M -75,-100 L -55,-100 M -70,-80 L -60,-80 M -75,-60 L -55,-60 M -70,-40 L -60,-40" stroke="#94A3B8" stroke-width="2"/>
</g>
<g class="anim-spring-r">
<rect x="55" y="50" width="10" height="150" fill="#475569" transform="translate(0, -150)"/>
<path d="M 55,-100 L 75,-100 M 60,-80 L 70,-80 M 55,-60 L 75,-60 M 60,-40 L 70,-40" stroke="#94A3B8" stroke-width="2"/>
</g>
<!-- 3. 左侧楔形块 (自锁滑块) -->
<!-- 自锁状态位置:内缘 x=-30, 顶y=-100, 底y=50 -->
<g class="anim-wedge-left">
<polygon points="-30,-100 -62.5,-100 -43.75,50 -30,50" fill="var(--wedge-color)" stroke="#38BDF8" stroke-width="2"/>
<!-- 防滑齿 -->
<rect x="-30" y="-95" width="10" height="140" fill="url(#teeth-pattern)"/>
</g>
<!-- 4. 右侧楔形块 -->
<g class="anim-wedge-right">
<polygon points="30,-100 62.5,-100 43.75,50 30,50" fill="var(--wedge-color)" stroke="#38BDF8" stroke-width="2"/>
<rect x="20" y="-95" width="10" height="140" fill="url(#teeth-pattern-r)"/>
</g>
<!-- 5. 导线实体 (受力源) -->
<g class="anim-wire">
<circle cx="0" cy="-25" r="30" fill="#E2E8F0" stroke="var(--accent-orange)" stroke-width="4" filter="url(#glow-orange)"/>
<!-- 截面纹理 -->
<circle cx="0" cy="-25" r="20" fill="none" stroke="#94A3B8" stroke-width="1" stroke-dasharray="3 3"/>
<circle cx="0" cy="-25" r="10" fill="none" stroke="#94A3B8" stroke-width="1" stroke-dasharray="2 2"/>
<text x="0" y="-21" font-family="var(--font-mono)" font-size="10" fill="var(--housing-dark)" font-weight="bold" text-anchor="middle">WIRE</text>
<!-- 6. 力学矢量可视化 (跟随导线并在自锁时显示) -->
<g class="anim-force">
<!-- 向下重力/拔力 (Fy) -->
<line x1="0" y1="5" x2="0" y2="85" stroke="var(--force-red)" stroke-width="4" marker-end="url(#arrow-red)"/>
<text x="15" y="75" font-family="var(--font-sans)" font-size="14" font-weight="bold" fill="var(--force-red)">Fy</text>
<!-- 左侧外壳给予楔形块的法向支持力 (Fn) 垂直于斜面 -->
<!-- 斜率1/8,法线斜率-8 -->
<line x1="-100" y1="-32.5" x2="-60" y2="-27.5" stroke="#3B82F6" stroke-width="3" marker-end="url(#arrow-blue)"/>
<!-- 右侧法向支持力 (Fn) -->
<line x1="100" y1="-32.5" x2="60" y2="-27.5" stroke="#3B82F6" stroke-width="3" marker-end="url(#arrow-blue)"/>
<!-- 左侧楔形块对导线的侧向夹紧力 (Fx) -->
<line x1="-90" y1="-25" x2="-35" y2="-25" class="force-flow" stroke="var(--force-green)" stroke-width="4" marker-end="url(#arrow-green)"/>
<text x="-65" y="-35" font-family="var(--font-sans)" font-size="14" font-weight="bold" fill="var(--force-green)">Fx</text>
<!-- 右侧楔形块对导线的侧向夹紧力 (Fx) -->
<line x1="90" y1="-25" x2="35" y2="-25" class="force-flow" stroke="var(--force-green)" stroke-width="4" marker-end="url(#arrow-green)"/>
<text x="50" y="-35" font-family="var(--font-sans)" font-size="14" font-weight="bold" fill="var(--force-green)">Fx</text>
</g>
</g>
</g>
</svg>
</div>
<!-- 逻辑控制脚本:同步力学参数显示 -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const uiStatus = document.getElementById('ui-status');
const uiForceY = document.getElementById('ui-force-y');
const uiForceX = document.getElementById('ui-force-x');
const uiMA = document.getElementById('ui-ma');
const totalDuration = 8000; // 动画周期 8s
// 物理参数设定
const theta = 7.125; // 角度
const mu = 0.05; // 滑动摩擦系数假设值
const tanTheta = 0.125;
// 力放大倍数 MA = 1 / [2 * (tanθ + μ)]
const MA = 1 / (2 * (tanTheta + mu));
function updateData() {
const now = Date.now();
const progress = (now % totalDuration) / totalDuration;
// 根据 CSS 动画进度区间设定力学数值
// 0.0 - 0.2: 导线下沉未接触
// 0.2 - 0.4: 接触并推动楔形块下沉
// 0.4 - 0.75: 完全锁紧 (力最大)
// 0.75 - 0.85: 解锁复位中
// 0.85 - 1.0: 顶部待机
let fy = 0;
let fx = 0;
if (progress < 0.2) {
uiStatus.textContent = "空载下行中...";
uiStatus.style.color = "var(--text-dim)";
} else if (progress < 0.4) {
uiStatus.textContent = "楔入自锁行程";
uiStatus.style.color = "var(--tech-cyan)";
// 力值线性增加
const ratio = (progress - 0.2) / 0.2;
fy = ratio * 450;
} else if (progress < 0.75) {
uiStatus.textContent = "触发最终理想解 (IFR自锁)";
uiStatus.style.color = "var(--force-green)";
// 模拟微小的动态力学波动
fy = 450 + Math.random() * 20;
} else if (progress < 0.85) {
uiStatus.textContent = "卸载复位";
uiStatus.style.color = "var(--text-dim)";
const ratio = 1 - ((progress - 0.75) / 0.1);
fy = ratio * 450;
} else {
uiStatus.textContent = "待机准备";
uiStatus.style.color = "var(--text-dim)";
}
// 根据公式计算夹紧力
fx = fy * MA;
uiForceY.textContent = fy.toFixed(0) + " N";
uiForceX.textContent = fx.toFixed(0) + " N";
if (fy > 10) {
uiMA.textContent = MA.toFixed(2) + "x";
uiMA.style.color = "var(--force-green)";
} else {
uiMA.textContent = "--";
uiMA.style.color = "var(--text-dim)";
}
requestAnimationFrame(updateData);
}
// 启动同步循环
requestAnimationFrame(updateData);
});
</script>
</body>
</html>
这里为您提供一份全新设计的、基于高科技工程 CAD 蓝图风格的高保真动画,深度仿真并可视化“楔形自锁效应”的核心力学原理。动画无需任何点击,加载后即刻自动循环播放。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>楔形自锁效应 (Wedge Self-Locking) - 高保真力学仿真</title>
<style>
:root {
/* 战术工程蓝图配色系统 */
--bg-color: #020617; /* 极暗蓝底色 */
--grid-line: rgba(14, 165, 233, 0.15); /* 网格线 */
--grid-sub: rgba(14, 165, 233, 0.05);
/* 核心组件色彩 */
--color-casing: #0F172A; /* 外壳装甲底色 */
--color-casing-border: #38BDF8;/* 外壳边框发光 */
--color-wedge: #1E293B; /* 楔形块 */
--color-wedge-border: #94A3B8;
--color-wire: #E2E8F0; /* 导线核心 */
/* 力学特征色 (高对比度霓虹色) */
--force-input: #F97316; /* 输入拉力 (亮橙) */
--force-clamp: #10B981; /* 夹紧力 (荧光绿) */
--force-fric: #FACC15; /* 摩擦力 (警示黄) */
--force-stress: #EF4444; /* 应力集中/危险 (亮红) */
/* 字体排版规范 */
--font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
--font-sans: system-ui, -apple-system, sans-serif;
/* 全局动画周期 */
--cycle: 8s;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body, html {
width: 100%;
height: 100%;
background-color: var(--bg-color);
background-image:
linear-gradient(var(--grid-line) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px),
linear-gradient(var(--grid-sub) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-sub) 1px, transparent 1px);
background-size: 100px 100px, 100px 100px, 20px 20px, 20px 20px;
background-position: center center;
font-family: var(--font-sans);
color: #fff;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
/* ==================== 边缘数据面板 (严控尺寸防遮挡) ==================== */
.hud-layer {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 100;
}
.hud-panel {
position: absolute;
background: rgba(2, 6, 23, 0.75);
border: 1px solid rgba(56, 189, 248, 0.2);
backdrop-filter: blur(12px);
padding: 16px 20px;
box-shadow: 0 8px 32px rgba(0,0,0,0.6);
border-radius: 4px;
}
/* 绝对边缘定位 */
.panel-tl { top: 20px; left: 20px; width: 300px; border-left: 3px solid var(--color-casing-border); }
.panel-tr { top: 20px; right: 20px; width: 280px; text-align: right; border-right: 3px solid var(--force-clamp); }
.panel-bl { bottom: 20px; left: 20px; width: 320px; border-left: 3px solid var(--force-fric); }
.panel-title {
font-size: 12px;
font-weight: bold;
color: var(--color-casing-border);
margin-bottom: 12px;
letter-spacing: 1px;
text-transform: uppercase;
display: flex;
align-items: center;
gap: 8px;
}
.panel-tr .panel-title { justify-content: flex-end; color: var(--force-clamp); }
.panel-bl .panel-title { color: var(--force-fric); }
.data-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 11px;
font-family: var(--font-mono);
color: #94A3B8;
margin-bottom: 8px;
}
.data-val { font-size: 13px; font-weight: bold; }
.val-orange { color: var(--force-input); text-shadow: 0 0 8px rgba(249,115,22,0.4); }
.val-green { color: var(--force-clamp); text-shadow: 0 0 8px rgba(16,185,129,0.4); }
.val-cyan { color: var(--color-casing-border); }
.desc-text {
font-size: 11px;
line-height: 1.6;
color: #64748B;
margin-top: 12px;
border-top: 1px solid rgba(255,255,255,0.05);
padding-top: 8px;
}
/* ==================== SVG 核心动画区 ==================== */
.svg-canvas {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
svg {
width: 100%;
height: 100%;
max-width: 1000px;
max-height: 800px;
overflow: visible;
}
/* ----- 几何实体基础样式 ----- */
.geo-casing { fill: var(--color-casing); stroke: var(--color-casing-border); stroke-width: 2; }
.geo-wedge { fill: var(--color-wedge); stroke: var(--color-wedge-border); stroke-width: 1.5; }
.geo-pad { fill: #000; stroke: #334155; }
.geo-wire { fill: var(--color-wire); stroke: #CBD5E1; stroke-width: 2; }
.dim-line { stroke: var(--color-casing-border); stroke-dasharray: 4 4; stroke-width: 1; opacity: 0.5; }
.dim-text { fill: var(--color-casing-border); font-family: var(--font-mono); font-size: 12px; }
/* ----- 力学矢量 (透明度由CSS动画控制) ----- */
.vector-arrow { stroke-width: 3; fill: none; opacity: 0; }
.vector-label { font-family: var(--font-mono); font-size: 14px; font-weight: bold; opacity: 0; }
.vec-input { stroke: var(--force-input); }
.lbl-input { fill: var(--force-input); }
.vec-clamp { stroke: var(--force-clamp); }
.lbl-clamp { fill: var(--force-clamp); }
.vec-fric { stroke: var(--force-fric); stroke-width: 2;}
.lbl-fric { fill: var(--force-fric); font-size: 12px;}
/* ----- 应力热区 (危险红) ----- */
.stress-glow {
fill: none;
stroke: var(--force-stress);
stroke-width: 6;
filter: blur(4px);
opacity: 0;
}
/* ==================== 物理系统动画 (CSS Keyframes) ==================== */
/* 1. 中央导线下沉 (主驱动) */
.anim-wire { animation: wireDrop var(--cycle) cubic-bezier(0.25, 0.1, 0.25, 1) infinite; }
@keyframes wireDrop {
0%, 15% { transform: translateY(0px); }
30%, 80% { transform: translateY(80px); }
90%, 100% { transform: translateY(0px); }
}
/* 2. 左侧楔块沿斜面滑动 (dx = 0.2 * dy -> x:16px, y:80px) */
.anim-wedge-l { animation: wedgeSlideL var(--cycle) cubic-bezier(0.25, 0.1, 0.25, 1) infinite; }
@keyframes wedgeSlideL {
0%, 15% { transform: translate(0px, 0px); }
30%, 80% { transform: translate(16px, 80px); }
90%, 100% { transform: translate(0px, 0px); }
}
/* 3. 右侧楔块沿斜面滑动 (dx = -0.2 * dy -> x:-16px, y:80px) */
.anim-wedge-r { animation: wedgeSlideR var(--cycle) cubic-bezier(0.25, 0.1, 0.25, 1) infinite; }
@keyframes wedgeSlideR {
0%, 15% { transform: translate(0px, 0px); }
30%, 80% { transform: translate(-16px, 80px); }
90%, 100% { transform: translate(0px, 0px); }
}
/* 4. 输入力 (下拉) 显示动画 */
.anim-f-input { animation: fInputShow var(--cycle) ease-in-out infinite; transform-origin: top center; }
@keyframes fInputShow {
0%, 10% { opacity: 0; transform: scaleY(0.2); }
25%, 85% { opacity: 1; transform: scaleY(1); }
90%, 100% { opacity: 0; transform: scaleY(0.2); }
}
/* 5. 夹紧力 (侧向) 放大显示动画 */
.anim-f-clamp { animation: fClampShow var(--cycle) ease-in-out infinite; }
.anim-f-clamp-l { transform-origin: right center; }
.anim-f-clamp-r { transform-origin: left center; }
@keyframes fClampShow {
0%, 25% { opacity: 0; transform: scaleX(0.2); }
35%, 80% { opacity: 1; transform: scaleX(1); }
88%, 100% { opacity: 0; transform: scaleX(0.2); }
}
/* 6. 摩擦力/法向力 显示动画 */
.anim-f-fric { animation: fFricShow var(--cycle) ease-in-out infinite; }
@keyframes fFricShow {
0%, 25% { opacity: 0; }
35%, 80% { opacity: 0.8; }
85%, 100% { opacity: 0; }
}
/* 7. 接触面应力发光 (表示自锁死死咬住) */
.anim-stress { animation: stressGlow var(--cycle) ease-in-out infinite; }
@keyframes stressGlow {
0%, 28% { opacity: 0; }
35%, 80% { opacity: 0.8; }
85%, 100% { opacity: 0; }
}
</style>
</head>
<body>
<!-- 边缘数据面板层 -->
<div class="hud-layer">
<!-- 左上:物理机制描述 -->
<div class="hud-panel panel-tl">
<div class="panel-title">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path></svg>
楔形自锁 (Wedge Effect)
</div>
<div class="data-row">
<span>能量转换拓扑</span>
<span class="val-cyan">轴向拉力 → 径向夹紧力</span>
</div>
<div class="data-row">
<span>系统核心状态</span>
<span class="val-cyan" id="ui-sys-status">待命 (STANDBY)</span>
</div>
<div class="desc-text">
楔形结构利用斜面原理,将微小的轴向位移转化为巨大的侧向挤压。当输入外力(如导线自重或外界拉力)拖动楔块下滑时,受固定外壳约束,楔块被迫向内收缩收紧。
</div>
</div>
<!-- 右上:遥测数据流 -->
<div class="hud-panel panel-tr">
<div class="panel-title">动态载荷遥测 (TELEMETRY)</div>
<div class="data-row">
<span>输入轴向拉力 (F_pull)</span>
<span class="data-val val-orange" id="ui-f-input">0 N</span>
</div>
<div class="data-row">
<span>输出径向夹紧力 (F_clamp)</span>
<span class="data-val val-green" id="ui-f-clamp">0 N</span>
</div>
<div class="data-row">
<span style="color:#64748B;">力学放大倍数 (M.A.)</span>
<span class="data-val val-cyan" id="ui-ratio">1.0 x</span>
</div>
</div>
<!-- 左下:自锁条件计算 -->
<div class="hud-panel panel-bl">
<div class="panel-title">自锁临界条件模型 (SELF-LOCK)</div>
<div class="data-row">
<span>外壳斜面夹角 (α)</span>
<span class="data-val val-cyan">11.3° (Slope=0.2)</span>
</div>
<div class="data-row">
<span>界面静摩擦系数 (μ)</span>
<span class="data-val" style="color:var(--force-fric);">0.30</span>
</div>
<div style="background: rgba(0,0,0,0.3); padding: 8px; border-radius: 4px; margin-top: 8px; font-family: var(--font-mono); font-size: 11px; color:#CBD5E1; text-align:center;">
条件: μ ≥ tan(α)<br>
0.30 ≥ 0.20 [ <span style="color:var(--force-clamp); font-weight:bold;">条件达成</span> ]
</div>
<div class="desc-text">
由于接触面的静摩擦力始终大于下滑分力,一旦咬合,无论下方受到多大的破坏性拉扯,系统均绝对不会滑脱,且<strong>“越拉越紧”</strong>。
</div>
</div>
</div>
<!-- 核心 SVG 动画区 -->
<div class="svg-canvas">
<svg viewBox="0 0 1000 800" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- 发光滤镜 -->
<filter id="glow-cyan"><feGaussianBlur stdDeviation="3" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<filter id="glow-orange"><feGaussianBlur stdDeviation="4" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<!-- 箭头标识 -->
<marker id="arrow-in" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--force-input)" />
</marker>
<marker id="arrow-cl" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--force-clamp)" />
</marker>
<marker id="arrow-fr" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="5" markerHeight="5" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--force-fric)" />
</marker>
<!-- 阴影剖面线 -->
<pattern id="hatch-pattern" width="8" height="8" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="0" y2="8" stroke="rgba(56, 189, 248, 0.15)" stroke-width="2" />
</pattern>
</defs>
<!-- 中心坐标辅助系 -->
<g opacity="0.3">
<line x1="500" y1="100" x2="500" y2="700" stroke="var(--color-casing-border)" stroke-dasharray="8 8" stroke-width="1"/>
<circle cx="500" cy="400" r="250" fill="none" stroke="var(--color-casing-border)" stroke-width="0.5"/>
</g>
<!-- ================= 静态外壳 (V型座) ================= -->
<!-- 设定斜率 dx/dy = 0.2, 角度约为 11.3度 -->
<g id="outer-casing">
<!-- 左半壳体 -->
<path class="geo-casing" d="M 360,150 L 420,150 L 476,430 L 360,430 Z" />
<path d="M 360,150 L 420,150 L 476,430 L 360,430 Z" fill="url(#hatch-pattern)" />
<!-- 右半壳体 -->
<path class="geo-casing" d="M 640,150 L 580,150 L 524,430 L 640,430 Z" />
<path d="M 640,150 L 580,150 L 524,430 L 640,430 Z" fill="url(#hatch-pattern)" />
<!-- 角度标注 -->
<line class="dim-line" x1="420" y1="150" x2="420" y2="280" />
<path d="M 420,230 A 80 80 0 0 0 436,230" fill="none" stroke="var(--color-casing-border)" stroke-width="1"/>
<text x="390" y="225" class="dim-text">α=11.3°</text>
</g>
<!-- ================= 动态楔块与导线 ================= -->
<g id="dynamic-system">
<!-- 导线本体 (居中,受力下沉) -->
<!-- 初始占据 x:480~520 -->
<g class="anim-wire" transform="translate(500, 260)">
<rect x="-20" y="-120" width="40" height="400" rx="4" class="geo-wire" />
<!-- 表面纹理 -->
<line x1="-8" y1="-100" x2="-8" y2="250" stroke="#94A3B8" stroke-dasharray="10 10"/>
<line x1="8" y1="-100" x2="8" y2="250" stroke="#94A3B8" stroke-dasharray="10 10"/>
<!-- 输入拉力动画 (F_pull) -->
<g class="anim-f-input" transform="translate(0, 180)">
<line class="vector-arrow vec-input" x1="0" y1="0" x2="0" y2="100" marker-end="url(#arrow-in)" />
<text x="15" y="80" class="vector-label lbl-input">F_pull</text>
</g>
</g>
<!-- 左侧楔块 -->
<!-- 初始内壁 x=470,下沉80px后向内移动16px,内壁达到486。与导线(边缘480)产生视觉挤压咬合 -->
<g class="anim-wedge-l">
<!-- 楔形本体 -->
<path class="geo-wedge" d="M 420,150 L 460,150 L 460,350 L 452,350 L 412,150 Z" transform="translate(10, 0)" />
<!-- 橡胶防滑齿/高摩擦面 -->
<rect class="geo-pad" x="468" y="160" width="4" height="180" rx="2" />
<!-- 摩擦力/法向力图示 -->
<g class="anim-f-fric" transform="translate(440, 250)">
<line class="vector-arrow vec-fric" x1="-30" y1="6" x2="20" y2="-4" marker-end="url(#arrow-fr)" />
<text x="-40" y="25" class="vector-label lbl-fric">F_N</text>
<line class="vector-arrow vec-fric" x1="-10" y1="30" x2="-20" y2="-20" marker-end="url(#arrow-fr)" />
<text x="-35" y="-15" class="vector-label lbl-fric">f</text>
</g>
</g>
<!-- 右侧楔块 -->
<g class="anim-wedge-r">
<path class="geo-wedge" d="M 580,150 L 540,150 L 540,350 L 548,350 L 588,150 Z" transform="translate(-10, 0)" />
<rect class="geo-pad" x="528" y="160" width="4" height="180" rx="2" />
</g>
<!-- ================= 应力可视化与夹紧力 (随咬合显示) ================= -->
<!-- 左侧夹紧力 -->
<g class="anim-f-clamp anim-f-clamp-l" transform="translate(470, 260)">
<line class="vector-arrow vec-clamp" x1="-120" y1="0" x2="-20" y2="0" marker-end="url(#arrow-cl)" />
<text x="-130" y="-12" class="vector-label lbl-clamp">F_clamp</text>
</g>
<!-- 右侧夹紧力 -->
<g class="anim-f-clamp anim-f-clamp-r" transform="translate(530, 260)">
<line class="vector-arrow vec-clamp" x1="120" y1="0" x2="20" y2="0" marker-end="url(#arrow-cl)" />
<text x="60" y="-12" class="vector-label lbl-clamp">F_clamp</text>
</g>
<!-- 咬合界面的红色应力高亮 -->
<g class="anim-stress">
<line class="stress-glow" x1="475" y1="260" x2="475" y2="400" />
<line class="stress-glow" x1="525" y1="260" x2="525" y2="400" />
</g>
</g>
</svg>
</div>
<!-- 数据同步脚本:将 CSS 时间轴映射为 UI 数字展示 -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const uiInput = document.getElementById('ui-f-input');
const uiClamp = document.getElementById('ui-f-clamp');
const uiRatio = document.getElementById('ui-ratio');
const uiStatus = document.getElementById('ui-sys-status');
const CYCLE_MS = 8000; // 与 CSS 变量 --cycle 一致
function updateData(timestamp) {
// 计算 0 ~ 1 进度
const p = (timestamp % CYCLE_MS) / CYCLE_MS;
let inputForce = 0;
let clampForce = 0;
let statusText = "待命 (STANDBY)";
let statusColor = "var(--color-casing-border)";
// 状态机映射 (匹配 CSS Keyframes)
// 0-15%: 待机
// 15-30%: 下拉发力
// 30-80%: 锁定/最大受力
// 80-90%: 释放复位
// 90-100%: 待机
if (p > 0.15 && p <= 0.30) {
const ratio = (p - 0.15) / 0.15;
// 缓动曲线
const ease = 1 - Math.pow(1 - ratio, 3);
inputForce = ease * 1200;
clampForce = ease * 6000; // 放大系数约为 5x
statusText = "位移触发/收紧中...";
statusColor = "var(--force-fric)";
}
else if (p > 0.30 && p <= 0.80) {
// 加入随机抖动模拟真实受力
inputForce = 1200 + Math.random() * 20;
clampForce = 6000 + Math.random() * 50;
statusText = "完全自锁 [LOCKED]";
statusColor = "var(--force-clamp)";
}
else if (p > 0.80 && p <= 0.90) {
const ratio = (p - 0.80) / 0.10;
inputForce = 1200 * (1 - ratio);
clampForce = 6000 * (1 - ratio);
statusText = "张力释放...";
statusColor = "var(--force-input)";
}
// 渲染数据
uiInput.textContent = Math.floor(inputForce) + " N";
uiClamp.textContent = Math.floor(clampForce) + " N";
uiStatus.textContent = statusText;
uiStatus.style.color = statusColor;
// 机械增益 (Mechanical Advantage)
if (inputForce > 50) {
uiRatio.textContent = (clampForce / inputForce).toFixed(1) + " x";
} else {
uiRatio.textContent = "1.0 x";
}
requestAnimationFrame(updateData);
}
// 启动引擎
requestAnimationFrame(updateData);
});
</script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
