Skip to main content

Overview

The hero section is the first visual element visitors see. It features an animated particle background, an interactive SVG character that responds to mouse movements, and a drawing canvas where users can create their own greetings.

HTML Structure

index.html (lines 60-105)
<section id="home" class="relative min-h-screen flex items-center justify-center overflow-hidden">
  <!-- Canvas for particle background -->
  <canvas id="particlesCanvas" class="absolute inset-0 z-0 pointer-events-none max-w-screen-xl mx-auto"></canvas>
  
  <div class="container relative z-10 mx-auto px-6 py-24 flex flex-col md:flex-row items-center gap-12">
    <!-- Hero Text -->
    <div class="md:w-1/2 text-center md:text-left">
      <h2 class="text-4xl md:text-6xl font-bold mb-6">
        Hola, soy <span class="text-red-accent">NILVER T.I</span>
      </h2>
      <p class="text-xl md:text-2xl text-gray-300 mb-8">
        Desarrollador de Tecnologías de la Información especializado en soluciones web modernas.
      </p>
      <div class="flex flex-col sm:flex-row sm:justify-start justify-center gap-4">
        <a href="#projects" class="bg-red-accent hover:bg-red-700 text-white px-6 py-3 rounded-md font-medium transition hover:scale-105">
          Ver proyectos
        </a>
        <a href="#contact" class="border border-red-accent text-red-accent hover:bg-red-900 hover:bg-opacity-20 px-6 py-3 rounded-md font-medium transition hover:scale-105">
          Contactar
        </a>
      </div>
    </div>

    <!-- Animated SVG Character -->
    <div class="md:w-1/2 flex justify-center">
      <svg id="profileSvg" width="400" height="400" viewBox="0 0 200 200" class="w-full max-w-xs">
        <circle cx="100" cy="100" r="80" fill="none" stroke="#333" stroke-width="2" />
        <circle cx="100" cy="100" r="60" fill="none" stroke="#333" stroke-width="2" />
        <circle cx="100" cy="100" r="40" fill="none" stroke="#333" stroke-width="2" />
        <path id="faceOutline" fill="none" stroke="#E50914" stroke-width="3" stroke-linecap="round"
          d="M60,100 Q100,150 140,100" />
        <circle id="leftEye" cx="80" cy="80" r="10" fill="#E50914" />
        <circle id="rightEye" cx="120" cy="80" r="10" fill="#E50914" />
      </svg>
    </div>

    <!-- Drawing Canvas -->
    <div class="mt-8 text-center">
      <p class="text-gray-400 mb-2">Dibuja tu saludo:</p>
      <canvas id="drawingCanvas" width="300" height="150" class="drawing-canvas bg-black rounded-md"></canvas>
      <button id="clearCanvas" class="mt-2 text-sm text-gray-400 hover:text-red-accent">Limpiar</button>
    </div>
  </div>
</section>

Particle Background Animation

The particle background creates a dynamic visual effect using HTML5 Canvas.

Implementation

fondo.js (complete file)
const canvas = document.getElementById('particlesCanvas');
const ctx = canvas.getContext('2d');

let particles = [];

// Resize canvas to match section height
function resizeCanvas() {
  canvas.width = window.innerWidth;
  canvas.height = document.getElementById('home').offsetHeight;
}

window.addEventListener('resize', resizeCanvas);
resizeCanvas();

// Create 100 particles with random properties
for (let i = 0; i < 100; i++) {
  particles.push({
    x: Math.random() * canvas.width,
    y: Math.random() * canvas.height,
    radius: Math.random() * 2 + 1,
    speedX: Math.random() * 0.6 - 0.3,
    speedY: Math.random() * 0.6 - 0.3
  });
}

// Animation loop
function animateParticles() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  for (let p of particles) {
    // Update position
    p.x += p.speedX;
    p.y += p.speedY;

    // Bounce off edges
    if (p.x < 0 || p.x > canvas.width) p.speedX *= -1;
    if (p.y < 0 || p.y > canvas.height) p.speedY *= -1;

    // Draw particle
    ctx.beginPath();
    ctx.arc(p.x, p.y, p.radius, 0, 2 * Math.PI);
    ctx.fillStyle = 'rgba(229, 9, 20, 0.4)';
    ctx.fill();
  }

  requestAnimationFrame(animateParticles);
}

animateParticles();
The animation uses requestAnimationFrame for smooth 60fps rendering. Particles bounce off canvas edges by reversing their velocity when reaching boundaries.

Customizing Particles

Change the number of particles:
for (let i = 0; i < 150; i++) { // Increase from 100 to 150
  particles.push({...});
}

Animated SVG Character

The SVG character’s eyes follow the mouse cursor, and the mouth expression changes based on mouse position.

SVG Structure

SVG Elements
<svg id="profileSvg" width="400" height="400" viewBox="0 0 200 200">
  <!-- Concentric circles background -->
  <circle cx="100" cy="100" r="80" fill="none" stroke="#333" stroke-width="2" />
  <circle cx="100" cy="100" r="60" fill="none" stroke="#333" stroke-width="2" />
  <circle cx="100" cy="100" r="40" fill="none" stroke="#333" stroke-width="2" />
  
  <!-- Mouth (smile curve) -->
  <path id="faceOutline" fill="none" stroke="#E50914" stroke-width="3" 
    stroke-linecap="round" d="M60,100 Q100,150 140,100" />
  
  <!-- Eyes -->
  <circle id="leftEye" cx="80" cy="80" r="10" fill="#E50914" />
  <circle id="rightEye" cx="120" cy="80" r="10" fill="#E50914" />
</svg>

Mouse Tracking Animation

main.js (lines 50-76)
const profileSvg = document.getElementById('profileSvg');
if (profileSvg) {
  const leftEye = document.getElementById('leftEye');
  const rightEye = document.getElementById('rightEye');
  const faceOutline = document.getElementById('faceOutline');

  profileSvg.addEventListener('mousemove', e => {
    const rect = profileSvg.getBoundingClientRect();
    // Convert mouse position to SVG coordinates
    const x = (e.clientX - rect.left) / rect.width * 200;
    const y = (e.clientY - rect.top) / rect.height * 200;
    
    // Move eyes toward mouse (5% of distance)
    leftEye.setAttribute('cx', 80 + (x - 80) * 0.05);
    leftEye.setAttribute('cy', 80 + (y - 80) * 0.05);
    rightEye.setAttribute('cx', 120 + (x - 120) * 0.05);
    rightEye.setAttribute('cy', 80 + (y - 80) * 0.05);
    
    // Adjust mouth curve based on mouse Y position
    faceOutline.setAttribute('d', `M60,100 Q100,${130 + (y - 100) * 0.2} 140,100`);
  });

  // Reset to default position when mouse leaves
  profileSvg.addEventListener('mouseleave', () => {
    leftEye.setAttribute('cx', '80');
    leftEye.setAttribute('cy', '80');
    rightEye.setAttribute('cx', '120');
    rightEye.setAttribute('cy', '80');
    faceOutline.setAttribute('d', 'M60,100 Q100,150 140,100');
  });
}
Coordinate Conversion:
const x = (e.clientX - rect.left) / rect.width * 200;
Converts browser coordinates to SVG viewBox coordinates (0-200)Eye Movement:
leftEye.setAttribute('cx', 80 + (x - 80) * 0.05);
Moves the eye 5% of the distance between current position (80) and mouse position (x)Mouth Curve:
faceOutline.setAttribute('d', `M60,100 Q100,${130 + (y - 100) * 0.2} 140,100`);
Uses SVG quadratic curve (Q) where control point Y changes based on mouse Y position

Interactive Drawing Canvas

Users can draw directly on the canvas with their mouse.

Canvas Implementation

main.js (lines 78-112)
const canvas = document.getElementById('drawingCanvas');
if (canvas) {
  const ctx = canvas.getContext('2d');
  let isDrawing = false;

  // Mouse event listeners
  canvas.addEventListener('mousedown', e => { 
    isDrawing = true; 
    draw(e); 
  });
  canvas.addEventListener('mousemove', draw);
  canvas.addEventListener('mouseup', () => isDrawing = false);
  canvas.addEventListener('mouseout', () => isDrawing = false);

  function draw(e) {
    if (!isDrawing) return;
    
    // Configure brush
    ctx.lineWidth = 2;
    ctx.lineCap = 'round';
    ctx.strokeStyle = '#E50914';
    
    // Get mouse position relative to canvas
    const rect = canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    
    // Draw line from last position to current
    if (ctx.lastX && ctx.lastY) {
      ctx.beginPath();
      ctx.moveTo(ctx.lastX, ctx.lastY);
      ctx.lineTo(x, y);
      ctx.stroke();
    }
    
    // Store current position for next frame
    ctx.lastX = x;
    ctx.lastY = y;
  }

  // Clear canvas button
  document.getElementById('clearCanvas')?.addEventListener('click', () => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
  });
}

Canvas Styling

estilos.css (lines 56-59)
.drawing-canvas {
  border: 1px solid #E50914;
  cursor: crosshair;
}

Hero Text and CTAs

The hero text includes responsive typography and call-to-action buttons.
<h2 class="text-4xl md:text-6xl font-bold mb-6">
  Hola, soy <span class="text-red-accent">NILVER T.I</span>
</h2>

Button Hover Effect

estilos.css (lines 22-28)
.hover-scale {
  transition: transform 0.3s ease;
}

.hover-scale:hover {
  transform: scale(1.05);
}

Responsive Layout

The hero section adapts to different screen sizes:
ElementMobileDesktop
Layoutflex-col (stacked)md:flex-row (side-by-side)
Headingtext-4xlmd:text-6xl
Text aligntext-centermd:text-left
SVG sizemax-w-xsw-full max-w-xs
Buttonsflex-colsm:flex-row

Customization Examples

Change Hero Background Color

<section id="home" class="relative min-h-screen flex items-center justify-center overflow-hidden bg-gradient-to-br from-gray-900 to-black">

Add Touch Support for Drawing Canvas

// Add touch event listeners
canvas.addEventListener('touchstart', e => {
  isDrawing = true;
  const touch = e.touches[0];
  draw({ clientX: touch.clientX, clientY: touch.clientY });
  e.preventDefault();
});

canvas.addEventListener('touchmove', e => {
  if (!isDrawing) return;
  const touch = e.touches[0];
  draw({ clientX: touch.clientX, clientY: touch.clientY });
  e.preventDefault();
});

canvas.addEventListener('touchend', () => {
  isDrawing = false;
  ctx.lastX = null;
  ctx.lastY = null;
});

Modify SVG Character Colors

<!-- Change eyes to blue -->
<circle id="leftEye" cx="80" cy="80" r="10" fill="#3B82F6" />
<circle id="rightEye" cx="120" cy="80" r="10" fill="#3B82F6" />

<!-- Change mouth to green -->
<path id="faceOutline" fill="none" stroke="#10B981" stroke-width="3" stroke-linecap="round" d="M60,100 Q100,150 140,100" />

Performance Tips

The particle animation runs continuously. Consider pausing when the hero section is not visible:
let animationId;
function animateParticles() {
  // ... animation code ...
  animationId = requestAnimationFrame(animateParticles);
}

// Pause when out of view
const observer = new IntersectionObserver(entries => {
  if (entries[0].isIntersecting) {
    animateParticles();
  } else {
    cancelAnimationFrame(animationId);
  }
});
observer.observe(document.getElementById('home'));

Reduce Particles

Lower particle count from 100 to 50 for better mobile performance

Optimize Canvas

Clear drawing canvas area only instead of full canvas

Throttle Mouse Events

Use requestAnimationFrame to throttle SVG mouse tracking

Lazy Load

Initialize animations only when hero section is visible

Troubleshooting

Ensure canvas has proper z-index and dimensions:
#particlesCanvas {
  position: absolute;
  z-index: 0;
  pointer-events: none;
}
Check that SVG has proper event listeners and IDs match:
console.log(document.getElementById('profileSvg')); // Should not be null
console.log(document.getElementById('leftEye')); // Should not be null
Verify canvas context is 2D and dimensions are set:
const ctx = canvas.getContext('2d');
console.log(canvas.width, canvas.height); // Should show 300, 150

Build docs developers (and LLMs) love