独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>高保真 SVG 原理动画 - TRIZ 最终理想解</title>
<style>
:root {
--bg-color: #0b1120;
--panel-bg: rgba(15, 23, 42, 0.75);
--border-color: rgba(51, 65, 85, 0.6);
--text-main: #f8fafc;
--text-muted: #94a3b8;
--accent-cyan: #06b6d4;
--accent-orange: #f97316;
--accent-green: #10b981;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: var(--bg-color);
color: var(--text-main);
display: flex;
height: 100vh;
overflow: hidden;
background-image:
radial-gradient(circle at 50% 50%, rgba(6, 182, 212, 0.05) 0%, transparent 50%),
linear-gradient(rgba(255,255,255,0.02) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.02) 1px, transparent 1px);
background-size: 100% 100%, 40px 40px, 40px 40px;
}
.canvas-container {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
position: relative;
padding: 2rem;
}
svg {
width: 100%;
height: 100%;
max-width: 900px;
max-height: 900px;
filter: drop-shadow(0 0 20px rgba(0,0,0,0.5));
}
.panel-container {
width: 380px;
background: var(--panel-bg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border-left: 1px solid var(--border-color);
display: flex;
flex-direction: column;
padding: 2rem;
z-index: 10;
overflow-y: auto;
box-shadow: -10px 0 30px rgba(0,0,0,0.5);
}
.badge {
display: inline-block;
padding: 0.25rem 0.75rem;
background: linear-gradient(135deg, #10b98122, #05966922);
color: var(--accent-green);
border: 1px solid rgba(16, 185, 129, 0.3);
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 1px;
margin-bottom: 1rem;
text-transform: uppercase;
}
h1 {
font-size: 1.5rem;
font-weight: 700;
line-height: 1.3;
margin-bottom: 1.5rem;
background: linear-gradient(to right, #fff, #94a3b8);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.section-title {
font-size: 0.85rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 2px;
margin-bottom: 0.75rem;
margin-top: 1.5rem;
border-bottom: 1px solid var(--border-color);
padding-bottom: 0.5rem;
}
.triz-card {
background: rgba(255,255,255,0.03);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
}
.triz-card p {
font-size: 0.9rem;
line-height: 1.6;
color: #cbd5e1;
}
.triz-card strong {
color: var(--accent-orange);
font-weight: 600;
}
.param-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.75rem;
margin-bottom: 1.5rem;
}
.param-item {
background: rgba(0,0,0,0.3);
padding: 0.75rem;
border-radius: 6px;
border: 1px solid rgba(255,255,255,0.05);
}
.param-value {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
color: var(--accent-cyan);
font-weight: 700;
font-size: 1.1rem;
margin-bottom: 0.2rem;
}
.param-label {
font-size: 0.7rem;
color: var(--text-muted);
}
.controls {
margin-top: auto;
background: rgba(0,0,0,0.3);
padding: 1.25rem;
border-radius: 8px;
border: 1px solid var(--border-color);
}
.control-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.control-title {
font-size: 0.9rem;
font-weight: 600;
}
.btn-toggle {
background: transparent;
border: 1px solid var(--accent-cyan);
color: var(--accent-cyan);
padding: 0.4rem 1rem;
border-radius: 4px;
cursor: pointer;
font-size: 0.8rem;
font-weight: 600;
transition: all 0.2s ease;
}
.btn-toggle:hover {
background: rgba(6, 182, 212, 0.1);
}
.btn-toggle.active {
background: var(--accent-cyan);
color: #000;
}
.slider-container {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.slider-labels {
display: flex;
justify-content: space-between;
font-size: 0.75rem;
color: var(--text-muted);
}
input[type=range] {
-webkit-appearance: none;
width: 100%;
background: transparent;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 16px;
width: 16px;
border-radius: 50%;
background: var(--accent-cyan);
cursor: pointer;
margin-top: -6px;
box-shadow: 0 0 10px rgba(6, 182, 212, 0.5);
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 4px;
cursor: pointer;
background: var(--border-color);
border-radius: 2px;
}
/* 隐藏滚动条 */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(255,255,255,0.1);
border-radius: 3px;
}
</style>
</head>
<body>
<div class="canvas-container">
<svg id="pumpSvg" viewBox="0 0 800 800" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="glowOrange" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="6" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<filter id="glowCyan" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="8" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<marker id="arrowhead" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
<polygon points="0 0, 6 3, 0 6" fill="var(--accent-green)" />
</marker>
<radialGradient id="hubGradient" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#334155"/>
<stop offset="100%" stop-color="#0f172a"/>
</radialGradient>
<!-- SVG 图案:背景网格 -->
<pattern id="gridPattern" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(255,255,255,0.03)" stroke-width="1"/>
</pattern>
</defs>
<!-- 中心对齐基准:cx=400, cy=400 -->
<!-- 背景外壳 / 泵管定子 -->
<path id="stator" d="M 580 400 A 180 180 0 0 1 220 400" fill="none" stroke="#1e293b" stroke-width="24" stroke-linecap="round"/>
<path id="stator-inner" d="M 568 400 A 168 168 0 0 1 232 400" fill="none" stroke="#334155" stroke-width="2" stroke-linecap="round"/>
<!-- 动态流体区域 -->
<path id="fluidArea" fill="var(--accent-cyan)" fill-opacity="0.3" filter="url(#glowCyan)"/>
<!-- 动态流体气泡(由 JS 插入) -->
<g id="fluidBubbles"></g>
<!-- 动态压缩指示箭头(由 JS 插入) -->
<g id="compressionArrows"></g>
<!-- 核心创新:柔性中介带 -->
<path id="isolationBelt" fill="none" stroke="var(--accent-orange)" stroke-width="4" filter="url(#glowOrange)"/>
<!-- 隔离带无滑动标记(证明纯径向运动) -->
<g id="beltMarkers"></g>
<!-- 驱动转子组 -->
<g id="rotorSystem">
<!-- 中心轮毂 -->
<circle cx="400" cy="400" r="30" fill="url(#hubGradient)" stroke="#475569" stroke-width="2"/>
<circle cx="400" cy="400" r="10" fill="#94a3b8"/>
<!-- 转子臂与滚轮(由 JS 控制位置) -->
<g id="arms"></g>
<g id="rollers"></g>
</g>
<!-- 注释与说明引线 -->
<g id="annotations" font-family="ui-sans-serif, system-ui" font-size="13">
<!-- 隔离带标注 -->
<path d="M 280 230 L 250 200 L 150 200" fill="none" stroke="#cbd5e1" stroke-width="1" stroke-dasharray="3,3"/>
<circle cx="280" cy="230" r="3" fill="#cbd5e1"/>
<text x="150" y="190" fill="var(--accent-orange)" font-weight="bold">环形柔性隔离带</text>
<text x="150" y="215" fill="#94a3b8" font-size="11">隔离滚动摩擦</text>
<!-- 纯垂直挤压标注 -->
<path d="M 400 550 L 400 620 L 500 620" fill="none" stroke="#cbd5e1" stroke-width="1" stroke-dasharray="3,3"/>
<circle cx="400" cy="550" r="3" fill="#cbd5e1"/>
<text x="510" y="615" fill="var(--accent-green)" font-weight="bold">纯径向(垂直)挤压</text>
<text x="510" y="635" fill="#94a3b8" font-size="11">消除切向拉扯,管壁零磨损</text>
<!-- 无相对滑动标注 -->
<path d="M 525 320 L 580 250 L 650 250" fill="none" stroke="#cbd5e1" stroke-width="1" stroke-dasharray="3,3"/>
<circle cx="525" cy="320" r="3" fill="#cbd5e1"/>
<text x="575" y="240" fill="#f8fafc" font-weight="bold">固定标记位验证</text>
<text x="575" y="265" fill="#94a3b8" font-size="11">隔离带只产生径向形变</text>
</g>
</svg>
</div>
<div class="panel-container">
<div class="badge">TRIZ 最终理想解 (IFR)</div>
<h1>随动柔性隔离中介设计</h1>
<div class="triz-card">
<p><strong>传统矛盾:</strong> 滚轮直接作用于泵管,必须既要“滚动推进流体”(需求),又要“避免切向摩擦撕裂管壁”(限制)。</p>
<div style="height: 1px; background: rgba(255,255,255,0.1); margin: 0.75rem 0;"></div>
<p><strong>理想解机理:</strong> 引入一层随动的柔性中介带,系统巧妙地将滚轮的滚动摩擦完全吸收,转化为中介带对泵管的<strong>纯垂直挤压</strong>,彻底根除了管壁的切向拉扯损耗。</p>
</div>
<div class="section-title">核心工程参数</div>
<div class="param-grid">
<div class="param-item">
<div class="param-value">1.2<span style="font-size:0.6em">mm</span></div>
<div class="param-label">隔离带厚度</div>
</div>
<div class="param-item">
<div class="param-value">>15<span style="font-size:0.6em">MPa</span></div>
<div class="param-label">抗拉伸屈服强度</div>
</div>
</div>
<div class="section-title">动态视觉指引</div>
<ul style="list-style: none; padding-left: 0; font-size: 0.85rem; color: var(--text-muted); line-height: 1.8; margin-bottom: 2rem;">
<li><span style="display:inline-block; width:12px; height:12px; border-radius:50%; background:var(--accent-orange); margin-right:8px; box-shadow: 0 0 5px var(--accent-orange);"></span>橙色外环为高强度柔性隔离带</li>
<li><span style="display:inline-block; width:12px; height:12px; border-radius:50%; background:var(--accent-green); margin-right:8px; box-shadow: 0 0 5px var(--accent-green);"></span>绿色箭头指示纯垂直径向挤压力</li>
<li><span style="display:inline-block; width:12px; height:12px; border-radius:50%; background:#fb923c; margin-right:8px;"></span>带上定点标记仅随径向涨缩无切向位移</li>
</ul>
<div class="controls">
<div class="control-header">
<span class="control-title">系统运行控制</span>
<button id="toggleBtn" class="btn-toggle active">暂停运行</button>
</div>
<div class="slider-container">
<input type="range" id="speedSlider" min="0.5" max="4" step="0.1" value="1.5">
<div class="slider-labels">
<span>慢速演示</span>
<span>全速运转</span>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// --- 核心物理参数 ---
const CENTER = 400;
const R_ARM = 120; // 转子臂长
const R_ROLLER = 35; // 滚轮半径
const R_IDLE = 125; // 隔离带非受力时的自然基准半径
const R_STATOR = 175; // 定子(外壳)内壁半径
// --- 动画状态 ---
let isPlaying = true;
let rotorAngle = 0; // 转子整体角度
let speed = 1.5; // 每帧旋转角度
// --- DOM 元素引用 ---
const armsGroup = document.getElementById('arms');
const rollersGroup = document.getElementById('rollers');
const beltPathEl = document.getElementById('isolationBelt');
const fluidAreaEl = document.getElementById('fluidArea');
const beltMarkersGroup = document.getElementById('beltMarkers');
const compressionArrowsGroup = document.getElementById('compressionArrows');
const fluidBubblesGroup = document.getElementById('fluidBubbles');
const toggleBtn = document.getElementById('toggleBtn');
const speedSlider = document.getElementById('speedSlider');
// --- 初始化构造 UI 元素 ---
// 1. 创建转子臂和滚轮 (3组,相隔120度)
for(let i=0; i<3; i++) {
// 转子臂
let arm = document.createElementNS("http://www.w3.org/2000/svg", "line");
arm.setAttribute("id", `arm_${i}`);
arm.setAttribute("x1", CENTER);
arm.setAttribute("y1", CENTER);
arm.setAttribute("stroke", "#64748b");
arm.setAttribute("stroke-width", "12");
arm.setAttribute("stroke-linecap", "round");
armsGroup.appendChild(arm);
// 滚轮组
let rollerGrp = document.createElementNS("http://www.w3.org/2000/svg", "g");
rollerGrp.setAttribute("id", `roller_${i}`);
let rollerOuter = document.createElementNS("http://www.w3.org/2000/svg", "circle");
rollerOuter.setAttribute("r", R_ROLLER);
rollerOuter.setAttribute("fill", "#1e293b");
rollerOuter.setAttribute("stroke", "#94a3b8");
rollerOuter.setAttribute("stroke-width", "3");
// 滚轮内旋转指示线(表明其自身也在滚动)
let rollerSpoke = document.createElementNS("http://www.w3.org/2000/svg", "line");
rollerSpoke.setAttribute("x1", 0);
rollerSpoke.setAttribute("y1", -R_ROLLER);
rollerSpoke.setAttribute("x2", 0);
rollerSpoke.setAttribute("y2", R_ROLLER);
rollerSpoke.setAttribute("stroke", "#475569");
rollerSpoke.setAttribute("stroke-width", "4");
let rollerCenter = document.createElementNS("http://www.w3.org/2000/svg", "circle");
rollerCenter.setAttribute("r", "6");
rollerCenter.setAttribute("fill", "#cbd5e1");
rollerGrp.appendChild(rollerOuter);
rollerGrp.appendChild(rollerSpoke);
rollerGrp.appendChild(rollerCenter);
rollersGroup.appendChild(rollerGrp);
}
// 2. 创建隔离带定点标记 (每隔 10 度一个,共 36 个)
// 这些点只会在当前角度上做径向伸缩,证明隔离带没有滑动
for(let i=0; i<36; i++) {
let dot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
dot.setAttribute("id", `marker_${i*10}`);
dot.setAttribute("r", "3");
dot.setAttribute("fill", "#fcd34d");
beltMarkersGroup.appendChild(dot);
}
// 3. 创建纯垂直挤压指示箭头 (下半圆工作区:20度到160度)
for(let i=2; i<=16; i++) {
let angle = i * 10;
let rad = angle * Math.PI / 180;
let r1 = 158; // 箭头起点基准
let r2 = 170; // 箭头终点基准
let arrow = document.createElementNS("http://www.w3.org/2000/svg", "line");
arrow.setAttribute("id", `arrow_${angle}`);
arrow.setAttribute("x1", CENTER + r1 * Math.cos(rad));
arrow.setAttribute("y1", CENTER + r1 * Math.sin(rad));
arrow.setAttribute("x2", CENTER + r2 * Math.cos(rad));
arrow.setAttribute("y2", CENTER + r2 * Math.sin(rad));
arrow.setAttribute("stroke", "var(--accent-green)");
arrow.setAttribute("stroke-width", "3");
arrow.setAttribute("marker-end", "url(#arrowhead)");
arrow.setAttribute("opacity", "0"); // 默认透明
compressionArrowsGroup.appendChild(arrow);
}
// 4. 初始化流体气泡
const numBubbles = 30;
const bubbles = [];
for(let i=0; i<numBubbles; i++) {
let bubble = document.createElementNS("http://www.w3.org/2000/svg", "circle");
let size = Math.random() * 3 + 2;
bubble.setAttribute("r", size);
bubble.setAttribute("fill", "#ffffff");
bubble.setAttribute("opacity", "0.6");
fluidBubblesGroup.appendChild(bubble);
bubbles.push({
el: bubble,
angle: Math.random() * 180, // 只在下半圆 [0, 180] 出现
offsetRatio: Math.random() // 在隔离带和定子之间的径向偏移比例
});
}
// --- 核心数学机理:计算任意角度下隔离带被撑开的半径 ---
// 隔离带是柔性的,被滚轮向外撑开。
// 由于有 3 个滚轮,我们需要取自然状态和三个滚轮影响的“最大值外包络线”。
function getBeltRadius(thetaDeg, currentRotorAngles) {
let maxR = R_IDLE;
for(let a of currentRotorAngles) {
// 计算当前计算点与滚轮中心的夹角差
let diff = (thetaDeg - a) * Math.PI / 180;
// 归一化到 [-pi, pi]
diff = Math.atan2(Math.sin(diff), Math.cos(diff));
// 判断该角度射线是否与滚轮圆相交
if (Math.abs(R_ARM * Math.sin(diff)) <= R_ROLLER) {
// 求解射线与滚轮外缘的交点到圆心的距离
let val = Math.max(0, R_ROLLER * R_ROLLER - R_ARM * R_ARM * Math.sin(diff) * Math.sin(diff));
let rInter = R_ARM * Math.cos(diff) + Math.sqrt(val);
if (rInter > maxR) {
maxR = rInter;
}
}
}
return maxR;
}
// --- 渲染循环 (高频执行) ---
function render() {
if (isPlaying) {
rotorAngle = (rotorAngle + speed) % 360;
}
// 3个滚轮的绝对角度
let rAngles = [rotorAngle, (rotorAngle + 120) % 360, (rotorAngle + 240) % 360];
// 1. 预计算 0~359 度的隔离带半径
let beltR = new Array(360);
for(let i=0; i<360; i++) {
beltR[i] = getBeltRadius(i, rAngles);
}
// 2. 绘制隔离带路径 (连续闭合曲线)
let bPath = "";
for(let i=0; i<360; i++) {
let rad = i * Math.PI / 180;
let x = CENTER + beltR[i] * Math.cos(rad);
let y = CENTER + beltR[i] * Math.sin(rad);
bPath += (i === 0 ? "M" : "L") + x + "," + y + " ";
}
bPath += "Z";
beltPathEl.setAttribute("d", bPath);
// 3. 绘制被挤压的流体区域 (仅在下半部分 0 到 180 度有效)
// 流体存在于 隔离带外壁与定子内壁之间
let fPath = "";
// 正向沿隔离带外壁走
for(let i=0; i<=180; i++) {
let rad = i * Math.PI / 180;
// 流体内边界是隔离带的半径 + 厚度预留
let rInner = beltR[i] + 3;
if(rInner > R_STATOR) rInner = R_STATOR; // 完全闭合
let x = CENTER + rInner * Math.cos(rad);
let y = CENTER + rInner * Math.sin(rad);
fPath += (i === 0 ? "M" : "L") + x + "," + y + " ";
}
// 反向沿定子内壁走回
for(let i=180; i>=0; i--) {
let rad = i * Math.PI / 180;
let x = CENTER + R_STATOR * Math.cos(rad);
let y = CENTER + R_STATOR * Math.sin(rad);
fPath += "L" + x + "," + y + " ";
}
fPath += "Z";
fluidAreaEl.setAttribute("d", fPath);
// 4. 更新转子臂和滚轮位置
for(let i=0; i<3; i++) {
let a = rAngles[i];
let rad = a * Math.PI / 180;
let cx = CENTER + R_ARM * Math.cos(rad);
let cy = CENTER + R_ARM * Math.sin(rad);
// 更新机械臂末端位置
let arm = document.getElementById(`arm_${i}`);
arm.setAttribute("x2", cx);
arm.setAttribute("y2", cy);
// 滚轮自身也在发生逆向或同向滚动(表现其在隔离带内侧滚动)
// R_ARM / R_ROLLER = 120 / 35 ≈ 3.4
let rollerRotation = a * 3.4;
let rGrp = document.getElementById(`roller_${i}`);
rGrp.setAttribute("transform", `translate(${cx}, ${cy}) rotate(${rollerRotation})`);
}
// 5. 更新隔离带定点标记(纯径向伸缩)
for(let i=0; i<36; i++) {
let angle = i * 10;
let rad = angle * Math.PI / 180;
let r = beltR[angle];
let dot = document.getElementById(`marker_${angle}`);
if (dot) {
dot.setAttribute("cx", CENTER + r * Math.cos(rad));
dot.setAttribute("cy", CENTER + r * Math.sin(rad));
}
}
// 6. 更新纯垂直挤压箭头 (动态显示挤压力)
// 只有当隔离带产生明显的向外扩张时,才显示垂直挤压箭头
for(let i=2; i<=16; i++) {
let angle = i * 10;
let currentR = beltR[angle];
let arrow = document.getElementById(`arrow_${angle}`);
if (arrow) {
// 半径超过 130 开始显示,达到 155 时最亮
let opacity = (currentR - 130) / (155 - 130);
opacity = Math.max(0, Math.min(1, opacity));
arrow.setAttribute("opacity", opacity.toFixed(2));
// 让箭头稍微向外移动,增加压迫感
let pushOffset = opacity * 5;
let rad = angle * Math.PI / 180;
let r1 = 155 + pushOffset;
let r2 = 167 + pushOffset;
arrow.setAttribute("x1", CENTER + r1 * Math.cos(rad));
arrow.setAttribute("y1", CENTER + r1 * Math.sin(rad));
arrow.setAttribute("x2", CENTER + r2 * Math.cos(rad));
arrow.setAttribute("y2", CENTER + r2 * Math.sin(rad));
}
}
// 7. 更新流体气泡动画 (展现被封装的流体前移)
for(let i=0; i<bubbles.length; i++) {
let b = bubbles[i];
if (isPlaying) {
// 流体整体推进速度大致等同于转子扫过角度
b.angle = (b.angle + speed) % 180;
}
let bAngleInt = Math.floor(b.angle);
let currentInnerR = beltR[bAngleInt] || R_IDLE;
// 如果该角度被完全闭合(被挤压住了),气泡隐藏
if (currentInnerR < R_STATOR - 8) {
let rad = b.angle * Math.PI / 180;
// 在内外壁之间按比例分布
let rDist = currentInnerR + 4 + b.offsetRatio * (R_STATOR - currentInnerR - 8);
let bx = CENTER + rDist * Math.cos(rad);
let by = CENTER + rDist * Math.sin(rad);
b.el.setAttribute("cx", bx);
b.el.setAttribute("cy", by);
b.el.setAttribute("opacity", b.offsetRatio * 0.6 + 0.2); // 越靠外的越亮
} else {
b.el.setAttribute("opacity", 0);
}
}
requestAnimationFrame(render);
}
// 启动渲染循环
render();
// --- 交互事件 ---
toggleBtn.addEventListener('click', () => {
isPlaying = !isPlaying;
if (isPlaying) {
toggleBtn.textContent = "暂停运行";
toggleBtn.classList.add('active');
} else {
toggleBtn.textContent = "恢复运行";
toggleBtn.classList.remove('active');
}
});
speedSlider.addEventListener('input', (e) => {
speed = parseFloat(e.target.value);
});
});
</script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
