From 69e0eda1e5339d50ffc94fabd44be13bbd2afc18 Mon Sep 17 00:00:00 2001 From: bender Date: Tue, 10 Mar 2026 20:01:06 +0000 Subject: [PATCH 1/2] Update src/app/page.tsx --- src/app/page.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/app/page.tsx b/src/app/page.tsx index 067c572..0aa0946 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -11,6 +11,7 @@ import SocialProofOne from '@/components/sections/socialProof/SocialProofOne'; import TestimonialCardFifteen from '@/components/sections/testimonial/TestimonialCardFifteen'; import ContactSplit from '@/components/sections/contact/ContactSplit'; import FooterLogoReveal from '@/components/sections/footer/FooterLogoReveal'; +import CanvasScrollVideo from '@/components/sections/canvas/CanvasScrollVideo'; import { Award, Badge, BookOpen, Brain, DollarSign, Eye, Heart, Layers, Lock, Mail, MessageSquare, Sparkles, Target, TrendingUp, Zap, BarChart3 } from 'lucide-react'; export default function LandingPage() { @@ -67,6 +68,10 @@ export default function LandingPage() { /> +
+ +
+
Date: Tue, 10 Mar 2026 20:01:06 +0000 Subject: [PATCH 2/2] Add src/components/sections/canvas/CanvasScrollVideo.tsx --- .../sections/canvas/CanvasScrollVideo.tsx | 285 ++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 src/components/sections/canvas/CanvasScrollVideo.tsx diff --git a/src/components/sections/canvas/CanvasScrollVideo.tsx b/src/components/sections/canvas/CanvasScrollVideo.tsx new file mode 100644 index 0000000..177b888 --- /dev/null +++ b/src/components/sections/canvas/CanvasScrollVideo.tsx @@ -0,0 +1,285 @@ +"use client"; + +import { useEffect, useRef, useState } from "react"; +import gsap from "gsap"; +import ScrollTrigger from "gsap/ScrollTrigger"; + +gsap.registerPlugin(ScrollTrigger); + +interface Particle { + x: number; + y: number; + vx: number; + vy: number; + size: number; + opacity: number; + hue: number; +} + +const CanvasScrollVideo: React.FC = () => { + const canvasRef = useRef(null); + const containerRef = useRef(null); + const particlesRef = useRef([]); + const scrollProgressRef = useRef(0); + const cameraZRef = useRef(8); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + const ctx = canvas.getContext("2d"); + if (!ctx) return; + + // Set canvas size + const updateCanvasSize = () => { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + }; + updateCanvasSize(); + + // Initialize particles (levitating books with neon glow) + const initializeParticles = () => { + const particles: Particle[] = []; + for (let i = 0; i < 40; i++) { + particles.push({ + x: Math.random() * canvas.width, + y: Math.random() * canvas.height, + vx: (Math.random() - 0.5) * 2, + vy: (Math.random() - 0.5) * 2, + size: Math.random() * 3 + 1, + opacity: Math.random() * 0.7 + 0.3, + hue: Math.random() * 60 + 240 // Purple to violet range + }); + } + particlesRef.current = particles; + }; + initializeParticles(); + + // Draw apartment window frame + const drawApartmentWindow = (progress: number) => { + // Warm sunset-gold background + const goldGrad = ctx.createLinearGradient(0, 0, canvas.width, canvas.height); + goldGrad.addColorStop(0, "#1a1a2e"); // Deep purple + goldGrad.addColorStop(0.5, "#d4a574"); // Warm sunset gold + goldGrad.addColorStop(1, "#2d1b4e"); // Deep purple + ctx.fillStyle = goldGrad; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // Window frame (apartment building window) + const frameWidth = canvas.width * 0.6; + const frameHeight = canvas.height * 0.7; + const frameX = (canvas.width - frameWidth) / 2; + const frameY = (canvas.height - frameHeight) / 2; + + // Outer frame shadow + ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; + ctx.shadowBlur = 20; + ctx.strokeStyle = "#5a3d3d"; + ctx.lineWidth = 8; + ctx.strokeRect(frameX, frameY, frameWidth, frameHeight); + ctx.shadowColor = "transparent"; + + // Window panes + ctx.strokeStyle = "#8b7355"; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(frameX + frameWidth / 2, frameY); + ctx.lineTo(frameX + frameWidth / 2, frameY + frameHeight); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(frameX, frameY + frameHeight / 2); + ctx.lineTo(frameX + frameWidth, frameY + frameHeight / 2); + ctx.stroke(); + + // Inner glass reflection (gradient) + const glassGrad = ctx.createLinearGradient(frameX, frameY, frameX + frameWidth, frameY + frameHeight); + glassGrad.addColorStop(0, "rgba(255, 255, 255, 0.05)"); + glassGrad.addColorStop(0.5, "rgba(255, 255, 255, 0)"); + glassGrad.addColorStop(1, "rgba(212, 165, 116, 0.1)"); + ctx.fillStyle = glassGrad; + ctx.fillRect(frameX, frameY, frameWidth, frameHeight); + }; + + // Draw study room elements + const drawStudyRoom = (progress: number) => { + const roomWidth = canvas.width * 0.5; + const roomHeight = canvas.height * 0.5; + const roomX = (canvas.width - roomWidth) / 2; + const roomY = (canvas.height - roomHeight) / 2; + + // Room background with warm tones + const roomGrad = ctx.createLinearGradient(roomX, roomY, roomX + roomWidth, roomY + roomHeight); + roomGrad.addColorStop(0, "#3d2817"); + roomGrad.addColorStop(0.5, "#6b4226"); + roomGrad.addColorStop(1, "#2a1810"); + ctx.fillStyle = roomGrad; + ctx.fillRect(roomX, roomY, roomWidth, roomHeight); + + // Desk + ctx.fillStyle = "#8b6f47"; + ctx.fillRect(roomX + roomWidth * 0.1, roomY + roomHeight * 0.6, roomWidth * 0.8, roomHeight * 0.3); + ctx.fillStyle = "#5a4a2a"; + ctx.fillRect(roomX + roomWidth * 0.1, roomY + roomHeight * 0.5, roomWidth * 0.8, roomHeight * 0.1); + + // Teenager at desk (simplified silhouette) + ctx.fillStyle = "#1a0a0a"; + // Head + ctx.beginPath(); + ctx.arc(roomX + roomWidth * 0.3, roomY + roomHeight * 0.35, roomHeight * 0.08, 0, Math.PI * 2); + ctx.fill(); + // Torso + ctx.fillRect(roomX + roomWidth * 0.25, roomY + roomHeight * 0.42, roomWidth * 0.1, roomHeight * 0.15); + // Arms + ctx.fillRect(roomX + roomWidth * 0.15, roomY + roomHeight * 0.48, roomWidth * 0.15, roomHeight * 0.04); + }; + + // Draw levitating books with neon glow + const drawLevitatingBooks = (progress: number) => { + const bookPositions = [ + { x: 0.2, y: 0.25, rotation: Math.sin(progress * Math.PI * 2) * 0.3 }, + { x: 0.5, y: 0.15, rotation: Math.cos(progress * Math.PI * 2) * 0.3 }, + { x: 0.75, y: 0.3, rotation: Math.sin(progress * Math.PI * 2 + Math.PI / 3) * 0.3 }, + { x: 0.35, y: 0.05, rotation: Math.cos(progress * Math.PI * 2 + Math.PI / 2) * 0.3 } + ]; + + bookPositions.forEach((pos, idx) => { + const bookX = canvas.width * pos.x; + const bookY = canvas.height * pos.y + Math.sin(progress * Math.PI * 2 + idx) * 15; + + // Book neon glow + ctx.shadowColor = `hsl(${240 + idx * 30}, 100%, 50%)`; + ctx.shadowBlur = 25; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + + // Book shape + ctx.save(); + ctx.translate(bookX, bookY); + ctx.rotate(pos.rotation); + ctx.fillStyle = `hsl(${240 + idx * 30}, 100%, 35%)`; + ctx.fillRect(-20, -30, 40, 60); + ctx.strokeStyle = `hsl(${240 + idx * 30}, 100%, 60%)`; + ctx.lineWidth = 2; + ctx.strokeRect(-20, -30, 40, 60); + ctx.restore(); + }); + + ctx.shadowColor = "transparent"; + }; + + // Draw glowing math symbols + const drawMathSymbols = (progress: number) => { + const symbols = ["∫", "∑", "∞", "π", "√", "≈", "±"]; + symbols.forEach((symbol, idx) => { + const x = canvas.width * (0.15 + (idx * 0.12)); + const y = canvas.height * (0.45 + Math.sin(progress * Math.PI * 2 + idx) * 0.1); + const hue = 270 + idx * 15; + + ctx.save(); + ctx.shadowColor = `hsl(${hue}, 100%, 50%)`; + ctx.shadowBlur = 15; + ctx.font = "italic 28px serif"; + ctx.fillStyle = `hsl(${hue}, 100%, 60%)`; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.globalAlpha = 0.6 + Math.sin(progress * Math.PI * 2 + idx) * 0.3; + ctx.fillText(symbol, x, y); + ctx.restore(); + }); + }; + + // Draw particles (levitating magical elements) + const drawParticles = () => { + particlesRef.current.forEach((particle) => { + particle.x += particle.vx; + particle.y += particle.vy; + + // Bounce off edges + if (particle.x < 0 || particle.x > canvas.width) particle.vx *= -1; + if (particle.y < 0 || particle.y > canvas.height) particle.vy *= -1; + + // Neon glow effect + ctx.shadowColor = `hsl(${particle.hue}, 100%, 50%)`; + ctx.shadowBlur = 10; + ctx.fillStyle = `hsla(${particle.hue}, 100%, 50%, ${particle.opacity})`; + ctx.beginPath(); + ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2); + ctx.fill(); + }); + }; + + // Animation loop + const animate = () => { + const progress = scrollProgressRef.current; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // Draw background and apartment window + drawApartmentWindow(progress); + + // Camera push-in effect (zoom in as progress increases) + ctx.save(); + const zoomLevel = 1 + progress * 0.5; + ctx.translate(canvas.width / 2, canvas.height / 2); + ctx.scale(zoomLevel, zoomLevel); + ctx.translate(-canvas.width / 2, -canvas.height / 2); + + // Draw study room elements + drawStudyRoom(progress); + drawLevitatingBooks(progress); + drawMathSymbols(progress); + drawParticles(); + + ctx.restore(); + + requestAnimationFrame(animate); + }; + + animate(); + + // Setup scroll trigger + if (containerRef.current) { + ScrollTrigger.create({ + trigger: containerRef.current, + onUpdate: (self) => { + scrollProgressRef.current = self.progress; + } + }); + } + + // Handle window resize + const handleResize = () => { + updateCanvasSize(); + }; + window.addEventListener("resize", handleResize); + + return () => { + window.removeEventListener("resize", handleResize); + ScrollTrigger.getAll().forEach((trigger) => trigger.kill()); + }; + }, []); + + return ( +
+ + {/* Optional: Add decorative elements around the canvas */} +
+
+
+
+ ); +}; + +export default CanvasScrollVideo; \ No newline at end of file -- 2.49.1