From 710565361e8da8ae4b2a74c6e17f1874fb065733 Mon Sep 17 00:00:00 2001 From: Mehmet Volkan Date: Fri, 18 Apr 2025 04:00:55 +0200 Subject: [PATCH 01/30] =?UTF-8?q?=F0=9F=9A=80=20Deployment=20(#9)=20(#10)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(infra): Enhance infrastructure setup with Terraform and CI/CD integration - Add Terraform configuration for AWS resources including S3, CloudFront, and IAM - Implement GitHub Actions workflow for automated deployment to staging and production - Update README with project overview and infrastructure details - Extend .gitignore to include Terraform and AWS credential files * fix(experience): update background image path in experience page * fix(experience): change background image format from PNG to JPG in experience page * fix(nuxt.config): remove unused Poppins font family from Google Fonts configuration * fix(deploy): update S3 bucket selection logic for deployment based on branch * feat(deploy): add Terraform setup and CloudFront cache control to deployment workflow - Introduced Terraform setup step in GitHub Actions for consistent infrastructure management - Added cache control settings in CloudFront distribution to optimize content delivery - Updated S3 sync logic to ensure proper deployment based on branch * feat(nuxt.config): add runtime config for staging environment and update sitemap logic - Introduced runtime configuration to manage environment variables, specifically for staging - Added noindex meta tag for staging environment to prevent indexing by search engines - Updated sitemap generation to exclude URLs in staging - Created a new robots configuration file for better management of bot access - Implemented middleware to block search engine bots from accessing the staging environment * feat(deploy): enhance CloudFront cache invalidation logic in deployment workflow - Updated deployment workflow to dynamically retrieve CloudFront distribution IDs based on the domain - Improved error handling for cache invalidation by checking if the distribution ID exists - Enhanced Terraform output structure to include both domain names and IDs for better clarity * feat(deploy): improve CloudFront output handling in deployment workflow - Added debugging steps to retrieve and inspect CloudFront outputs from Terraform - Enhanced extraction of distribution ID with explicit JSON parsing - Improved error handling for cases where the distribution ID is not found or is null * feat(deploy): refine CloudFront output structure and ID extraction in deployment workflow - Updated Terraform output to include domain names and IDs with a new key format - Enhanced ID extraction logic in deployment workflow for better clarity and debugging - Removed redundant outputs.tf file to streamline configuration * feat(deploy): enhance Terraform initialization and output handling in deployment workflow - Added Terraform initialization and apply steps to ensure proper state management - Improved error handling for CloudFront output retrieval and JSON parsing - Added checks for output file existence and content before processing * feat(deploy): update Terraform state management in deployment workflow - Replaced Terraform apply step with refresh to ensure the state is up-to-date - Improved error handling to allow workflow continuation despite refresh failures * feat(deploy): enhance CloudFront distribution ID retrieval in deployment workflow - Streamlined the process of finding CloudFront distribution IDs by querying AWS directly - Added fallback logic to match distributions by origin domain name if alias matching fails - Improved debugging output to list all distributions when no match is found * feat(deploy): enhance CloudFront distribution ID fallback logic in deployment workflow - Improved handling of null values in Aliases and Origins when retrieving CloudFront distribution IDs - Added additional fallback mechanism to match distributions by S3 bucket name if previous attempts fail - Enhanced debugging output to provide clearer information on distribution listings and fallback actions * feat(deploy): enhance CloudFront distribution ID retrieval and debugging in deployment workflow - Improved debugging output to list all CloudFront distributions with detailed information for better visibility - Enhanced fallback logic to match distributions by S3 website endpoint and bucket name - Added a check for the number of distributions found to streamline the selection process * feat(deploy): simplify CloudFront distribution retrieval and improve error handling in deployment workflow - Removed extensive debugging output for CloudFront distributions to streamline the process - Enhanced logic to find distribution ID by matching S3 bucket name directly - Improved error handling for cache invalidation, providing clearer messages for permission issues * feat(deploy): enhance CloudFront cache invalidation process and output clarity * feat(app): enhance page transition handling and add transition fixes - Introduced onMounted lifecycle hook to manage content visibility and transition readiness - Added logic to re-trigger animations after a delay for smoother transitions - Created a new fixes.scss file to address transition and animation issues across various elements - Updated main.scss to import the new fixes for improved styling consistency * refactor(styles): remove deprecated fixes.scss file and update main.scss * fix(AppHero): replace NuxtImg with standard img tag and adjust background transform for improved layout * refactor(AppHero): restructure template and styles for improved layout and animation - Updated the AppHero component template to enhance structure and readability - Introduced a new grid layout for background boxes with fade-in animation - Adjusted padding in the index page for better visual alignment * style(AppHero): update text element and adjust font styles for consistency - Replaced tag with for better semantic structure - Updated font size to 1.25rem and added margin-top for improved layout * refactor(README, AppHero, AppFooter, index): update infrastructure documentation and improve component styles - Revised README to enhance clarity on infrastructure features and setup - Adjusted styles in AppFooter for better spacing - Restructured AppHero component layout and styles for improved visual consistency - Updated index page styles to enhance background and grid layout * refactor(AppHero): comment out unused elements for cleaner template * refactor(AppHero): enhance mobile detection and optimize rendering performance - Implemented client-side mobile detection with a resize listener for dynamic updates - Introduced deferred rendering for boxes to improve loading performance after critical content - Lazy-loaded video modal content to enhance user experience - Optimized box generation logic and CSS for better performance and reduced complexity From 9c95614c6aad80754d70c6354d86a2fb461f9fc4 Mon Sep 17 00:00:00 2001 From: Deveci Date: Fri, 18 Apr 2025 19:34:26 +0200 Subject: [PATCH 02/30] Implement canvas-based matrix animation in AppHero component - Introduced a new matrix animation effect using a canvas for improved performance and visual appeal. - Added a character set and words for the animation, enhancing the dynamic display. - Refactored the layout to accommodate the new animation, including CSS adjustments for better responsiveness and aesthetics. - Cleaned up existing code and optimized rendering logic for a smoother user experience. --- components/AppHero.vue | 495 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 483 insertions(+), 12 deletions(-) diff --git a/components/AppHero.vue b/components/AppHero.vue index 4d1a187..40ac0f7 100644 --- a/components/AppHero.vue +++ b/components/AppHero.vue @@ -145,17 +145,414 @@ const openVideoModal = () => { }); }; -// Optimized box generation -const boxCount = computed(() => (isMobile.value ? 24 : 70)); +// Canvas-based matrix animation +const matrixCanvasRef = ref(null); +const matrixAnimationStarted = ref(false); +let animationFrameId = null; +let matrixCanvas = null; +let matrixCtx = null; + +// Matrix character set +const matrixChars = '01'.split(''); + +// Matrix character set +const matrixWords = [ + // Frontend Frameworks & Libraries + 'VUE', + 'NUXT', + 'NEXT', + 'REACT', + 'VITE', + 'WEBPACK', + 'BABEL', + 'TYPESCRIPT', + 'JAVASCRIPT', + 'HTML', + 'CSS', + 'SCSS', + 'TAILWIND', + 'VUEX', + 'GRAPHQL', + + // Backend & Servers + 'NODE', + 'EXPRESS', + 'NEST', + 'SYMFONY', + 'LARAVEL', + 'PHP', + 'GO', + 'POSTGRESQL', + 'MYSQL', + 'DOCKER', + 'KUBERNETES', + + // Cloud & DevOps + 'AWS', + 'EC2', + 'S3', + 'LAMBDA', + 'CLOUDFRONT', + 'FIREBASE', + 'NETLIFY', + 'VERCEL', + 'HEROKU', + 'GITHUB', + 'GITLAB', + 'BITBUCKET', + 'CI/CD', + 'JENKINS', + 'TERRAFORM', + 'GRAFANA', + + // Design & UX + 'UI', + 'UX', + 'FIGMA', + 'SKETCH', + 'ADOBE', + 'PHOTOSHOP', + 'WIREFRAME', + 'PROTOTYPE', + 'ACCESSIBILITY', + 'A11Y', + 'RESPONSIVE', + 'MOBILE', + + // General Programming + 'STAGING', + 'BUILD', + 'TEST', + 'DEBUG', + 'DEPLOY', + 'RELEASE', + 'AGILE', + 'SCRUM', + 'KANBAN', + 'JIRA', + 'API', + 'REST', + 'CLEAN', + 'SOLID', + 'PATTERNS', + 'ALGORITHMS', + 'DATA', + + // Performance & Optimization + 'OPTIMIZE', + 'CACHE', + 'LAZY', + 'BUNDLE', + 'MINIFY', + 'COMPRESS', + 'PERFORMANCE', + 'METRICS', + 'LIGHTHOUSE', + 'WEBVITALS', + 'LCP', + 'FID', + 'CLS', + 'SEO', + 'PWA', + + // Data & Analytics + 'ANALYTICS', + 'AI', + 'VISUALIZATION', + 'D3', + 'CHART', + 'DASHBOARD', + 'LOGGING', + 'MONITOR', + 'TRACK' +]; +const drops = []; + +// Initialize the drops +const initDrops = () => { + if (!matrixCanvas) return; + + // Calculate how many drops to add based on width + const fontSize = isMobile.value ? 10 : 14; + const columns = Math.floor((matrixCanvas.width / fontSize) * 1.2); // Increase density of columns + + // Reset drops array + drops.length = 0; + + // Create initial drops + for (let i = 0; i < columns; i++) { + // Random starting y position + drops.push({ + x: i * fontSize * 2 + Math.random() * fontSize, // Reduce spacing between words + y: Math.random() * matrixCanvas.height, + speed: 0.5 + Math.random() * 1.5, + opacity: 0.03 + Math.random() * 0.15, // Lower overall opacity + word: matrixWords[Math.floor(Math.random() * matrixWords.length)], + length: Math.floor(2 + Math.random() * 5), // Fewer trailing characters + chars: [] + }); + + // Generate trailing characters for this drop + for (let j = 0; j < drops[i].length; j++) { + drops[i].chars.push({ + char: drops[i].word[Math.floor(Math.random() * drops[i].word.length)], + opacity: j === 0 ? 0.9 : (1 - j / drops[i].length) * 0.7 + }); + } + } +}; + +// The animation loop +const startMatrixAnimation = () => { + if (!matrixCtx || !matrixCanvas) return; + + // More thorough clearing to prevent trails + matrixCtx.globalCompositeOperation = 'source-over'; + matrixCtx.fillStyle = 'rgba(2, 6, 23, 0.15)'; // Increased opacity for better clearing + matrixCtx.fillRect(0, 0, matrixCanvas.width, matrixCanvas.height); + + const fontSize = isMobile.value ? 10 : 14; + + // Draw each drop + drops.forEach((drop, _) => { + // Skip drops without proper initialization + if (!drop || !drop.word || !drop.chars || !Array.isArray(drop.chars)) return; + + // Move drop down by its speed + drop.y += drop.speed; + + // Draw the main word at the head of the drop with lighter green + matrixCtx.font = `bold ${fontSize}px monospace`; + // Clear compositing to prevent white halos + matrixCtx.globalCompositeOperation = 'source-over'; + // Use a lighter shade of green with lower opacity + matrixCtx.fillStyle = `rgba(120, 230, 160, ${drop.opacity * 0.8})`; // Lighter green with reduced opacity + matrixCtx.fillText(drop.word, drop.x, drop.y); + + // Draw trailing characters + drop.chars.forEach((charObj, j) => { + // Skip undefined char objects + if (!charObj || typeof charObj !== 'object') return; + + // Create trailing effect with fading CTA color + const trailY = drop.y - (j + 1) * fontSize; + + if (trailY > 0 && trailY < matrixCanvas.height) { + // Trailing characters have fading opacity and color transition from CTA to white + const opacity = (1 - j / drop.length) * drop.opacity * 0.5; // Lower opacity for trails + + if (j < 2) { + // First trailing characters with lighter green + matrixCtx.globalCompositeOperation = 'source-over'; + matrixCtx.fillStyle = `rgba(140, 245, 180, ${opacity})`; + } else { + // Rest fade to very light cyan + matrixCtx.globalCompositeOperation = 'source-over'; + matrixCtx.fillStyle = `rgba(160, 245, 200, ${opacity})`; + } + + matrixCtx.font = `${fontSize}px monospace`; + matrixCtx.fillText(charObj.char, drop.x + (Math.random() > 0.5 ? fontSize / 4 : 0), trailY); + + // Occasionally change character + if (Math.random() > 0.92) { + if (drop.word && drop.word.length > 0) { + charObj.char = drop.word[Math.floor(Math.random() * drop.word.length)]; + } + } + } + }); + + // Reset drop when it goes off screen + if (drop.y > matrixCanvas.height + fontSize) { + // Move drop far above viewport to prevent trails + drop.y = -fontSize * 4; // Position higher above the viewport + drop.speed = 0.5 + Math.random() * 1.5; + drop.opacity = 0.03 + Math.random() * 0.15; // Lower overall opacity + drop.word = matrixWords[Math.floor(Math.random() * matrixWords.length)]; + + // Ensure length is properly set + if (!drop.length || drop.length < 1) { + drop.length = Math.floor(2 + Math.random() * 5); + } + + // Clear and regenerate trailing characters + drop.chars = []; + for (let j = 0; j < drop.length; j++) { + if (drop.word && drop.word.length > 0) { + drop.chars.push({ + char: drop.word[Math.floor(Math.random() * drop.word.length)], + opacity: j === 0 ? 0.9 : (1 - j / drop.length) * 0.7 + }); + } + } + } + }); + + // Occasionally add a new fast "highlight" drop + if (Math.random() > 0.99 && drops.length < (matrixCanvas.width / fontSize) * 1.2) { + const fontSize = isMobile.value ? 10 : 14; + const x = Math.floor((Math.random() * matrixCanvas.width) / fontSize) * fontSize; + + // Check if there's already a drop at this x position + const existingDropIndex = drops.findIndex((d) => d.x === x); + + if (existingDropIndex === -1) { + // Add a new bright drop + const newDrop = { + x, + y: -5 * fontSize, + speed: 2 + Math.random() * 3, + opacity: 0.7 + Math.random() * 0.3, + length: Math.floor(4 + Math.random() * 8), + chars: [] + }; + + // Generate characters for this drop + for (let j = 0; j < newDrop.length; j++) { + newDrop.chars.push({ + char: matrixChars[Math.floor(Math.random() * matrixChars.length)], + opacity: j === 0 ? 1 : (1 - j / newDrop.length) * 0.9 + }); + } + + drops.push(newDrop); + } + } + + // Occasionally add a new fast "highlight" drop with a full word + if (Math.random() > 0.97 && drops.length < matrixCanvas.width / fontSize) { + // Increased probability and allowed density + const x = Math.floor((Math.random() * matrixCanvas.width) / fontSize) * fontSize; + + // Check if there's already a drop near this x position (with smaller distance check) + const existingDropIndex = drops.findIndex((d) => Math.abs(d.x - x) < fontSize * 2); + + if (existingDropIndex === -1) { + // Add a new bright word drop + const newWord = matrixWords[Math.floor(Math.random() * matrixWords.length)]; + const newDrop = { + x, + y: -5 * fontSize, + speed: 2 + Math.random() * 3, + opacity: 0.04 + Math.random() * 0.12, // Much lower opacity + word: newWord, + length: Math.floor(2 + Math.random() * 4), + chars: [] + }; + + // Generate trailing characters for this drop + for (let j = 0; j < newDrop.length; j++) { + newDrop.chars.push({ + char: newWord[Math.floor(Math.random() * newWord.length)], + opacity: j === 0 ? 1 : (1 - j / newDrop.length) * 0.9 + }); + } + + drops.push(newDrop); + } + } + + // Continue animation loop + animationFrameId = requestAnimationFrame(startMatrixAnimation); +}; + +// Matrix digital rain effect setup +onMounted(() => { + // Render boxes after LCP is done + setTimeout(() => { + isBoxesRendered.value = true; + }, 1000); + + // Initialize matrix animation after boxes are rendered + setTimeout(() => { + matrixAnimationStarted.value = true; + initMatrixAnimation(); + }, 1500); + + // Keyboard event listeners + window.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && isModalOpen.value) { + closeModal(); + } + }); +}); + +// Initialize the matrix animation +const initMatrixAnimation = () => { + if (!isBoxesRendered.value) return; + + // Only set up canvas once + nextTick(() => { + matrixCanvas = matrixCanvasRef.value; + if (!matrixCanvas) return; + + // Create canvas context with settings to prevent white background artifacts + matrixCtx = matrixCanvas.getContext('2d', { + alpha: false, + willReadFrequently: false, + desynchronized: true + }); + + // Disable text anti-aliasing for sharper text + matrixCtx.imageSmoothingEnabled = false; + + // Initial full clear of the canvas + matrixCtx.fillStyle = 'rgb(2, 6, 23)'; // navyBlue + matrixCtx.fillRect(0, 0, matrixCanvas.width, matrixCanvas.height); + + // Set canvas dimensions to match container + const hero = document.getElementById('hero'); + if (hero) { + matrixCanvas.width = hero.offsetWidth; + matrixCanvas.height = hero.offsetHeight; + } else { + matrixCanvas.width = window.innerWidth; + matrixCanvas.height = window.innerHeight; + } + + // Add resize listener + window.addEventListener('resize', () => { + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(() => { + isMobile.value = window.innerWidth < 1024; + + // Resize canvas + if (matrixCanvas) { + if (hero) { + matrixCanvas.width = hero.offsetWidth; + matrixCanvas.height = hero.offsetHeight; + } else { + matrixCanvas.width = window.innerWidth; + matrixCanvas.height = window.innerHeight; + } + + // Reset drops when resizing + initDrops(); + } + }, 250); + }); + + // Start matrix animation + initDrops(); + startMatrixAnimation(); + }); +}; + +// Clean up animation on component unmount +onBeforeUnmount(() => { + if (animationFrameId !== null) { + cancelAnimationFrame(animationFrameId); + } +});