Files
kidsai/html/drone_bup/js/roof-cleaning-animation.js
2025-06-24 15:43:32 +02:00

269 lines
9.0 KiB
JavaScript

/**
* 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;
}
})();