独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全环面球面牵引无级变速系统 (TRIZ IFR)</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Rajdhani:wght@500;600;700&family=JetBrains+Mono:wght@400;700&display=swap');
:root {
--bg-color: #050608;
--grid-color: rgba(255, 255, 255, 0.03);
--primary-glow: #00f0ff; /* Input */
--secondary-glow: #ff0055; /* Output */
--accent-glow: #facc15; /* Tilt/EHL */
--text-main: #e2e8f0;
--text-muted: #64748b;
--panel-bg: rgba(10, 15, 20, 0.65);
--panel-border: rgba(255, 255, 255, 0.1);
}
body, html {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
background-color: var(--bg-color);
color: var(--text-main);
font-family: 'JetBrains Mono', monospace;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
#animation-container {
position: relative;
width: 100%;
height: 100%;
}
svg {
width: 100%;
height: 100%;
display: block;
}
/* HUD Panels */
.hud-panel {
position: absolute;
background: var(--panel-bg);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid var(--panel-border);
padding: 1.5rem;
border-radius: 4px;
pointer-events: none;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.hud-panel::before {
content: '';
position: absolute;
top: 0; left: 0; width: 3px; height: 100%;
}
/* Top Left: Title & IFR Concept */
#triz-info {
top: 2rem;
left: 2rem;
max-width: 380px;
}
#triz-info::before { background: var(--accent-glow); }
/* Top Right: Telemetry Data */
#telemetry {
top: 2rem;
right: 2rem;
width: 280px;
}
#telemetry::before { background: var(--primary-glow); }
/* Bottom Right: Live Graph */
#graph-panel {
bottom: 2rem;
right: 2rem;
width: 350px;
height: 120px;
}
#graph-panel::before { background: var(--secondary-glow); }
h1 {
font-family: 'Rajdhani', sans-serif;
font-size: 1.5rem;
text-transform: uppercase;
letter-spacing: 2px;
margin: 0 0 0.5rem 0;
color: #fff;
}
h2 {
font-family: 'Rajdhani', sans-serif;
font-size: 0.9rem;
color: var(--accent-glow);
margin: 0 0 1rem 0;
text-transform: uppercase;
letter-spacing: 1px;
}
.desc-text {
font-size: 0.75rem;
line-height: 1.6;
color: var(--text-muted);
margin-bottom: 0.8rem;
}
.desc-text strong {
color: #fff;
font-weight: 700;
}
/* Data Readouts */
.data-row {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 0.8rem;
border-bottom: 1px solid rgba(255,255,255,0.05);
padding-bottom: 0.3rem;
}
.data-row:last-child { margin-bottom: 0; border-bottom: none; }
.data-label {
font-size: 0.7rem;
color: var(--text-muted);
text-transform: uppercase;
}
.data-value {
font-size: 1.2rem;
font-weight: 700;
}
.val-input { color: var(--primary-glow); }
.val-output { color: var(--secondary-glow); }
.val-ratio { color: #fff; }
.val-angle { color: var(--accent-glow); }
/* Graph Canvas */
#ratio-graph {
width: 100%;
height: 100%;
}
/* SVG specific styles */
.glow-line {
filter: drop-shadow(0 0 8px currentColor);
}
</style>
</head>
<body>
<div id="animation-container">
<svg viewBox="0 0 1600 900" preserveAspectRatio="xMidYMid slice">
<defs>
<!-- Background Grid Pattern -->
<pattern id="grid" 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>
<!-- Glow Filters -->
<filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="8" result="blur" />
<feMerge>
<feMergeNode in="blur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="glow-pink" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="8" result="blur" />
<feMerge>
<feMergeNode in="blur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="glow-ehl" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="6" result="blur" />
<feComponentTransfer in="blur" result="glow">
<feFuncA type="linear" slope="2"/>
</feComponentTransfer>
<feMerge>
<feMergeNode in="glow"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- Gradients -->
<linearGradient id="metal-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#475569" />
<stop offset="50%" stop-color="#1e293b" />
<stop offset="100%" stop-color="#0f172a" />
</linearGradient>
<radialGradient id="ball-grad" cx="30%" cy="30%" r="70%">
<stop offset="0%" stop-color="#94a3b8" />
<stop offset="70%" stop-color="#334155" />
<stop offset="100%" stop-color="#0f172a" />
</radialGradient>
</defs>
<!-- Background -->
<rect width="100%" height="100%" fill="url(#grid)" />
<!-- Center Origin Group -->
<g transform="translate(800, 400)">
<!-- Main Axis Line -->
<line x1="-700" y1="350" x2="700" y2="350" stroke="#334155" stroke-width="2" stroke-dasharray="10, 10" />
<text x="650" y="340" fill="#64748b" font-size="12" text-anchor="end">SYSTEM MAIN AXIS</text>
<!-- Static Housing / Disks Outline -->
<!-- Input Torus Disk (Left) -->
<path d="M -300 -100 C -200 -100, -180 50, -180 150 C -180 250, -200 400, -300 400 L -400 400 L -400 -100 Z" fill="url(#metal-grad)" stroke="#1e293b" stroke-width="2"/>
<!-- Output Torus Disk (Right) -->
<path d="M 300 -100 C 200 -100, 180 50, 180 150 C 180 250, 200 400, 300 400 L 400 400 L 400 -100 Z" fill="url(#metal-grad)" stroke="#1e293b" stroke-width="2"/>
<!-- Rotating Indicators (Visualizing Speed) -->
<path id="input-spin" d="M -290 -90 C -190 -90, -170 50, -170 150 C -170 250, -190 390, -290 390" fill="none" stroke="var(--primary-glow)" stroke-width="4" stroke-dasharray="20 40" filter="url(#glow-cyan)" opacity="0.8"/>
<path id="output-spin" d="M 290 -90 C 190 -90, 170 50, 170 150 C 170 250, 190 390, 290 390" fill="none" stroke="var(--secondary-glow)" stroke-width="4" stroke-dasharray="20 40" filter="url(#glow-pink)" opacity="0.8"/>
<!-- Dynamic Mechanics Group -->
<g id="mechanics">
<!-- Dynamic Radius Lines (R_in, R_out) -->
<line id="rad-in-line" x1="0" y1="350" x2="0" y2="0" stroke="var(--primary-glow)" stroke-width="2" stroke-dasharray="4 4" opacity="0.6"/>
<line id="rad-out-line" x1="0" y1="350" x2="0" y2="0" stroke="var(--secondary-glow)" stroke-width="2" stroke-dasharray="4 4" opacity="0.6"/>
<text id="rad-in-text" x="0" y="0" fill="var(--primary-glow)" font-size="12" text-anchor="end" dx="-10">R_in</text>
<text id="rad-out-text" x="0" y="0" fill="var(--secondary-glow)" font-size="12" text-anchor="start" dx="10">R_out</text>
<!-- Traction Ball Sub-system -->
<!-- The ball's center is vertically offset from main axis. Let's say main axis is at Y=350 relative to center, ball is at Y=0 -->
<g id="traction-ball-group">
<!-- The Steel Ball -->
<circle cx="0" cy="0" r="160" fill="url(#ball-grad)" stroke="#475569" stroke-width="2" />
<!-- Ball Spin visual lines (equator/meridians abstraction) -->
<ellipse cx="0" cy="0" rx="160" ry="40" fill="none" stroke="#475569" stroke-width="1" opacity="0.5" transform="rotate(90)" />
<!-- Tilt Axis -->
<line x1="0" y1="-220" x2="0" y2="220" stroke="var(--accent-glow)" stroke-width="3" filter="url(#glow-ehl)" />
<!-- Axis End Caps -->
<circle cx="0" cy="-220" r="8" fill="var(--bg-color)" stroke="var(--accent-glow)" stroke-width="2"/>
<circle cx="0" cy="220" r="8" fill="var(--bg-color)" stroke="var(--accent-glow)" stroke-width="2"/>
</g>
<!-- EHL Oil Film Contact Points (Drawn over the ball to stay fixed relative to disks while axis tilts) -->
<g id="contact-in" filter="url(#glow-ehl)">
<ellipse cx="0" cy="0" rx="6" ry="18" fill="#facc15" />
<circle cx="0" cy="0" r="12" fill="none" stroke="#facc15" stroke-width="2" class="pulse-ring"/>
</g>
<g id="contact-out" filter="url(#glow-ehl)">
<ellipse cx="0" cy="0" rx="6" ry="18" fill="#facc15" />
<circle cx="0" cy="0" r="12" fill="none" stroke="#facc15" stroke-width="2" class="pulse-ring"/>
</g>
</g>
</g>
</svg>
</div>
<!-- Left HUD: Context & IFR -->
<div id="triz-info" class="hud-panel">
<h1>全环面球面牵引</h1>
<h2>TRIZ: 最终理想解 (IFR)</h2>
<div class="desc-text">
<strong>问题重构:</strong>传统行星排依靠刚性齿轮啮合,固定节圆导致传动比无法在单体内平滑演变。
</div>
<div class="desc-text">
<strong>理想状态实现:</strong>用高硬度钢球替换带齿行星轮,用平滑牵引环替代太阳轮/齿圈。通过液压控制支架改变钢球自转轴倾角,<strong>无需改变系统基本拓扑</strong>即可无级改变接触半径。
</div>
<div class="desc-text">
<strong>资源极致利用:</strong>利用 <strong>EHL (弹性流体动压) 极压油膜</strong>,在 >300 MPa 压力下油膜发生玻璃化转变,产生巨大抗剪切力传递扭矩,彻底消除了金属实体干涉的物理矛盾。
</div>
</div>
<!-- Right HUD: Telemetry -->
<div id="telemetry" class="hud-panel">
<div class="data-row">
<span class="data-label">Input Speed (Sun)</span>
<span class="data-value val-input" id="val-in-rpm">3000 RPM</span>
</div>
<div class="data-row">
<span class="data-label">Planet Tilt Angle (θ)</span>
<span class="data-value val-angle" id="val-tilt">0.00°</span>
</div>
<div class="data-row">
<span class="data-label">Traction Ratio (i)</span>
<span class="data-value val-ratio" id="val-ratio">1.000</span>
</div>
<div class="data-row">
<span class="data-label">Output Speed (Ring)</span>
<span class="data-value val-output" id="val-out-rpm">3000 RPM</span>
</div>
</div>
<!-- Bottom Right HUD: Graph -->
<div id="graph-panel" class="hud-panel">
<div style="font-size: 0.6rem; color: var(--text-muted); margin-bottom: 5px; text-transform: uppercase;">Ratio Curve History</div>
<svg id="ratio-graph">
<!-- Axes -->
<line x1="0" y1="100%" x2="100%" y2="100%" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<line x1="0" y1="0" x2="0" y2="100%" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<!-- Ratio 1.0 Reference Line -->
<line x1="0" y1="50%" x2="100%" y2="50%" stroke="rgba(255,255,255,0.1)" stroke-width="1" stroke-dasharray="2 2"/>
<!-- Dynamic Path -->
<polyline id="graph-path" fill="none" stroke="var(--secondary-glow)" stroke-width="2" vector-effect="non-scaling-stroke" />
</svg>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
// Math & Geometry Constants
const R_BALL = 160; // Radius of the traction ball
const MAIN_AXIS_Y = 350; // Distance from ball center to main system axis
const BASE_ANGLE_DEG = 45; // Base contact angle from horizontal when tilt is 0
const BASE_ANGLE_RAD = BASE_ANGLE_DEG * Math.PI / 180;
const INPUT_RPM = 3000;
// SVG Elements
const ballGroup = document.getElementById('traction-ball-group');
const contactIn = document.getElementById('contact-in');
const contactOut = document.getElementById('contact-out');
const radInLine = document.getElementById('rad-in-line');
const radOutLine = document.getElementById('rad-out-line');
const radInText = document.getElementById('rad-in-text');
const radOutText = document.getElementById('rad-out-text');
const inputSpin = document.getElementById('input-spin');
const outputSpin = document.getElementById('output-spin');
// UI Elements
const elTilt = document.getElementById('val-tilt');
const elRatio = document.getElementById('val-ratio');
const elOutRpm = document.getElementById('val-out-rpm');
const graphPath = document.getElementById('graph-path');
// Animation State
let startTime = null;
let inputSpinOffset = 0;
let outputSpinOffset = 0;
// Graph Data
const graphHistory = [];
const MAX_GRAPH_POINTS = 100;
function update(timestamp) {
if (!startTime) startTime = timestamp;
const elapsed = timestamp - startTime;
// 1. Calculate Tilt Angle (Oscillating between -20 and +20 degrees)
// Using a smooth sine wave for automatic continuous demonstration
const tiltDeg = 20 * Math.sin(elapsed * 0.0015);
const tiltRad = tiltDeg * Math.PI / 180;
// 2. Update Ball Tilt Visual
ballGroup.setAttribute('transform', `rotate(${tiltDeg})`);
// 3. Calculate Geometry & Contact Points
// Input is on the left. Normal angle is 180 - BASE_ANGLE. As ball tilts right (+deg), contact point moves UP (smaller angle).
const angleIn = Math.PI - BASE_ANGLE_RAD - tiltRad;
const pxIn = R_BALL * Math.cos(angleIn);
const pyIn = -R_BALL * Math.sin(angleIn); // Negative because SVG Y is down, we want Up visually
// Output is on the right. Normal angle is BASE_ANGLE. As ball tilts right (+deg), contact point moves DOWN (smaller angle).
const angleOut = BASE_ANGLE_RAD - tiltRad;
const pxOut = R_BALL * Math.cos(angleOut);
const pyOut = -R_BALL * Math.sin(angleOut);
// 4. Calculate Radii relative to main axis (MAIN_AXIS_Y is positive down)
const radiusIn = MAIN_AXIS_Y - pyIn;
const radiusOut = MAIN_AXIS_Y - pyOut;
// Ratio = R_in / R_out (Speed of output = Input * R_in / R_out)
const ratio = radiusIn / radiusOut;
const outputRPM = INPUT_RPM * ratio;
// 5. Update SVG Positions
// Contact point glowing ellipses (rotate them to align with tangent)
contactIn.setAttribute('transform', `translate(${pxIn}, ${pyIn}) rotate(${ (angleIn * 180/Math.PI) + 90 })`);
contactOut.setAttribute('transform', `translate(${pxOut}, ${pyOut}) rotate(${ (angleOut * 180/Math.PI) + 90 })`);
// Radius lines (from main axis up to contact point)
radInLine.setAttribute('x1', pxIn);
radInLine.setAttribute('y1', MAIN_AXIS_Y);
radInLine.setAttribute('x2', pxIn);
radInLine.setAttribute('y2', pyIn);
radOutLine.setAttribute('x1', pxOut);
radOutLine.setAttribute('y1', MAIN_AXIS_Y);
radOutLine.setAttribute('x2', pxOut);
radOutLine.setAttribute('y2', pyOut);
// Text positions
radInText.setAttribute('x', pxIn);
radInText.setAttribute('y', MAIN_AXIS_Y - radiusIn/2);
radOutText.setAttribute('x', pxOut);
radOutText.setAttribute('y', MAIN_AXIS_Y - radiusOut/2);
// 6. Animate Rotation Spinners
// Speed multiplier for visual effect
const baseSpinSpeed = 2;
inputSpinOffset -= baseSpinSpeed;
outputSpinOffset -= baseSpinSpeed * ratio;
inputSpin.setAttribute('stroke-dashoffset', inputSpinOffset);
outputSpin.setAttribute('stroke-dashoffset', outputSpinOffset);
// 7. Update UI Text
elTilt.textContent = (tiltDeg > 0 ? '+' : '') + tiltDeg.toFixed(2) + '°';
elRatio.textContent = ratio.toFixed(3);
elOutRpm.textContent = Math.round(outputRPM) + ' RPM';
// 8. Update Graph
graphHistory.push(ratio);
if (graphHistory.length > MAX_GRAPH_POINTS) {
graphHistory.shift();
}
// Build SVG polyline points for graph
// Map ratio [0.5, 2.0] to Y [100%, 0%]
const graphWidth = 350;
const graphHeight = 90; // Fit within 120px panel
let pointsString = "";
graphHistory.forEach((val, index) => {
const x = (index / (MAX_GRAPH_POINTS - 1)) * graphWidth;
// Map ratio range (approx 0.6 to 1.6 based on geometry) to graph height
// Let's strictly map 0.5 to bottom (graphHeight), 2.0 to top (0), 1.0 to center
let y = graphHeight - ((val - 0.5) / 1.5) * graphHeight;
pointsString += `${x},${y} `;
});
graphPath.setAttribute('points', pointsString.trim());
// Loop
requestAnimationFrame(update);
}
// Start animation
requestAnimationFrame(update);
});
</script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
