Files
nexus-terminal/packages/backend/html-presets/数字电路.html
T
2025-05-27 21:36:06 +08:00

195 lines
7.9 KiB
HTML

<style>
#circuit-board-container {
width: 100%; height: 100%;
background-color: #0a192f; /* 非常深的科技蓝 */
overflow: hidden; position: relative;
}
#circuit-canvas { display: block; position: absolute; top: 0; left: 0; }
</style>
<div id="circuit-board-container">
<canvas id="circuit-canvas"></canvas>
</div>
<script>
(function() {
const canvas = document.getElementById('circuit-canvas');
const layerElement = canvas.closest('.terminal-custom-html-layer') || canvas.parentElement;
if (!canvas || !layerElement) return;
const ctx = canvas.getContext('2d');
let width, height, animationFrameId;
const traces = [];
const numTraces = 30;
const gridSize = 40; // 网格大小,用于确定路径点
const traceColor = "rgba(50, 180, 220, 0.15)"; // 静态轨迹颜色
const signalColor = "rgba(100, 220, 255, 0.9)"; // 信号颜色
const signalSpeed = 2; // 信号移动速度 (px/frame)
class Trace {
constructor() {
this.points = [];
this.generatePath();
this.signalPosition = 0; // 信号在路径上的当前距离
this.signalActive = Math.random() < 0.7; // 70% 的轨迹初始有信号
this.signalRestartDelay = 0; // 信号消失后的重启延迟
}
generatePath() {
this.points = [];
const startX = Math.floor(Math.random() * (width / gridSize)) * gridSize;
const startY = Math.floor(Math.random() * (height / gridSize)) * gridSize;
this.points.push({ x: startX, y: startY });
let currentX = startX;
let currentY = startY;
const pathLength = 5 + Math.floor(Math.random() * 10); // 路径段数
for (let i = 0; i < pathLength; i++) {
const directions = []; // 0: up, 1: down, 2: left, 3: right
if (currentY > 0) directions.push(0);
if (currentY < height - gridSize) directions.push(1);
if (currentX > 0) directions.push(2);
if (currentX < width - gridSize) directions.push(3);
if (directions.length === 0) break; // No valid moves
const move = directions[Math.floor(Math.random() * directions.length)];
let nextX = currentX, nextY = currentY;
switch (move) {
case 0: nextY -= gridSize; break; // Up
case 1: nextY += gridSize; break; // Down
case 2: nextX -= gridSize; break; // Left
case 3: nextX += gridSize; break; // Right
}
// 避免立即回头
if (this.points.length > 1) {
const prevPoint = this.points[this.points.length - 2];
if (prevPoint.x === nextX && prevPoint.y === nextY) {
i--; // Try again
continue;
}
}
this.points.push({ x: nextX, y: nextY });
currentX = nextX;
currentY = nextY;
}
this.totalLength = 0;
for(let i = 0; i < this.points.length -1; i++) {
this.totalLength += gridSize; // All segments are gridSize long
}
}
update() {
if (this.signalActive) {
this.signalPosition += signalSpeed;
if (this.signalPosition > this.totalLength + 20) { // +20 to let it fade out
this.signalActive = false;
this.signalPosition = 0;
this.signalRestartDelay = 100 + Math.random() * 300; // 100-400 frames delay
}
} else {
this.signalRestartDelay--;
if (this.signalRestartDelay <= 0 && Math.random() < 0.01) { // Low chance to reactivate
this.signalActive = true;
// Optional: re-generate path
// this.generatePath();
}
}
}
draw() {
if (this.points.length < 2) return;
// Draw static trace
ctx.beginPath();
ctx.moveTo(this.points[0].x, this.points[0].y);
for (let i = 1; i < this.points.length; i++) {
ctx.lineTo(this.points[i].x, this.points[i].y);
}
ctx.strokeStyle = traceColor;
ctx.lineWidth = 1 + Math.random()*0.5;
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.stroke();
// Draw signal
if (this.signalActive) {
ctx.beginPath();
let distanceTraveled = 0;
let signalDrawn = false;
for (let i = 0; i < this.points.length - 1; i++) {
const p1 = this.points[i];
const p2 = this.points[i+1];
const segmentLength = gridSize;
if (!signalDrawn && this.signalPosition >= distanceTraveled && this.signalPosition < distanceTraveled + segmentLength) {
const progressInSegment = (this.signalPosition - distanceTraveled) / segmentLength;
const sigX = p1.x + (p2.x - p1.x) * progressInSegment;
const sigY = p1.y + (p2.y - p1.y) * progressInSegment;
ctx.moveTo(sigX, sigY); // Start of signal for this frame
signalDrawn = true; // Draw only one moveTo
}
if(signalDrawn && this.signalPosition + 10 > distanceTraveled) { // 10 is signal "tail" length
ctx.lineTo(p2.x, p2.y);
if(this.signalPosition > distanceTraveled + segmentLength + 10) {
// break; // Signal has passed this segment
}
}
if (this.signalPosition < distanceTraveled + segmentLength && signalDrawn) {
// Draw up to the end of current segment or signal head
const endProgress = Math.min(1, (this.signalPosition + 10 - distanceTraveled) / segmentLength); // +10 for tail
const endX = p1.x + (p2.x - p1.x) * endProgress;
const endY = p1.y + (p2.y - p1.y) * endProgress;
ctx.lineTo(endX, endY);
break; // Signal is within this segment or ends here
}
distanceTraveled += segmentLength;
}
if (signalDrawn) {
ctx.strokeStyle = signalColor;
ctx.lineWidth = 2 + Math.random();
ctx.shadowColor = signalColor;
ctx.shadowBlur = 8 + Math.random() * 5;
ctx.stroke();
ctx.shadowBlur = 0; // Reset shadow
}
}
}
}
function init() {
width = layerElement.offsetWidth;
height = layerElement.offsetHeight;
canvas.width = width;
canvas.height = height;
traces.length = 0;
for (let i = 0; i < numTraces; i++) {
traces.push(new Trace());
}
}
function animate() {
ctx.clearRect(0, 0, width, height);
traces.forEach(trace => {
trace.update();
trace.draw();
});
animationFrameId = requestAnimationFrame(animate);
}
let resizeTimeout;
function onResize() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
if (animationFrameId) cancelAnimationFrame(animationFrameId);
init();
animate();
}, 250);
}
init();
animate();
window.addEventListener('resize', onResize);
canvas.cleanup = function() { if (animationFrameId) cancelAnimationFrame(animationFrameId); };
})();
</script>