This commit is contained in:
oliver
2026-08-28 10:11:11 -03:00
parent f533ff8e36
commit 585951c8ce
1380 changed files with 1540268 additions and 0 deletions
+235
View File
@@ -0,0 +1,235 @@
// D3.js Animations for Homepage - Training & Learning Focus
function initD3Animation() {
const container = d3.select("#d3-animation");
if (container.empty()) return;
const containerRect = container.node().getBoundingClientRect();
const width = containerRect.width;
const height = containerRect.height || 400;
// Clear any existing SVG
container.selectAll("*").remove();
const svg = container.append("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", `0 0 ${width} ${height}`)
.style("background", "transparent")
.style("overflow", "hidden");
// Create a main group for all elements
const mainGroup = svg.append("g")
.attr("class", "main-group");
// Create animated network visualization representing Odoo training modules
const nodes = [
{ id: "Fundamentos", group: 1, size: 35, level: 1 },
{ id: "CRM Básico", group: 2, size: 25, level: 2 },
{ id: "Ventas", group: 2, size: 25, level: 2 },
{ id: "Inventario", group: 3, size: 25, level: 2 },
{ id: "Contabilidad", group: 4, size: 30, level: 2 },
{ id: "CRM Avanzado", group: 2, size: 20, level: 3 },
{ id: "Reportes", group: 4, size: 20, level: 3 },
{ id: "Integraciones", group: 3, size: 25, level: 3 },
{ id: "Certificación", group: 1, size: 30, level: 4 },
{ id: "Especialización", group: 1, size: 25, level: 4 }
];
const links = [
{ source: "Fundamentos", target: "CRM Básico", value: 3 },
{ source: "Fundamentos", target: "Ventas", value: 3 },
{ source: "Fundamentos", target: "Inventario", value: 3 },
{ source: "Fundamentos", target: "Contabilidad", value: 3 },
{ source: "CRM Básico", target: "CRM Avanzado", value: 2 },
{ source: "Ventas", target: "CRM Avanzado", value: 2 },
{ source: "Contabilidad", target: "Reportes", value: 3 },
{ source: "Inventario", target: "Integraciones", value: 3 },
{ source: "CRM Avanzado", target: "Certificación", value: 3 },
{ source: "Reportes", target: "Certificación", value: 2 },
{ source: "Integraciones", target: "Especialización", value: 2 },
{ source: "Certificación", target: "Especialización", value: 3 }
];
// Color scale with orange theme
const color = d3.scaleOrdinal()
.domain([1, 2, 3, 4])
.range(["#FF6B35", "#FF8A50", "#FFA726", "#FFB74D"]);
// Add boundary force to keep nodes within container
const boundaryForce = () => {
nodes.forEach(node => {
node.x = Math.max(node.size, Math.min(width - node.size, node.x));
node.y = Math.max(node.size, Math.min(height - node.size, node.y));
});
};
// Create force simulation with boundaries
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(80))
.force("charge", d3.forceManyBody().strength(-250))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("collision", d3.forceCollide().radius(d => d.size + 8))
.force("boundary", boundaryForce);
// Create links
const link = mainGroup.append("g")
.attr("class", "links")
.selectAll("line")
.data(links)
.enter().append("line")
.attr("stroke", "#FF6B35")
.attr("stroke-opacity", 0.3)
.attr("stroke-width", d => Math.sqrt(d.value));
// Create nodes container
const node = mainGroup.append("g")
.attr("class", "nodes")
.selectAll("g")
.data(nodes)
.enter().append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// Add circles
node.append("circle")
.attr("r", d => d.size)
.attr("fill", d => color(d.group))
.attr("stroke", "#1a1a1a")
.attr("stroke-width", 2)
.style("cursor", "pointer")
.on("mouseover", function(event, d) {
d3.select(this)
.transition()
.duration(200)
.attr("r", d.size * 1.2);
})
.on("mouseout", function(event, d) {
d3.select(this)
.transition()
.duration(200)
.attr("r", d.size);
});
// Add text labels
node.append("text")
.text(d => d.id)
.attr("text-anchor", "middle")
.attr("dy", ".35em")
.style("fill", "#1a1a1a")
.style("font-size", "11px")
.style("font-weight", "700")
.style("pointer-events", "none");
// Add animated pulses
node.append("circle")
.attr("r", d => d.size)
.attr("fill", "none")
.attr("stroke", d => color(d.group))
.attr("stroke-width", 2)
.attr("opacity", 0)
.style("pointer-events", "none")
.each(function(d, i) {
d3.select(this)
.transition()
.delay(i * 200)
.duration(2000)
.attr("r", d => d.size * 2)
.attr("opacity", 0)
.on("end", function repeat() {
d3.select(this)
.attr("r", d.size)
.attr("opacity", 0.5)
.transition()
.duration(2000)
.attr("r", d.size * 2)
.attr("opacity", 0)
.on("end", repeat);
});
});
// Update positions on tick
simulation.on("tick", () => {
// Apply boundary force
boundaryForce();
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node
.attr("transform", d => `translate(${d.x},${d.y})`);
});
// Drag functions with boundaries
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = Math.max(d.size, Math.min(width - d.size, event.x));
d.fy = Math.max(d.size, Math.min(height - d.size, event.y));
}
function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
// Add floating particles with orange theme
const particles = mainGroup.append("g")
.attr("class", "particles");
function createParticle() {
const particle = particles.append("circle")
.attr("r", Math.random() * 2 + 1)
.attr("cx", Math.random() * width)
.attr("cy", height + 5)
.attr("fill", "#FF6B35")
.attr("opacity", 0.2);
particle.transition()
.duration(Math.random() * 8000 + 4000)
.ease(d3.easeLinear)
.attr("cy", -5)
.attr("opacity", 0)
.remove();
}
// Create particles periodically
setInterval(createParticle, 500);
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
initD3Animation();
// Reinitialize on window resize
let resizeTimer;
window.addEventListener('resize', function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
initD3Animation();
}, 250);
});
});
// Smooth scroll for anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});