104 lines
3.8 KiB
HTML
104 lines
3.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
background: #fdfdfd;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 100vh;
|
|
}
|
|
.hero-vector {
|
|
width: 90%;
|
|
max-width: 800px;
|
|
overflow: visible;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<svg class="hero-vector" viewBox="0 0 900 350" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path id="track-1" d="M 50 175 C 150 50, 250 300, 450 175 C 650 50, 750 300, 850 175 C 750 50, 650 300, 450 175 C 250 50, 150 300, 50 175 Z"
|
|
stroke="#429b82" stroke-width="8" stroke-linecap="round" fill="none" />
|
|
|
|
<path id="track-2" d="M 50 175 C 150 300, 250 50, 450 175 C 650 300, 750 50, 850 175 C 750 300, 650 50, 450 175 C 250 300, 150 50, 50 175 Z"
|
|
stroke="#b2c6ce" stroke-width="8" stroke-linecap="round" fill="none" opacity="0.6" />
|
|
|
|
<g id="dashed-lines"></g>
|
|
|
|
<g id="nodes-layer"></g>
|
|
</svg>
|
|
|
|
<script>
|
|
const track1 = document.getElementById('track-1');
|
|
const track2 = document.getElementById('track-2');
|
|
const len1 = track1.getTotalLength();
|
|
const len2 = track2.getTotalLength();
|
|
|
|
const nodesLayer = document.getElementById('nodes-layer');
|
|
const linesLayer = document.getElementById('dashed-lines');
|
|
|
|
// Configuración de los nodos móviles
|
|
const nodesConfig = [
|
|
{ track: track1, length: len1, progress: 0.00, speed: 0.0004, color: '#133543', radius: 11 },
|
|
{ track: track1, length: len1, progress: 0.25, speed: 0.0004, color: '#429b82', radius: 9 },
|
|
{ track: track1, length: len1, progress: 0.50, speed: 0.0004, color: '#133543', radius: 11 },
|
|
{ track: track1, length: len1, progress: 0.75, speed: 0.0004, color: '#429b82', radius: 9 },
|
|
{ track: track2, length: len2, progress: 0.12, speed: 0.0003, color: '#133543', radius: 10 },
|
|
{ track: track2, length: len2, progress: 0.62, speed: 0.0003, color: '#429b82', radius: 10 }
|
|
];
|
|
|
|
// Crear elementos SVG en el DOM
|
|
const nodes = nodesConfig.map(cfg => {
|
|
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
circle.setAttribute('r', cfg.radius);
|
|
circle.setAttribute('fill', cfg.color);
|
|
nodesLayer.appendChild(circle);
|
|
return { ...cfg, element: circle, x: 0, y: 0 };
|
|
});
|
|
|
|
// Crear líneas punteadas para conectar pares de nodos consecutivos
|
|
const lines = [];
|
|
for (let i = 0; i < nodes.length - 1; i++) {
|
|
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
line.setAttribute('stroke', '#133543');
|
|
line.setAttribute('stroke-width', '2');
|
|
line.setAttribute('stroke-dasharray', '5,5');
|
|
line.setAttribute('opacity', '0.7');
|
|
linesLayer.appendChild(line);
|
|
lines.push(line);
|
|
}
|
|
|
|
// Bucle de animación (60fps)
|
|
function animate() {
|
|
// 1. Actualizar posición de cada nodo sobre su trayectoria
|
|
nodes.forEach(node => {
|
|
node.progress = (node.progress + node.speed) % 1;
|
|
const point = node.track.getPointAtLength(node.progress * node.length);
|
|
node.x = point.x;
|
|
node.y = point.y;
|
|
node.element.setAttribute('cx', point.x);
|
|
node.element.setAttribute('cy', point.y);
|
|
});
|
|
|
|
// 2. Conectar nodos ordenados por su eje X con las líneas punteadas
|
|
const sortedNodes = [...nodes].sort((a, b) => a.x - b.x);
|
|
for (let i = 0; i < sortedNodes.length - 1; i++) {
|
|
const p1 = sortedNodes[i];
|
|
const p2 = sortedNodes[i + 1];
|
|
lines[i].setAttribute('x1', p1.x);
|
|
lines[i].setAttribute('y1', p1.y);
|
|
lines[i].setAttribute('x2', p2.x);
|
|
lines[i].setAttribute('y2', p2.y);
|
|
}
|
|
|
|
requestAnimationFrame(animate);
|
|
}
|
|
|
|
requestAnimationFrame(animate);
|
|
</script>
|
|
</body>
|
|
</html> |