/** * Roof Cleaning Animation * Shows a drone flying over a dirty roof and cleaning it in a continuous loop */ (function() { document.addEventListener('DOMContentLoaded', function() { const heroSection = document.querySelector('.hero'); if (heroSection) { initRoofCleaningAnimation(heroSection); } }); function initRoofCleaningAnimation(container) { // Create canvas container const canvasContainer = document.createElement('div'); canvasContainer.className = 'roof-cleaning-animation'; canvasContainer.style.position = 'absolute'; canvasContainer.style.top = '0'; canvasContainer.style.left = '0'; canvasContainer.style.width = '100%'; canvasContainer.style.height = '100%'; canvasContainer.style.zIndex = '1'; canvasContainer.style.pointerEvents = 'none'; canvasContainer.style.overflow = 'hidden'; // Create canvas element const canvas = document.createElement('canvas'); canvas.width = container.offsetWidth; canvas.height = container.offsetHeight; canvas.style.position = 'absolute'; canvas.style.top = '0'; canvas.style.left = '0'; canvas.style.width = '100%'; canvas.style.height = '100%'; canvasContainer.appendChild(canvas); // Add to container container.appendChild(canvasContainer); // Set up the animation setupAnimation(canvas); // Handle window resize window.addEventListener('resize', function() { canvas.width = container.offsetWidth; canvas.height = container.offsetHeight; setupAnimation(canvas); }); } function setupAnimation(canvas) { const ctx = canvas.getContext('2d'); const width = canvas.width; const height = canvas.height; // Animation variables let droneX = -100; // Start off-screen const droneY = height * 0.4; // Position drone at 40% of height const droneWidth = 80; const droneHeight = 40; const droneSpeed = 2; const cleaningWidth = 80; // Width of cleaning path // Create offscreen canvases for better performance const roofCanvas = createRoofCanvas(width, height); const cleanCanvas = createCleanCanvas(width, height); const maskCanvas = document.createElement('canvas'); maskCanvas.width = width; maskCanvas.height = height; const maskCtx = maskCanvas.getContext('2d'); // Create drone image const drone = new Image(); drone.src = 'images/spraying_drone1.png'; // Droplets array for water spray const droplets = []; // Animation loop function animate() { // Clear the canvas ctx.clearRect(0, 0, width, height); // Draw sky background drawSky(ctx, width, height); // Update drone position droneX += droneSpeed; if (droneX > width + 100) { // Reset drone position and partially reset mask for continuous loop droneX = -100; // Reset left side of mask maskCtx.clearRect(0, height * 0.45, width * 0.2, height * 0.55); } // Update cleaning mask updateCleaningMask(maskCtx, droneX, droneY, cleaningWidth); // Draw dirty roof ctx.drawImage(roofCanvas, 0, 0); // Apply cleaning mask to show clean areas ctx.globalCompositeOperation = 'destination-out'; ctx.drawImage(maskCanvas, 0, 0); ctx.globalCompositeOperation = 'source-over'; // Draw clean roof in masked areas ctx.drawImage(cleanCanvas, 0, 0); // Generate water droplets if (Math.random() < 0.3) { // 30% chance each frame createDroplet(droplets, droneX + droneWidth/2, droneY + droneHeight/2); } // Update and draw water droplets updateDroplets(ctx, droplets); // Draw the drone ctx.drawImage(drone, droneX, droneY, droneWidth, droneHeight); // Continue animation loop requestAnimationFrame(animate); } // Wait for drone image to load before starting animation drone.onload = function() { animate(); }; } function drawSky(ctx, width, height) { // Create gradient for sky const skyGradient = ctx.createLinearGradient(0, 0, 0, height * 0.45); skyGradient.addColorStop(0, '#87CEEB'); // Sky blue skyGradient.addColorStop(1, '#e6f7ff'); // Light blue ctx.fillStyle = skyGradient; ctx.fillRect(0, 0, width, height * 0.45); } function createRoofCanvas(width, height) { const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); // Draw dirty roof const roofY = height * 0.45; // Start roof at 45% of height const roofHeight = height * 0.55; // Roof takes up 55% of height // Base roof color (dirty brownish) ctx.fillStyle = '#8B4513'; ctx.fillRect(0, roofY, width, roofHeight); // Add roof tiles texture ctx.fillStyle = '#6B3501'; for (let y = roofY; y < height; y += 20) { for (let x = 0; x < width; x += 60) { const offset = (Math.floor(y / 20) % 2) * 30; ctx.fillRect(x + offset, y, 30, 10); } } // Add dirt and moss spots for (let i = 0; i < 200; i++) { const spotX = Math.random() * width; const spotY = roofY + Math.random() * roofHeight; const spotSize = 3 + Math.random() * 15; ctx.fillStyle = Math.random() > 0.5 ? 'rgba(50, 30, 0, 0.4)' : // Dirt 'rgba(30, 70, 30, 0.3)'; // Moss ctx.beginPath(); ctx.arc(spotX, spotY, spotSize, 0, Math.PI * 2); ctx.fill(); } return canvas; } function createCleanCanvas(width, height) { const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); // Draw clean roof const roofY = height * 0.45; const roofHeight = height * 0.55; // Base clean roof color (reddish-brown) ctx.fillStyle = '#A0522D'; ctx.fillRect(0, roofY, width, roofHeight); // Add clean roof tiles texture ctx.fillStyle = '#8B4513'; for (let y = roofY; y < height; y += 20) { for (let x = 0; x < width; x += 60) { const offset = (Math.floor(y / 20) % 2) * 30; ctx.fillRect(x + offset, y, 30, 10); } } return canvas; } function updateCleaningMask(ctx, droneX, droneY, cleaningWidth) { // Create radial gradient for cleaning effect const gradient = ctx.createRadialGradient( droneX + 40, droneY + 30, 5, droneX + 40, droneY + 30, cleaningWidth ); gradient.addColorStop(0, 'rgba(0, 0, 0, 1)'); gradient.addColorStop(1, 'rgba(0, 0, 0, 0)'); ctx.fillStyle = gradient; ctx.beginPath(); ctx.arc(droneX + 40, droneY + 30, cleaningWidth, 0, Math.PI * 2); ctx.fill(); } function createDroplet(droplets, x, y) { droplets.push({ x: x, y: y, size: 2 + Math.random() * 3, speedX: -1 + Math.random() * 2, speedY: 4 + Math.random() * 2, opacity: 0.7 + Math.random() * 0.3 }); // Limit number of droplets for performance if (droplets.length > 50) { droplets.shift(); } } function updateDroplets(ctx, droplets) { // Draw water droplets ctx.fillStyle = '#82CAFF'; for (let i = 0; i < droplets.length; i++) { const droplet = droplets[i]; // Update position droplet.x += droplet.speedX; droplet.y += droplet.speedY; // Reduce opacity as it falls droplet.opacity -= 0.02; if (droplet.opacity <= 0) { droplets.splice(i, 1); i--; continue; } // Draw droplet ctx.globalAlpha = droplet.opacity; ctx.beginPath(); ctx.arc(droplet.x, droplet.y, droplet.size, 0, Math.PI * 2); ctx.fill(); } ctx.globalAlpha = 1; } })();