From 4f17b07f70fbfa5223169e4dba38ce969998c862 Mon Sep 17 00:00:00 2001
From: AdamBtech <60339324+AdamBtech@users.noreply.github.com>
Date: Fri, 23 May 2025 19:33:22 +0200
Subject: [PATCH] Added scramble animation on mount of section title component
---
.../section-title.component.html | 42 ++++---
.../section-title/section-title.component.ts | 106 +++++++++++++++++-
2 files changed, 129 insertions(+), 19 deletions(-)
diff --git a/src/app/shared/ui/section-title/section-title.component.html b/src/app/shared/ui/section-title/section-title.component.html
index a6198e2..fd9413c 100644
--- a/src/app/shared/ui/section-title/section-title.component.html
+++ b/src/app/shared/ui/section-title/section-title.component.html
@@ -1,19 +1,27 @@
-
- {{title()}}
-
-
-
- {{title()}}
-
-
-
- {{title()}}
-
+
+
+ {{title()}}
+
+
+
+
+ {{title()}}
+
+
+
+
+ {{title()}}
+
diff --git a/src/app/shared/ui/section-title/section-title.component.ts b/src/app/shared/ui/section-title/section-title.component.ts
index c0786cc..a693e39 100644
--- a/src/app/shared/ui/section-title/section-title.component.ts
+++ b/src/app/shared/ui/section-title/section-title.component.ts
@@ -1,4 +1,11 @@
-import { Component, input } from '@angular/core';
+import {
+ Component,
+ input,
+ OnInit,
+ OnDestroy,
+ ElementRef,
+ ViewChild,
+} from '@angular/core';
@Component({
selector: 'app-section-title',
@@ -6,6 +13,101 @@ import { Component, input } from '@angular/core';
templateUrl: './section-title.component.html',
styleUrl: './section-title.component.css',
})
-export class SectionTitleComponent {
+export class SectionTitleComponent implements OnInit, OnDestroy {
+ @ViewChild('mainText', { static: true })
+ mainTextRef!: ElementRef;
+ @ViewChild('shadow1', { static: true }) shadow1Ref!: ElementRef;
+ @ViewChild('shadow2', { static: true }) shadow2Ref!: ElementRef;
+
title = input();
+
+ private animationFrame?: number;
+ private timeoutIds: number[] = [];
+
+ ngOnInit() {
+ // Start animation after component initializes
+ setTimeout(() => this.startScrambleAnimation(), 100);
+ }
+
+ ngOnDestroy() {
+ if (this.animationFrame) {
+ cancelAnimationFrame(this.animationFrame);
+ }
+ this.timeoutIds.forEach((id) => clearTimeout(id));
+ }
+
+ private startScrambleAnimation() {
+ const titleText = this.title() || '';
+ if (!titleText) return;
+
+ const duration = 2000; // Total animation duration
+ const letterDelay = 80; // Delay between each letter reveal
+
+ this.simultaneousScrambleAndReveal(titleText, duration, letterDelay);
+ }
+
+ private simultaneousScrambleAndReveal(
+ targetText: string,
+ totalDuration: number,
+ letterDelay: number,
+ ) {
+ const startTime = Date.now();
+ const scrambleChars = '01';
+ const revealedLetters = new Array(targetText.length).fill(false);
+ const letterBinaryStates = new Array(targetText.length)
+ .fill(null)
+ .map(() => (Math.random() > 0.5 ? '0' : '1'));
+
+ // Schedule letter reveals
+ for (let i = 0; i < targetText.length; i++) {
+ if (targetText[i] !== ' ') {
+ const timeoutId = setTimeout(() => {
+ revealedLetters[i] = true;
+ }, i * letterDelay);
+ this.timeoutIds.push(timeoutId);
+ } else {
+ revealedLetters[i] = true; // Spaces are always "revealed"
+ }
+ }
+
+ const animate = () => {
+ const elapsed = Date.now() - startTime;
+
+ if (elapsed < totalDuration) {
+ let displayText = '';
+
+ for (let i = 0; i < targetText.length; i++) {
+ if (targetText[i] === ' ') {
+ displayText += ' ';
+ } else if (revealedLetters[i]) {
+ displayText += targetText[i];
+ } else {
+ // Each position gets its own random binary that changes
+ letterBinaryStates[i] = Math.random() > 0.5 ? '0' : '1';
+ displayText += letterBinaryStates[i];
+ }
+ }
+
+ this.updateTextContent(displayText);
+ this.animationFrame = requestAnimationFrame(animate);
+ } else {
+ // Final state - show complete text
+ this.updateTextContent(targetText);
+ }
+ };
+
+ animate();
+ }
+
+ private updateTextContent(text: string) {
+ if (this.mainTextRef?.nativeElement) {
+ this.mainTextRef.nativeElement.textContent = text;
+ }
+ if (this.shadow1Ref?.nativeElement) {
+ this.shadow1Ref.nativeElement.textContent = text;
+ }
+ if (this.shadow2Ref?.nativeElement) {
+ this.shadow2Ref.nativeElement.textContent = text;
+ }
+ }
}