独立渲染引擎就绪引擎就绪
<!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: #0b0f19;
--grid-color: #1a2333;
--text-main: #e2e8f0;
--text-muted: #64748b;
--screen-cyan: #00f3ff;
--arm-orange: #f59e0b;
--truss-blue: #1e293b;
--icr-magenta: #e879f9;
--danger-red: #ef4444;
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: var(--bg-color);
font-family: 'Consolas', 'Courier New', monospace;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
color: var(--text-main);
}
/* 绝对避免遮挡中心动画的边缘文字层 */
.overlay-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10;
}
.text-block {
position: absolute;
background: rgba(11, 15, 25, 0.85);
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 12px 16px;
border-radius: 6px;
backdrop-filter: blur(4px);
}
.top-left { top: 20px; left: 20px; max-width: 300px; }
.top-right { top: 20px; right: 20px; max-width: 280px; text-align: right; }
.bottom-left { bottom: 20px; left: 20px; max-width: 250px; }
.bottom-right { bottom: 20px; right: 20px; max-width: 300px; }
h1 {
font-size: 14px;
margin: 0 0 8px 0;
color: var(--screen-cyan);
letter-spacing: 1px;
text-transform: uppercase;
}
p {
font-size: 11px;
line-height: 1.6;
margin: 0;
color: var(--text-muted);
}
.highlight { color: var(--arm-orange); font-weight: bold; }
.highlight-icr { color: var(--icr-magenta); font-weight: bold; }
.legend-item {
display: flex;
align-items: center;
margin-top: 6px;
font-size: 11px;
color: var(--text-main);
justify-content: flex-end;
}
.legend-item .color-box {
width: 12px;
height: 12px;
margin-left: 8px;
border-radius: 2px;
}
/* SVG 动画容器 */
#sim-container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 1;
}
svg {
width: 100%;
height: 100%;
max-width: 1200px;
max-height: 900px;
}
/* SVG 元素样式 */
.grid { stroke: var(--grid-color); stroke-width: 1; }
.truss { fill: var(--truss-blue); stroke: #334155; stroke-width: 2; }
.link { stroke: var(--arm-orange); stroke-linecap: round; stroke-linejoin: round; }
.link-shadow { stroke: rgba(0,0,0,0.5); stroke-linecap: round; }
.pivot { fill: #fff; stroke: var(--arm-orange); stroke-width: 3; }
.pivot-ground { fill: #1e293b; stroke: var(--screen-cyan); stroke-width: 3; }
.screen-body { fill: #0f172a; stroke: var(--screen-cyan); stroke-width: 2; }
.screen-front { fill: var(--screen-cyan); opacity: 0.1; }
.screen-led { stroke: var(--screen-cyan); stroke-width: 4; stroke-linecap: round; filter: drop-shadow(0 0 8px var(--screen-cyan)); }
.trajectory { fill: none; stroke: rgba(255, 255, 255, 0.15); stroke-width: 1.5; stroke-dasharray: 4 4; }
.trajectory-icr { fill: none; stroke: rgba(232, 121, 249, 0.3); stroke-width: 2; stroke-dasharray: 6 6; }
.icr-line { stroke: var(--icr-magenta); stroke-width: 1; stroke-dasharray: 3 3; opacity: 0.5; }
.icr-point { fill: var(--bg-color); stroke: var(--icr-magenta); stroke-width: 2; }
.icr-glow { fill: var(--icr-magenta); opacity: 0.3; filter: blur(4px); }
.force-arrow { fill: var(--danger-red); opacity: 0; transition: opacity 0.3s; }
.visible { opacity: 0.8 !important; }
</style>
</head>
<body>
<!-- 绝对边缘定位的文字说明层,保证中心画面纯净 -->
<div class="overlay-layer">
<div class="text-block top-left">
<h1>TRIZ最终理想解仿真</h1>
<p>基于“非对称重型四连杆塞拉门机构”。<br>
<span class="highlight">IFR核心:</span>不增加双电机与长导轨,利用纯连杆几何变形,在开启瞬间强制屏幕产生向后的“退让抽拉微动”,完美抵消厚度带来的前角干涉。</p>
</div>
<div class="text-block top-right">
<h1>关键参数状态</h1>
<p>短摇臂(L1): <span class="highlight">450mm</span></p>
<p>长摇臂(L2): <span class="highlight">780mm</span></p>
<p>当前曲柄角: <span id="angle-display">180</span>°</p>
<p>驱动状态: <span id="status-display" class="highlight">闭合就绪</span></p>
</div>
<div class="text-block bottom-left">
<h1>运动学拓扑 (俯视图)</h1>
<p>瞬时旋转中心 (ICR) 轨迹外凸量约 150mm。四连杆的非对称性使得 ICR 随屏幕运动发生大范围偏移,从而在闭合末端 15° 将单纯的旋转转化为<span class="highlight">斜向平移贴合</span>。</p>
</div>
<div class="text-block bottom-right">
<div class="legend-item">雷亚架/基座 <div class="color-box" style="background: var(--truss-blue); border: 1px solid #334155;"></div></div>
<div class="legend-item">高强度合金摇臂 <div class="color-box" style="background: var(--arm-orange);"></div></div>
<div class="legend-item">LED屏幕主体 <div class="color-box" style="background: var(--screen-cyan); box-shadow: 0 0 5px var(--screen-cyan);"></div></div>
<div class="legend-item">瞬心(ICR)及辅助线 <div class="color-box" style="background: var(--icr-magenta);"></div></div>
</div>
</div>
<!-- 核心 SVG 动画区 -->
<div id="sim-container">
<svg id="canvas" viewBox="-400 -300 800 600" preserveAspectRatio="xMidYMid meet">
<!-- 动态背景网格 -->
<defs>
<pattern id="gridPattern" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" class="grid"/>
</pattern>
<filter id="glow">
<feGaussianBlur stdDeviation="3" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<rect x="-1000" y="-1000" width="2000" height="2000" fill="url(#gridPattern)" />
<!-- 静态雷亚架 (Truss) -->
<g id="truss-group" transform="translate(180, 0)">
<rect x="0" y="-200" width="80" height="400" class="truss" rx="4" />
<path d="M0 -180 L80 -140 M0 -140 L80 -100 M0 -100 L80 -60 M0 -60 L80 -20 M0 -20 L80 20 M0 20 L80 60 M0 60 L80 100 M0 100 L80 140 M0 140 L80 180" stroke="#334155" stroke-width="4" fill="none"/>
<!-- 电机指示 -->
<circle cx="-45" cy="90" r="18" fill="#1e293b" stroke="#94a3b8" stroke-width="2"/>
<text x="-45" y="94" fill="#94a3b8" font-size="10" text-anchor="middle">MOTOR</text>
</g>
<!-- 轨迹记录层 (初始化时由JS绘制) -->
<g id="trajectories">
<path id="path-icr" class="trajectory-icr" d="" />
<path id="path-screen-corner" class="trajectory" d="" />
</g>
<!-- 瞬心辅助线与标记 -->
<g id="icr-group">
<line id="icr-line-1" class="icr-line" x1="0" y1="0" x2="0" y2="0" />
<line id="icr-line-2" class="icr-line" x1="0" y1="0" x2="0" y2="0" />
<circle id="icr-glow" class="icr-glow" cx="0" cy="0" r="15" />
<circle id="icr-pt" class="icr-point" cx="0" cy="0" r="4" />
<text id="icr-label" fill="var(--icr-magenta)" font-size="10" dx="10" dy="-10">ICR</text>
</g>
<!-- 机构主体 -->
<g id="mechanism">
<!-- 阴影层 -->
<line id="link1-shadow" class="link-shadow" stroke-width="12" x1="0" y1="0" x2="0" y2="0" transform="translate(4,4)" />
<line id="link2-shadow" class="link-shadow" stroke-width="14" x1="0" y1="0" x2="0" y2="0" transform="translate(4,4)" />
<!-- 连杆层 -->
<line id="link1" class="link" stroke-width="12" x1="0" y1="0" x2="0" y2="0" />
<line id="link2" class="link" stroke-width="14" x1="0" y1="0" x2="0" y2="0" />
<!-- 屏幕总成 (Coupler) -->
<g id="screen-assembly">
<!-- 屏幕支架安装板 -->
<path d="M -10 -90 L 10 -90 L 10 90 L -10 90 Z" fill="#334155" stroke="#0f172a" />
<!-- 屏幕主体 -->
<rect x="-40" y="-180" width="30" height="360" rx="4" class="screen-body" />
<!-- LED发光面 (面向左侧) -->
<line x1="-40" y1="-176" x2="-40" y2="176" class="screen-led" />
<!-- 运动示意箭头 (抽拉微动) -->
<g id="pull-arrow" class="force-arrow" transform="translate(-60, 0)">
<path d="M 0 0 L -30 0 M -10 -10 L 0 0 L -10 10" stroke="var(--danger-red)" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<text x="-15" y="-15" fill="var(--danger-red)" font-size="12" text-anchor="middle">退让微动</text>
</g>
</g>
<!-- 铰接点 (Pivots) -->
<!-- 地面铰点 (固定) -->
<circle id="pivot-A" class="pivot-ground" cx="0" cy="0" r="8" />
<circle id="pivot-D" class="pivot-ground" cx="0" cy="0" r="8" />
<!-- 运动铰点 -->
<circle id="pivot-B" class="pivot" cx="0" cy="0" r="6" />
<circle id="pivot-C" class="pivot" cx="0" cy="0" r="6" />
</g>
</svg>
</div>
<script>
/**
* 非对称重型四连杆塞拉门机构 - 运动学求解与渲染
* 采用严格的正向运动学(Forward Kinematics)圆交点算法保证连杆刚性不变。
*/
// --- 核心几何参数 (已换算为适合屏幕显示的缩放比例) ---
// 基于TRIZ IFR要求:实现开启时先抽拉后旋转,闭合时大角度旋转+末端斜向平移。
// A为主动曲柄铰点(电机), D为从动摇臂铰点(雷亚架固定)
const scale = 3;
const A = { x: 45 * scale, y: 30 * scale }; // 坐标: (135, 90)
const D = { x: 75 * scale, y: -51.5 * scale }; // 坐标: (225, -154.5)
const L_AB = 45 * scale; // 短摇臂(135) - 对应实际450mm
const L_CD = 234.06; // 长摇臂 - 对应实际780mm
const L_BC = 60 * scale; // 屏幕安装孔距(180)
// 动画控制状态
let currentAngle = 180; // 初始为180度 (屏幕闭合状态)
const minAngle = 180; // 闭合极位
const maxAngle = 290; // 开启极位 (收纳在雷亚架侧边)
let direction = 1; // 1为开启,-1为闭合
let isPaused = false;
const speed = 0.6; // 动画速度
// DOM 元素引用
const dom = {
link1: document.getElementById('link1'),
link2: document.getElementById('link2'),
link1Shadow: document.getElementById('link1-shadow'),
link2Shadow: document.getElementById('link2-shadow'),
screenGroup: document.getElementById('screen-assembly'),
pivotA: document.getElementById('pivot-A'),
pivotD: document.getElementById('pivot-D'),
pivotB: document.getElementById('pivot-B'),
pivotC: document.getElementById('pivot-C'),
icrLine1: document.getElementById('icr-line-1'),
icrLine2: document.getElementById('icr-line-2'),
icrPt: document.getElementById('icr-pt'),
icrGlow: document.getElementById('icr-glow'),
icrLabel: document.getElementById('icr-label'),
pathIcr: document.getElementById('path-icr'),
pathScreen: document.getElementById('path-screen-corner'),
txtAngle: document.getElementById('angle-display'),
txtStatus: document.getElementById('status-display'),
pullArrow: document.getElementById('pull-arrow')
};
// 初始化静态底座锚点
dom.pivotA.setAttribute('cx', A.x); dom.pivotA.setAttribute('cy', A.y);
dom.pivotD.setAttribute('cx', D.x); dom.pivotD.setAttribute('cy', D.y);
// 记录上一帧的C点,用于两圆交点方程的解选择,防止机构反转(Bifurcation)
let lastC = { x: 0, y: -90 };
/**
* 计算给定曲柄角下,连杆机构的各点坐标
*/
function solveKinematics(thetaDeg) {
const theta = thetaDeg * Math.PI / 180;
// B点坐标 (曲柄端点)
const B = {
x: A.x + L_AB * Math.cos(theta),
y: A.y + L_AB * Math.sin(theta)
};
// 求解C点 (以B为圆心L_BC为半径的圆,与以D为圆心L_CD为半径的圆的交点)
const d = Math.hypot(D.x - B.x, D.y - B.y);
// 检查格拉霍夫机构连通性
if (d > L_BC + L_CD || d < Math.abs(L_BC - L_CD)) {
return null; // 无法构成三角形,卡死
}
const a = (L_BC**2 - L_CD**2 + d**2) / (2 * d);
const h = Math.sqrt(Math.max(0, L_BC**2 - a**2));
const P = {
x: B.x + a * (D.x - B.x) / d,
y: B.y + a * (D.y - B.y) / d
};
// 两个可能的解
const C1 = {
x: P.x + h * (D.y - B.y) / d,
y: P.y - h * (D.x - B.x) / d
};
const C2 = {
x: P.x - h * (D.y - B.y) / d,
y: P.y + h * (D.x - B.x) / d
};
// 利用上一帧记录选择运动连续的解 (距离更近的)
const dist1 = Math.hypot(C1.x - lastC.x, C1.y - lastC.y);
const dist2 = Math.hypot(C2.x - lastC.x, C2.y - lastC.y);
const C = (dist1 < dist2) ? C1 : C2;
lastC = C;
// 计算屏幕 (Coupler) 的位姿
// 假设屏幕以BC连线为基准,中点为原点,与BC平行
const center = {
x: (B.x + C.x) / 2,
y: (B.y + C.y) / 2
};
// 屏幕垂直于BC时,计算角度。由于闭合时B=(0,90), C=(0,-90), 矢量BC指向正下,角度为-90度。
// 我们希望此时屏幕旋转角为0(垂直)。所以相对角度偏移为 +90度。
const angleBC = Math.atan2(C.y - B.y, C.x - B.x) * 180 / Math.PI;
const screenAngle = angleBC + 90;
return { B, C, center, screenAngle };
}
/**
* 计算两条直线的交点 (用于求瞬心 ICR)
*/
function getIntersection(p1, p2, p3, p4) {
const denom = (p1.x - p2.x)*(p3.y - p4.y) - (p1.y - p2.y)*(p3.x - p4.x);
if (Math.abs(denom) < 0.1) return null; // 平行,瞬心在无穷远
const x = ((p1.x*p2.y - p1.y*p2.x)*(p3.x - p4.x) - (p1.x - p2.x)*(p3.x*p4.y - p3.y*p4.x)) / denom;
const y = ((p1.x*p2.y - p1.y*p2.x)*(p3.y - p4.y) - (p1.y - p2.y)*(p3.x*p4.y - p3.y*p4.x)) / denom;
return {x, y};
}
/**
* 更新DOM元素视觉呈现
*/
function updateVisuals(kinematics) {
if (!kinematics) return;
const { B, C, center, screenAngle } = kinematics;
// 更新连杆
dom.link1.setAttribute('x1', A.x); dom.link1.setAttribute('y1', A.y);
dom.link1.setAttribute('x2', B.x); dom.link1.setAttribute('y2', B.y);
dom.link1Shadow.setAttribute('x1', A.x); dom.link1Shadow.setAttribute('y1', A.y);
dom.link1Shadow.setAttribute('x2', B.x); dom.link1Shadow.setAttribute('y2', B.y);
dom.link2.setAttribute('x1', D.x); dom.link2.setAttribute('y1', D.y);
dom.link2.setAttribute('x2', C.x); dom.link2.setAttribute('y2', C.y);
dom.link2Shadow.setAttribute('x1', D.x); dom.link2Shadow.setAttribute('y1', D.y);
dom.link2Shadow.setAttribute('x2', C.x); dom.link2Shadow.setAttribute('y2', C.y);
// 更新运动铰点
dom.pivotB.setAttribute('cx', B.x); dom.pivotB.setAttribute('cy', B.y);
dom.pivotC.setAttribute('cx', C.x); dom.pivotC.setAttribute('cy', C.y);
// 更新屏幕位置及旋转
dom.screenGroup.setAttribute('transform', `translate(${center.x}, ${center.y}) rotate(${screenAngle})`);
// 计算并更新瞬心 ICR (直线AB与直线CD的交点)
const icr = getIntersection(A, B, D, C);
if (icr && Math.abs(icr.x) < 2000 && Math.abs(icr.y) < 2000) {
dom.icrLine1.setAttribute('x1', B.x); dom.icrLine1.setAttribute('y1', B.y);
dom.icrLine1.setAttribute('x2', icr.x); dom.icrLine1.setAttribute('y2', icr.y);
dom.icrLine2.setAttribute('x1', C.x); dom.icrLine2.setAttribute('y1', C.y);
dom.icrLine2.setAttribute('x2', icr.x); dom.icrLine2.setAttribute('y2', icr.y);
dom.icrPt.setAttribute('cx', icr.x); dom.icrPt.setAttribute('cy', icr.y);
dom.icrGlow.setAttribute('cx', icr.x); dom.icrGlow.setAttribute('cy', icr.y);
dom.icrLabel.setAttribute('x', icr.x); dom.icrLabel.setAttribute('y', icr.y);
dom.icrPt.style.opacity = 1;
dom.icrGlow.style.opacity = 1;
dom.icrLabel.style.opacity = 1;
dom.icrLine1.style.opacity = 0.5;
dom.icrLine2.style.opacity = 0.5;
} else {
// 接近无穷远时隐藏
dom.icrPt.style.opacity = 0;
dom.icrGlow.style.opacity = 0;
dom.icrLabel.style.opacity = 0;
dom.icrLine1.style.opacity = 0;
dom.icrLine2.style.opacity = 0;
}
// 更新UI文字
dom.txtAngle.textContent = currentAngle.toFixed(1);
if (currentAngle < 195 && direction === -1) {
// 闭合末端:显示“退让抽拉微动”提示
dom.pullArrow.classList.add('visible');
dom.txtStatus.textContent = "退让贴合中...";
dom.txtStatus.style.color = "var(--danger-red)";
} else if (currentAngle < 195 && direction === 1) {
// 开启初段
dom.pullArrow.classList.add('visible');
dom.txtStatus.textContent = "抽离启动...";
dom.txtStatus.style.color = "var(--danger-red)";
} else {
dom.pullArrow.classList.remove('visible');
dom.txtStatus.textContent = direction === 1 ? "折叠收纳中..." : "展开闭合中...";
dom.txtStatus.style.color = "var(--arm-orange)";
}
}
/**
* 预计算并绘制特征轨迹线 (视觉引导)
*/
function drawTrajectories() {
let pathICR = "";
let pathScreen = "";
// 记录屏幕左上角的运动轨迹
// 局部坐标: 屏幕中心(0,0),左上角约位于 (-40, -180)
const localCorner = { x: -40, y: -180 };
for (let a = minAngle; a <= maxAngle; a += 2) {
const k = solveKinematics(a);
if (!k) continue;
// 计算屏幕左上角的世界坐标
const rad = k.screenAngle * Math.PI / 180;
const cornerX = k.center.x + localCorner.x * Math.cos(rad) - localCorner.y * Math.sin(rad);
const cornerY = k.center.y + localCorner.x * Math.sin(rad) + localCorner.y * Math.cos(rad);
if (a === minAngle) {
pathScreen += `M ${cornerX} ${cornerY} `;
} else {
pathScreen += `L ${cornerX} ${cornerY} `;
}
// 计算ICR轨迹
const icr = getIntersection(A, k.B, D, k.C);
if (icr && Math.abs(icr.x) < 800 && Math.abs(icr.y) < 800) {
if (pathICR === "") {
pathICR += `M ${icr.x} ${icr.y} `;
} else {
pathICR += `L ${icr.x} ${icr.y} `;
}
} else {
pathICR += ""; // 断点
}
}
dom.pathIcr.setAttribute('d', pathICR);
dom.pathScreen.setAttribute('d', pathScreen);
}
/**
* 主动画循环 (自动播放,不依赖交互)
*/
function animateLoop() {
if (!isPaused) {
currentAngle += speed * direction;
if (currentAngle >= maxAngle) {
currentAngle = maxAngle;
isPaused = true;
dom.txtStatus.textContent = "完全开启 (收纳)";
dom.txtStatus.style.color = "var(--text-muted)";
setTimeout(() => { direction = -1; isPaused = false; }, 1500);
} else if (currentAngle <= minAngle) {
currentAngle = minAngle;
isPaused = true;
dom.txtStatus.textContent = "完全闭合 (工作位)";
dom.txtStatus.style.color = "var(--screen-cyan)";
setTimeout(() => { direction = 1; isPaused = false; }, 1500);
}
const kinematics = solveKinematics(currentAngle);
updateVisuals(kinematics);
}
requestAnimationFrame(animateLoop);
}
// 初始化执行
window.addEventListener('DOMContentLoaded', () => {
// 首先预计算静态轨迹并渲染
lastC = { x: 0, y: -90 }; // 重置保证正确解
drawTrajectories();
// 强制重置确保从初始极位自动开始
currentAngle = 180;
direction = 1;
lastC = { x: 0, y: -90 };
const initialKinematics = solveKinematics(currentAngle);
updateVisuals(initialKinematics);
// 启动动画循环
requestAnimationFrame(animateLoop);
});
</script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
