<!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>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=IBM+Plex+Mono:wght@300;400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: { display: ['Syne','sans-serif'], mono: ['IBM Plex Mono','monospace'] }
}
}
}
</script>
<style>
:root {
--bg:#070b12;--surface:#0d1219;--card:#111827;--border:#1c2536;
--fg:#e2e8f0;--muted:#64748b;--accent:#00e5a0;--accent2:#00ffc8;
--metal:#3d4a5c;--metal-light:#6b7d94;--danger:#ff4757;--force:#ff6b35;
}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'IBM Plex Mono',monospace;background:var(--bg);color:var(--fg);min-height:100vh;overflow-x:hidden}
.font-display{font-family:'Syne',sans-serif}
body::before{content:'';position:fixed;inset:0;background:radial-gradient(ellipse at 25% 40%,rgba(0,229,160,.04),transparent 65%),radial-gradient(ellipse at 75% 60%,rgba(0,255,200,.02),transparent 55%);pointer-events:none;z-index:0}
.card{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:20px}
.glow-text{color:var(--accent2);text-shadow:0 0 20px rgba(0,255,200,.3)}
.slider-track{-webkit-appearance:none;appearance:none;width:100%;height:6px;border-radius:3px;background:var(--border);outline:none}
.slider-track::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:18px;height:18px;border-radius:50%;background:var(--accent);cursor:pointer;box-shadow:0 0 10px rgba(0,229,160,.5)}
.slider-track::-moz-range-thumb{width:18px;height:18px;border-radius:50%;background:var(--accent);cursor:pointer;border:none}
.btn{padding:8px 18px;border-radius:8px;border:1px solid var(--border);background:var(--surface);color:var(--fg);cursor:pointer;font-family:inherit;font-size:13px;transition:all .2s}
.btn:hover{border-color:var(--accent);color:var(--accent);box-shadow:0 0 12px rgba(0,229,160,.15)}
.btn.active{background:rgba(0,229,160,.12);border-color:var(--accent);color:var(--accent)}
.seq-step{display:flex;align-items:center;gap:6px;padding:4px 10px;border-radius:6px;font-size:11px;background:rgba(255,255,255,.03);border:1px solid transparent;transition:all .4s}
.seq-step.lit{border-color:var(--accent);background:rgba(0,229,160,.1);color:var(--accent2)}
.seq-dot{width:6px;height:6px;border-radius:50%;background:var(--muted);transition:all .4s}
.seq-step.lit .seq-dot{background:var(--accent);box-shadow:0 0 8px var(--accent)}
@keyframes pulse{0%,100%{opacity:.6}50%{opacity:1}}
.pulse-anim{animation:pulse 2s ease-in-out infinite}
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
</style>
</head>
<body>
<!-- 背景网格 -->
<svg style="position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;opacity:.03">
<defs><pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#fff" stroke-width=".5"/>
</pattern></defs>
<rect width="100%" height="100%" fill="url(#grid)"/>
</svg>
<div class="relative z-10 max-w-[1440px] mx-auto px-4 py-6">
<!-- 头部 -->
<header class="mb-6">
<div class="flex items-end gap-4 flex-wrap">
<h1 class="font-display font-extrabold text-3xl md:text-4xl tracking-tight" style="color:var(--fg)">
行星滚柱丝杠
</h1>
<span class="font-display font-semibold text-sm md:text-base tracking-wide" style="color:var(--accent)">
PLANETARY ROLLER SCREW
</span>
</div>
<p class="mt-2 text-sm" style="color:var(--muted)">
高推力直线执行机构 · 最终理想解(IFR)原理演示 — 线接触破除"大推力 vs 小体积"物理矛盾
</p>
</header>
<!-- 主内容网格 -->
<div class="grid gap-4" style="grid-template-columns:1fr 300px;grid-template-rows:auto auto">
<!-- 截面动画 (Hero) -->
<div class="card row-span-1" style="min-height:480px">
<div class="flex items-center gap-2 mb-3">
<span class="inline-block w-2 h-2 rounded-full" style="background:var(--accent)"></span>
<span class="text-xs font-semibold tracking-wider" style="color:var(--accent)">端面截面 · 实时运转</span>
</div>
<div class="flex justify-center">
<svg id="crossSVG" viewBox="0 0 500 500" style="width:100%;max-width:480px;height:auto"></svg>
</div>
</div>
<!-- 参数面板 -->
<div class="card flex flex-col gap-4">
<div>
<div class="text-xs mb-2" style="color:var(--muted)">核心参数</div>
<div class="space-y-2">
<div class="flex justify-between text-sm"><span>丝杠导程</span><span class="glow-text font-semibold">5 mm</span></div>
<div class="flex justify-between text-sm"><span>滚柱接触角</span><span class="glow-text font-semibold">45°</span></div>
<div class="flex justify-between text-sm"><span>滚柱数量</span><span class="glow-text font-semibold">8</span></div>
<div class="flex justify-between text-sm"><span>接触类型</span><span class="glow-text font-semibold">线接触</span></div>
</div>
</div>
<hr style="border-color:var(--border)">
<div>
<div class="text-xs mb-2" style="color:var(--muted)">实时数据</div>
<div class="space-y-2">
<div class="flex justify-between text-sm"><span>输出推力</span><span id="dataForce" style="color:var(--force)" class="font-semibold">4000 N</span></div>
<div class="flex justify-between text-sm"><span>滚柱应力</span><span id="dataStress" class="font-semibold" style="color:var(--accent)">低</span></div>
<div class="flex justify-between text-sm"><span>螺母位移</span><span id="dataDisp" class="font-semibold">0.0 mm</span></div>
<div class="flex justify-between text-sm"><span>运行状态</span><span id="dataStatus" class="font-semibold" style="color:var(--accent)">运转中</span></div>
</div>
</div>
<hr style="border-color:var(--border)">
<div>
<div class="text-xs mb-2" style="color:var(--muted)">IFR 核心洞察</div>
<p class="text-xs leading-relaxed" style="color:var(--muted)">
传统滚珠丝杠为<span style="color:var(--danger)">点接触</span>,接触面积仅 ~0.01mm²;行星滚柱丝杠为<span style="color:var(--accent2)">线接触</span>,面积达 ~0.1mm²,<span class="font-semibold" style="color:var(--accent)">提升 10 倍以上</span>。同等推力下,接触应力降低一个数量级,在极小体积内即可承载 4000N+。
</p>
</div>
<div>
<div class="text-xs mb-2" style="color:var(--muted)">适用边界</div>
<p class="text-xs leading-relaxed" style="color:var(--muted)">
行程 ≤ 500mm(避免细长丝杠临界转速共振);安装平行度要求 ≤ 0.02mm/m。
</p>
</div>
</div>
<!-- 侧视剖面 -->
<div class="card">
<div class="flex items-center gap-2 mb-3">
<span class="inline-block w-2 h-2 rounded-full" style="background:var(--force)"></span>
<span class="text-xs font-semibold tracking-wider" style="color:var(--force)">纵剖示意 · 运动转换</span>
</div>
<div class="flex justify-center">
<svg id="sideSVG" viewBox="0 0 680 200" style="width:100%;max-width:660px;height:auto"></svg>
</div>
<!-- 动作时序 -->
<div class="mt-4 flex flex-wrap gap-2" id="seqBar">
<div class="seq-step" data-step="0"><span class="seq-dot"></span>接收指令</div>
<div class="seq-step" data-step="1"><span class="seq-dot"></span>输出扭矩</div>
<div class="seq-step" data-step="2"><span class="seq-dot"></span>丝杠旋转</div>
<div class="seq-step" data-step="3"><span class="seq-dot"></span>螺母推出</div>
<div class="seq-step" data-step="4"><span class="seq-dot"></span>力传感反馈</div>
<div class="seq-step" data-step="5"><span class="seq-dot"></span>保压/换向</div>
</div>
</div>
<!-- 接触应力对比 -->
<div class="card">
<div class="text-xs mb-3 font-semibold tracking-wider" style="color:var(--muted)">接触应力对比</div>
<svg id="compareSVG" viewBox="0 0 260 220" style="width:100%;max-width:260px;height:auto;margin:0 auto;display:block"></svg>
</div>
</div>
<!-- 控制栏 -->
<div class="card mt-4">
<div class="flex flex-wrap items-center gap-6">
<div class="flex-1 min-w-[200px]">
<label class="text-xs block mb-1" style="color:var(--muted)">推力 <span id="forceLabel" class="font-semibold" style="color:var(--force)">4000 N</span></label>
<input type="range" id="forceSlider" min="0" max="6000" value="4000" step="100" class="slider-track w-full">
</div>
<div class="flex-1 min-w-[160px]">
<label class="text-xs block mb-1" style="color:var(--muted)">动画速度 <span id="speedLabel" class="font-semibold" style="color:var(--accent)">1.0x</span></label>
<input type="range" id="speedSlider" min="0" max="30" value="10" step="1" class="slider-track w-full">
</div>
<div class="flex gap-2">
<button id="playBtn" class="btn active" aria-label="播放/暂停"><i class="fas fa-pause"></i></button>
<button id="dirBtn" class="btn" aria-label="换向"><i class="fas fa-exchange-alt"></i></button>
</div>
</div>
</div>
</div>
<script>
/* ===== 全局状态 ===== */
const S = {
time: 0, screwAngle: 0, playing: true, speed: 1.0,
force: 4000, dir: 1, nutPos: 0.5
};
/* ===== 常量 ===== */
const CX = 250, CY = 250; // 截面中心
const NUT_OR = 215, NUT_IR = 192; // 螺母外径/内径
const ORBIT_R = 152; // 滚柱轨道半径
const ROLLER_R = 33; // 滚柱半径
const SCREW_OR = 115, SCREW_IR = 75; // 丝杠外径/内径
const N_ROLLERS = 8;
const SCREW_TEETH = 24, ROLLER_TEETH = 12, NUT_TEETH = 32;
/* ===== SVG 辅助 ===== */
const NS = 'http://www.w3.org/2000/svg';
function svgEl(tag, attrs, parent) {
const el = document.createElementNS(NS, tag);
for (const [k, v] of Object.entries(attrs || {})) el.setAttribute(k, v);
if (parent) parent.appendChild(el);
return el;
}
/* 生成齿轮路径 */
function gearPath(cx, cy, baseR, tipR, n, rot) {
const pts = [];
for (let i = 0; i < n; i++) {
const a = (i / n) * Math.PI * 2 + rot;
const hw = (0.35 / n) * Math.PI * 2;
pts.push([cx + baseR * Math.cos(a - hw * 1.1), cy + baseR * Math.sin(a - hw * 1.1)]);
pts.push([cx + tipR * Math.cos(a - hw * 0.5), cy + tipR * Math.sin(a - hw * 0.5)]);
pts.push([cx + tipR * Math.cos(a + hw * 0.5), cy + tipR * Math.sin(a + hw * 0.5)]);
pts.push([cx + baseR * Math.cos(a + hw * 1.1), cy + baseR * Math.sin(a + hw * 1.1)]);
}
return 'M ' + pts.map(p => p[0].toFixed(2) + ' ' + p[1].toFixed(2)).join(' L ') + ' Z';
}
/* ===== 截面视图构建 ===== */
const crossEls = {};
function buildCrossSection() {
const svg = document.getElementById('crossSVG');
svg.innerHTML = '';
// 定义滤镜
const defs = svgEl('defs', {}, svg);
// 发光滤镜
const fGlow = svgEl('filter', { id: 'glow', x: '-50%', y: '-50%', width: '200%', height: '200%' }, defs);
svgEl('feGaussianBlur', { in: 'SourceGraphic', stdDeviation: '4', result: 'blur' }, fGlow);
const fMerge = svgEl('feMerge', {}, fGlow);
svgEl('feMergeNode', { in: 'blur' }, fMerge);
svgEl('feMergeNode', { in: 'SourceGraphic' }, fMerge);
// 强发光
const fGlow2 = svgEl('filter', { id: 'glowStrong', x: '-50%', y: '-50%', width: '200%', height: '200%' }, defs);
svgEl('feGaussianBlur', { in: 'SourceGraphic', stdDeviation: '8', result: 'blur' }, fGlow2);
const fMerge2 = svgEl('feMerge', {}, fGlow2);
svgEl('feMergeNode', { in: 'blur' }, fMerge2);
svgEl('feMergeNode', { in: 'SourceGraphic' }, fMerge2);
// 中心十字参考线
svgEl('line', { x1: CX - 230, y1: CY, x2: CX + 230, y2: CY, stroke: '#1c2536', 'stroke-width': 0.5, 'stroke-dasharray': '4,6' }, svg);
svgEl('line', { x1: CX, y1: CY - 230, x2: CX, y2: CY + 230, stroke: '#1c2536', 'stroke-width': 0.5, 'stroke-dasharray': '4,6' }, svg);
// 螺母外壳(静态)
svgEl('circle', { cx: CX, cy: CY, r: NUT_OR, fill: 'none', stroke: '#2a3444', 'stroke-width': 22 }, svg);
// 螺母内齿
crossEls.nutTeeth = svgEl('path', {
d: gearPath(CX, CY, NUT_IR, NUT_IR + 10, NUT_TEETH, 0),
fill: '#2a3444', stroke: '#3d4a5c', 'stroke-width': 0.5
}, svg);
// 保持架(动态)
crossEls.cage = svgEl('circle', {
cx: CX, cy: CY, r: ORBIT_R, fill: 'none', stroke: '#4a5568',
'stroke-width': 1.5, 'stroke-dasharray': '8,12', opacity: 0.5
}, svg);
// 接触高亮组(在滚柱下面绘制,产生光晕效果)
crossEls.contactGroup = svgEl('g', {}, svg);
// 丝杠组(动态旋转)
crossEls.screwGroup = svgEl('g', {}, svg);
// 丝杠齿
crossEls.screwTeeth = svgEl('path', {
d: gearPath(CX, CY, SCREW_IR, SCREW_OR, SCREW_TEETH, 0),
fill: '#2d3748', stroke: '#4a5568', 'stroke-width': 0.5
}, crossEls.screwGroup);
// 丝杠轴心
svgEl('circle', { cx: CX, cy: CY, r: SCREW_IR - 5, fill: '#1a202c', stroke: '#2d3748', 'stroke-width': 1 }, crossEls.screwGroup);
// 丝杠中心孔
svgEl('circle', { cx: CX, cy: CY, r: 25, fill: '#0d1219', stroke: '#2d3748', 'stroke-width': 1 }, crossEls.screwGroup);
// 键槽标记
svgEl('rect', { x: CX - 4, y: CY - 25, width: 8, height: 12, fill: '#2d3748', rx: 1 }, crossEls.screwGroup);
// 滚柱轨道组(公转)
crossEls.rollerOrbitGroup = svgEl('g', {}, svg);
crossEls.rollers = [];
crossEls.rollerTeeth = [];
for (let i = 0; i < N_ROLLERS; i++) {
const angle = (i / N_ROLLERS) * Math.PI * 2;
const rx = CX + ORBIT_R * Math.cos(angle);
const ry = CY + ORBIT_R * Math.sin(angle);
const rg = svgEl('g', {}, crossEls.rollerOrbitGroup);
// 滚柱齿(外)
const outerTeeth = svgEl('path', {
d: gearPath(rx, ry, ROLLER_R - 6, ROLLER_R, ROLLER_TEETH, 0),
fill: '#1a3a3a', stroke: '#00e5a0', 'stroke-width': 0.5
}, rg);
// 滚柱体
svgEl('circle', { cx: rx, cy: ry, r: ROLLER_R - 8, fill: '#0f2828', stroke: '#1a4a4a', 'stroke-width': 0.5 }, rg);
// 滚柱中心
svgEl('circle', { cx: rx, cy: ry, r: 4, fill: '#00e5a0', opacity: 0.3 }, rg);
crossEls.rollers.push({ group: rg, outerTeeth, cx: rx, cy: ry, baseAngle: angle });
}
// 标注
svgEl('text', { x: CX, y: CY + NUT_OR + 28, 'text-anchor': 'middle', fill: '#64748b', 'font-size': 11, 'font-family': 'IBM Plex Mono' }, svg).textContent = '螺母 (Nut)';
svgEl('text', { x: CX, y: CY + 6, 'text-anchor': 'middle', fill: '#64748b', 'font-size': 10, 'font-family': 'IBM Plex Mono' }, svg).textContent = '丝杠';
// 接触角标注弧线
crossEls.angleArc = svgEl('path', {
d: '', fill: 'none', stroke: '#fbbf24', 'stroke-width': 1, 'stroke-dasharray': '3,3', opacity: 0
}, svg);
crossEls.angleLabel = svgEl('text', {
x: 0, y: 0, fill: '#fbbf24', 'font-size': 10, 'font-family': 'IBM Plex Mono', opacity: 0
}, svg);
}
/* ===== 侧视图构建 ===== */
const sideEls = {};
function buildSideView() {
const svg = document.getElementById('sideSVG');
svg.innerHTML = '';
const yMid = 95;
// 电机
svgEl('rect', { x: 8, y: yMid - 38, width: 72, height: 76, rx: 6, fill: '#1a202c', stroke: '#3d4a5c', 'stroke-width': 1.5 }, svg);
svgEl('text', { x: 44, y: yMid + 4, 'text-anchor': 'middle', fill: '#64748b', 'font-size': 13, 'font-weight': 600, 'font-family': 'Syne' }, svg).textContent = 'M';
// 电机轴
svgEl('rect', { x: 80, y: yMid - 6, width: 30, height: 12, rx: 2, fill: '#4a5568' }, svg);
// 联轴器
svgEl('rect', { x: 108, y: yMid - 12, width: 10, height: 24, rx: 3, fill: '#2d3748', stroke: '#00e5a0', 'stroke-width': 1 }, svg);
svgEl('rect', { x: 120, y: yMid - 12, width: 10, height: 24, rx: 3, fill: '#2d3748', stroke: '#00e5a0', 'stroke-width': 1 }, svg);
// 弹性元件
svgEl('line', { x1: 118, y1: yMid - 8, x2: 120, y2: yMid + 8, stroke: '#00e5a0', 'stroke-width': 1.5 }, svg);
// 丝杠轴
sideEls.screwShaft = svgEl('g', {}, svg);
svgEl('rect', { x: 130, y: yMid - 10, width: 420, height: 20, rx: 3, fill: '#2d3748', stroke: '#4a5568', 'stroke-width': 1 }, sideEls.screwShaft);
// 丝杠螺纹线(动态)
sideEls.screwThreads = svgEl('g', {}, sideEls.screwShaft);
for (let i = 0; i < 28; i++) {
svgEl('line', {
x1: 138 + i * 15, y1: yMid - 10,
x2: 145 + i * 15, y2: yMid + 10,
stroke: '#5a6a80', 'stroke-width': 1, opacity: 0.6
}, sideEls.screwThreads);
}
// 螺母组(可滑动)
sideEls.nutGroup = svgEl('g', {}, svg);
// 螺母外壳
svgEl('rect', { x: 0, y: yMid - 35, width: 90, height: 70, rx: 4, fill: '#1a2838', stroke: '#3d5a6c', 'stroke-width': 1.5 }, sideEls.nutGroup);
// 螺母内腔
svgEl('rect', { x: 4, y: yMid - 12, width: 82, height: 24, rx: 2, fill: '#0d1219' }, sideEls.nutGroup);
// 滚柱(在螺母内可见)
sideEls.sideRollers = [];
for (let i = 0; i < 4; i++) {
const ry = yMid - 24 + i * 16;
if (Math.abs(ry - yMid) < 8) continue;
const rc = svgEl('circle', { cx: 45, cy: ry, r: 6, fill: '#0f2828', stroke: '#00e5a0', 'stroke-width': 1 }, sideEls.nutGroup);
sideEls.sideRollers.push(rc);
}
// 直线导轨
svgEl('rect', { x: 130, y: yMid + 40, width: 420, height: 6, rx: 2, fill: '#1a202c', stroke: '#2d3748', 'stroke-width': 1 }, svg);
svgEl('rect', { x: 130, y: yMid + 52, width: 420, height: 6, rx: 2, fill: '#1a202c', stroke: '#2d3748', 'stroke-width': 1 }, svg);
// 导轨滑块
sideEls.sliderBlock = svgEl('g', {}, svg);
svgEl('rect', { x: 0, y: yMid + 36, width: 50, height: 28, rx: 3, fill: '#2d3748', stroke: '#4a5568', 'stroke-width': 1 }, sideEls.sliderBlock);
svgEl('rect', { x: 0, y: yMid + 48, width: 50, height: 16, rx: 2, fill: '#2d3748', stroke: '#4a5568', 'stroke-width': 1 }, sideEls.sliderBlock);
// 推力箭头
sideEls.forceArrow = svgEl('g', {}, svg);
svgEl('line', { x1: 0, y1: yMid, x2: 70, y2: yMid, stroke: '#ff6b35', 'stroke-width': 3 }, sideEls.forceArrow);
svgEl('polygon', { points: '70,-8 86,0 70,8', fill: '#ff6b35', transform: `translate(0,${yMid})` }, sideEls.forceArrow);
sideEls.forceLabel = svgEl('text', {
x: 80, y: yMid - 14, fill: '#ff6b35', 'font-size': 12, 'font-weight': 600, 'font-family': 'IBM Plex Mono'
}, sideEls.forceArrow);
sideEls.forceLabel.textContent = '4000N';
// 缸体轮廓
svgEl('rect', { x: 125, y: yMid - 48, width: 430, height: 108, rx: 6, fill: 'none', stroke: '#1c2536', 'stroke-width': 1, 'stroke-dasharray': '6,4' }, svg);
svgEl('text', { x: 340, y: yMid + 70, 'text-anchor': 'middle', fill: '#3d4a5c', 'font-size': 10, 'font-family': 'IBM Plex Mono' }, svg).textContent = '缸体';
// 力传感器标记
sideEls.sensor = svgEl('g', {}, svg);
svgEl('rect', { x: 0, y: yMid - 42, width: 24, height: 12, rx: 2, fill: '#1a202c', stroke: '#fbbf24', 'stroke-width': 1 }, sideEls.sensor);
svgEl('text', { x: 12, y: yMid - 34, 'text-anchor': 'middle', fill: '#fbbf24', 'font-size': 7, 'font-family': 'IBM Plex Mono' }, sideEls.sensor).textContent = 'F';
}
/* ===== 接触对比构建 ===== */
function buildCompare() {
const svg = document.getElementById('compareSVG');
svg.innerHTML = '';
// 左侧:滚珠丝杠
svgEl('text', { x: 65, y: 18, 'text-anchor': 'middle', fill: '#ff4757', 'font-size': 11, 'font-weight': 600, 'font-family': 'Syne' }, svg).textContent = '滚珠丝杠';
// 滚道
svgEl('path', { d: 'M 20,60 Q 65,35 110,60', fill: 'none', stroke: '#3d4a5c', 'stroke-width': 3 }, svg);
svgEl('path', { d: 'M 20,100 Q 65,125 110,100', fill: 'none', stroke: '#3d4a5c', 'stroke-width': 3 }, svg);
// 滚珠
svgEl('circle', { cx: 65, cy: 80, r: 14, fill: '#2d3748', stroke: '#5a6a80', 'stroke-width': 1.5 }, svg);
// 点接触
compareEls.ballContactTop = svgEl('circle', { cx: 65, cy: 67, r: 3, fill: '#ff4757', filter: 'url(#glow)' }, svg);
compareEls.ballContactBot = svgEl('circle', { cx: 65, cy: 93, r: 3, fill: '#ff4757', filter: 'url(#glow)' }, svg);
// 标注
svgEl('text', { x: 65, y: 145, 'text-anchor': 'middle', fill: '#ff4757', 'font-size': 10, 'font-family': 'IBM Plex Mono' }, svg).textContent = '点接触';
svgEl('text', { x: 65, y: 160, 'text-anchor': 'middle', fill: '#64748b', 'font-size': 9, 'font-family': 'IBM Plex Mono' }, svg).textContent = '~0.01mm²';
// 应力条
svgEl('rect', { x: 20, y: 172, width: 90, height: 8, rx: 3, fill: '#1a202c' }, svg);
compareEls.ballStressBar = svgEl('rect', { x: 20, y: 172, width: 72, height: 8, rx: 3, fill: '#ff4757' }, svg);
compareEls.ballStressLabel = svgEl('text', { x: 65, y: 196, 'text-anchor': 'middle', fill: '#ff4757', 'font-size': 9, 'font-weight': 600, 'font-family': 'IBM Plex Mono' }, svg);
// 右侧:滚柱丝杠
svgEl('text', { x: 195, y: 18, 'text-anchor': 'middle', fill: '#00e5a0', 'font-size': 11, 'font-weight': 600, 'font-family': 'Syne' }, svg).textContent = '滚柱丝杠';
// 滚道
svgEl('path', { d: 'M 150,60 Q 195,35 240,60', fill: 'none', stroke: '#3d4a5c', 'stroke-width': 3 }, svg);
svgEl('path', { d: 'M 150,100 Q 195,125 240,100', fill: 'none', stroke: '#3d4a5c', 'stroke-width': 3 }, svg);
// 滚柱
svgEl('rect', { x: 183, y: 63, width: 24, height: 34, rx: 4, fill: '#0f2828', stroke: '#00e5a0', 'stroke-width': 1.5 }, svg);
// 线接触
compareEls.rollerContactTop = svgEl('line', { x1: 187, y1: 66, x2: 203, y2: 66, stroke: '#00ffc8', 'stroke-width': 3, 'stroke-linecap': 'round', filter: 'url(#glow)' }, svg);
compareEls.rollerContactBot = svgEl('line', { x1: 187, y1: 94, x2: 203, y2: 94, stroke: '#00ffc8', 'stroke-width': 3, 'stroke-linecap': 'round', filter: 'url(#glow)' }, svg);
// 标注
svgEl('text', { x: 195, y: 145, 'text-anchor': 'middle', fill: '#00e5a0', 'font-size': 10, 'font-family': 'IBM Plex Mono' }, svg).textContent = '线接触';
svgEl('text', { x: 195, y: 160, 'text-anchor': 'middle', fill: '#64748b', 'font-size': 9, 'font-family': 'IBM Plex Mono' }, svg).textContent = '~0.1mm²';
// 应力条
svgEl('rect', { x: 150, y: 172, width: 90, height: 8, rx: 3, fill: '#1a202c' }, svg);
compareEls.rollerStressBar = svgEl('rect', { x: 150, y: 172, width: 15, height: 8, rx: 3, fill: '#00e5a0' }, svg);
compareEls.rollerStressLabel = svgEl('text', { x: 195, y: 196, 'text-anchor': 'middle', fill: '#00e5a0', 'font-size': 9, 'font-weight': 600, 'font-family': 'IBM Plex Mono' }, svg);
// 对比箭头
svgEl('text', { x: 130, y: 84, 'text-anchor': 'middle', fill: '#fbbf24', 'font-size': 14, 'font-weight': 800, 'font-family': 'Syne' }, svg).textContent = 'VS';
}
const compareEls = {};
/* ===== 更新函数 ===== */
function updateCrossSection() {
const sa = S.screwAngle * Math.PI / 180;
const orbitA = S.screwAngle * 0.35 * Math.PI / 180;
// 丝杠旋转
crossEls.screwTeeth.setAttribute('d', gearPath(CX, CY, SCREW_IR, SCREW_OR, SCREW_TEETH, sa));
crossEls.screwGroup.setAttribute('transform', `rotate(${S.screwAngle * 0.2}, ${CX}, ${CY})`);
// 保持架旋转
crossEls.cage.setAttribute('transform', `rotate(${S.screwAngle * 0.35}, ${CX}, ${CY})`);
// 螺母内齿微调(螺母不旋转,但齿形随丝杠匹配)
crossEls.nutTeeth.setAttribute('d', gearPath(CX, CY, NUT_IR, NUT_IR + 10, NUT_TEETH, 0));
// 滚柱公转 + 自转
crossEls.contactGroup.innerHTML = '';
const selfRot = -S.screwAngle * 1.8;
for (let i = 0; i < N_ROLLERS; i++) {
const baseA = (i / N_ROLLERS) * Math.PI * 2;
const currentA = baseA + orbitA;
const rx = CX + ORBIT_R * Math.cos(currentA);
const ry = CY + ORBIT_R * Math.sin(currentA);
const roller = crossEls.rollers[i];
roller.group.setAttribute('transform', `translate(${rx - roller.cx}, ${ry - roller.cy})`);
roller.outerTeeth.setAttribute('d', gearPath(rx, ry, ROLLER_R - 6, ROLLER_R, ROLLER_TEETH, selfRot * Math.PI / 180));
// 接触高亮:滚柱-丝杠
const contactAngleS = currentA;
const csX = CX + (SCREW_OR + 2) * Math.cos(contactAngleS);
const csY = CY + (SCREW_OR + 2) * Math.sin(contactAngleS);
const perpAngle = contactAngleS + Math.PI / 2;
const lineLen = 14;
const glowIntensity = Math.min(1, S.force / 5000);
svgEl('line', {
x1: csX - lineLen * Math.cos(perpAngle), y1: csY - lineLen * Math.sin(perpAngle),
x2: csX + lineLen * Math.cos(perpAngle), y2: csY + lineLen * Math.sin(perpAngle),
stroke: '#00ffc8', 'stroke-width': 2.5 + glowIntensity * 2,
'stroke-linecap': 'round', opacity: 0.4 + glowIntensity * 0.6,
filter: 'url(#glow)'
}, crossEls.contactGroup);
// 接触高亮:滚柱-螺母
const cnX = CX + (NUT_IR - 2) * Math.cos(contactAngleS);
const cnY = CY + (NUT_IR - 2) * Math.sin(contactAngleS);
svgEl('line', {
x1: cnX - lineLen * Math.cos(perpAngle), y1: cnY - lineLen * Math.sin(perpAngle),
x2: cnX + lineLen * Math.cos(perpAngle), y2: cnY + lineLen * Math.sin(perpAngle),
stroke: '#00ffc8', 'stroke-width': 2.5 + glowIntensity * 2,
'stroke-linecap': 'round', opacity: 0.4 + glowIntensity * 0.6,
filter: 'url(#glow)'
}, crossEls.contactGroup);
}
// 接触角标注(在第一个滚柱处)
if (S.force > 500) {
const a0 = orbitA;
const labelR = ORBIT_R + ROLLER_R + 22;
const lx = CX + labelR * Math.cos(a0);
const ly = CY + labelR * Math.sin(a0);
crossEls.angleLabel.setAttribute('x', lx);
crossEls.angleLabel.setAttribute('y', ly);
crossEls.angleLabel.setAttribute('opacity', 0.8);
crossEls.angleLabel.textContent = '45°';
crossEls.angleArc.setAttribute('opacity', 0.5);
} else {
crossEls.angleLabel.setAttribute('opacity', 0);
crossEls.angleArc.setAttribute('opacity', 0);
}
}
function updateSideView() {
const nutTravel = 140;
const baseX = 240;
const nutX = baseX + (S.nutPos - 0.5) * nutTravel * S.dir;
const sliderX = nutX - 10;
const arrowX = nutX + 90;
sideEls.nutGroup.setAttribute('transform', `translate(${nutX - baseX}, 0)`);
sideEls.sliderBlock.setAttribute('transform', `translate(${sliderX - baseX + 20}, 0)`);
sideEls.forceArrow.setAttribute('transform', `translate(${arrowX - baseX - 90}, 0)`);
sideEls.sensor.setAttribute('transform', `translate(${nutX - baseX - 12}, 0)`);
// 螺纹线动画
const threadOffset = (S.screwAngle * 0.5) % 15;
sideEls.screwThreads.setAttribute('transform', `translate(${threadOffset}, 0)`);
// 力箭头标签
sideEls.forceLabel.textContent = S.force + 'N';
// 力箭头大小随推力变化
const arrowScale = 0.5 + (S.force / 6000) * 0.8;
sideEls.forceArrow.setAttribute('opacity', Math.min(1, S.force / 500));
// 力传感器闪烁
const sensorPulse = Math.sin(S.time * 4) * 0.3 + 0.7;
sideEls.sensor.setAttribute('opacity', S.force > 200 ? sensorPulse : 0.3);
}
function updateCompare() {
const forceRatio = S.force / 6000;
// 滚珠丝杠应力条
const ballStressW = Math.min(90, 10 + forceRatio * 85);
compareEls.ballStressBar.setAttribute('width', ballStressW);
const ballColor = forceRatio < 0.4 ? '#fbbf24' : forceRatio < 0.7 ? '#ff8c00' : '#ff4757';
compareEls.ballStressBar.setAttribute('fill', ballColor);
compareEls.ballStressLabel.textContent = forceRatio > 0.65 ? '高应力 ⚠' : '中应力';
// 滚柱丝杠应力条
const rollerStressW = Math.min(90, 3 + forceRatio * 15);
compareEls.rollerStressBar.setAttribute('width', rollerStressW);
compareEls.rollerStressLabel.textContent = forceRatio > 0.8 ? '低应力 ✓' : '极低 ✓';
// 接触点发光强度
const glowR = 2 + forceRatio * 4;
compareEls.ballContactTop.setAttribute('r', glowR);
compareEls.ballContactBot.setAttribute('r', glowR);
compareEls.rollerContactTop.setAttribute('stroke-width', 2 + forceRatio * 3);
compareEls.rollerContactBot.setAttribute('stroke-width', 2 + forceRatio * 3);
}
function updateDataPanel() {
document.getElementById('dataForce').textContent = S.force + ' N';
document.getElementById('dataForce').style.color = S.force > 4500 ? '#ff4757' : S.force > 3000 ? '#ff6b35' : '#fbbf24';
const stressLevel = S.force > 4500 ? '中' : '低';
document.getElementById('dataStress').textContent = stressLevel;
document.getElementById('dataStress').style.color = S.force > 4500 ? '#fbbf24' : '#00e5a0';
const disp = (S.nutPos * 50).toFixed(1);
document.getElementById('dataDisp').textContent = disp + ' mm';
document.getElementById('dataStatus').textContent = S.playing ? '运转中' : '已暂停';
document.getElementById('dataStatus').style.color = S.playing ? '#00e5a0' : '#64748b';
}
function updateSequence() {
const cyclePos = (S.time * 0.3) % 6;
const steps = document.querySelectorAll('#seqBar .seq-step');
steps.forEach((step, i) => {
const dist = Math.abs(cyclePos - i);
step.classList.toggle('lit', dist < 0.8 || dist > 5.2);
});
}
/* ===== 动画主循环 ===== */
let lastTime = 0;
function animate(timestamp) {
const dt = Math.min((timestamp - lastTime) / 1000, 0.05);
lastTime = timestamp;
if (S.playing) {
S.time += dt * S.speed;
S.screwAngle += 90 * S.speed * dt * S.dir;
S.nutPos = 0.5 + 0.5 * Math.sin(S.time * 0.8);
}
updateCrossSection();
updateSideView();
updateCompare();
updateDataPanel();
updateSequence();
requestAnimationFrame(animate);
}
/* ===== 控件绑定 ===== */
function setupControls() {
const forceSlider = document.getElementById('forceSlider');
const speedSlider = document.getElementById('speedSlider');
const playBtn = document.getElementById('playBtn');
const dirBtn = document.getElementById('dirBtn');
forceSlider.addEventListener('input', () => {
S.force = parseInt(forceSlider.value);
document.getElementById('forceLabel').textContent = S.force + ' N';
});
speedSlider.addEventListener('input', () => {
S.speed = parseFloat(speedSlider.value) / 10;
document.getElementById('speedLabel').textContent = S.speed.toFixed(1) + 'x';
});
playBtn.addEventListener('click', () => {
S.playing = !S.playing;
playBtn.innerHTML = S.playing ? '<i class="fas fa-pause"></i>' : '<i class="fas fa-play"></i>';
playBtn.classList.toggle('active', S.playing);
});
dirBtn.addEventListener('click', () => {
S.dir *= -1;
dirBtn.classList.toggle('active');
});
}
/* ===== 初始化 ===== */
function init() {
buildCrossSection();
buildSideView();
buildCompare();
setupControls();
requestAnimationFrame(animate);
}
init();
</script>
</body>
</html>
实现要点说明:
核心视觉——端面截面动画
- 中央丝杠带齿轮状齿形(24齿),随时间旋转;8颗滚柱以行星方式公转并自转,齿形实时重绘以表现旋向;螺母外圈及内齿固定不动。
- 每颗滚柱与丝杠、螺母的接触位置绘制 发光线段(
#00ffc8+ SVGfeGaussianBlur滤镜),线段宽度与亮度随推力滑块动态变化——推力越大,接触线越亮越粗,直观传达"线接触大面积承载"的 IFR 核心思想。 - 第一个滚柱旁标注 45° 接触角,推力 >500N 时淡入。
侧视剖面示意
- 从左至右:伺服电机 → 弹性联轴器 → 丝杠轴(螺纹线随旋转平移)→ 螺母(含可见滚柱)→ 推力箭头(大小/颜色随力值变化)→ 力传感器标记(脉冲闪烁表示闭环反馈)。
- 螺母、导轨滑块、力箭头均随正弦周期左右滑动,展示旋转→直线运动转换。
- 底部6步动作时序条按循环依次点亮。
接触应力对比面板
- 左侧滚珠丝杠:上下两个红色发光点,应力条随推力增长迅速变红变长,超 4000N 时标注"高应力 ⚠"。
- 右侧行星滚柱丝杠:上下两条青绿发光线,应力条始终极短极绿,标注"极低 ✓"——直观呈现 10 倍接触面积差异。
交互控制
- 推力滑块 0–6000N:实时影响接触高亮强度、应力条长度/颜色、力箭头标注。
- 速度滑块 0–3x:控制旋转与直线运动速率。
- 播放/暂停、换向按钮:可随时冻结观察或反转运动方向。
积分规则:第一轮对话扣减6分,后续每轮扣4分
等待动画代码生成...
