Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | 1x 92x 92x 92x 92x 92x 92x 92x 92x 92x 92x 92x 92x 92x 92x 56x 56x 49x 27x 27x 27x 27x 56x 56x 153x 153x 153x 153x 153x 153x 78x 78x 402x 330x 72x 72x 72x 330x 78x 56x 56x 288x 232x 232x 56x 56x 56x 232x 56x | import { Component, ElementRef, Input, OnChanges, Renderer2, SimpleChanges, ViewChild, } from '@angular/core'; import { Observable, forkJoin, of } from 'rxjs'; /** * Component used to display an array of strings with a typing animation, as if * the text was typed, then erased, the the next text was typed, then erased, * and so forth. * * Inspired by * https://medium.com/swlh/an-infinite-type-and-delete-animation-in-angular-10-fc38d87d08ec */ @Component({ selector: 'app-typed-animated-text', templateUrl: './typed-animated-text.component.html', styleUrls: ['./typed-animated-text.component.css'], standalone: true, }) export class TypedAnimatedTextComponent implements OnChanges { /** The text element */ @ViewChild('textElement') textElement!: ElementRef; /** The line element that will blink as the cursor at the end of the line does */ @ViewChild('blinkElement') blinkElement!: ElementRef; /** * Array of `Observable<string>` containing the different texts to be * displayed, one after the other used as input. */ @Input() inputArray: Observable<string>[] = [of('')]; /** * Array of string containing the texts to be displayed. Artifact of the * creation of this component in my pure HTML/CSS/JS template. This could be * changed to only require arrays of observables and using pipes or other * tools on observables to get the same results. It might complicate the code * though. */ textArray: string[] = []; /** Text color */ @Input() textColor = 'black'; /** Font size */ @Input() fontSize = '1em'; /** Blinking line width */ @Input() blinkWidth = '2px'; /** Typing speed (in ms) */ @Input() typingSpeed = 80; /** Deleting speed (in ms) */ @Input() deleteSpeed = 30; /** This animation is on loop */ @Input() loop = true; /** Delay between end of typing and beginning of deleting (in ms) */ @Input() deleteDelay = 1100; /** Index of the current string being either displayed or erased */ private i = 0; /** Stores whether or not the animation has started */ private isTyping = false; /** * Typed animation text component constructor * * @param renderer The `Renderer` */ constructor(private renderer: Renderer2) { setTimeout(() => { this.initVariables(); }, 0); } /** * If the array of observable changes, the array of string has to be updated * too. */ ngOnChanges(changes: SimpleChanges): void { const change = changes['inputArray']; if (change && change.currentValue != change.previousValue) { forkJoin(this.inputArray).subscribe({ next: (r) => { this.textArray = r; if (!this.isTyping) { this.typingEffect(); this.isTyping = true; } }, }); } if (this.textElement) { this.initVariables(); } } /** Style setup. This could be done with binding in HTML */ initVariables(): void { this.renderer.setStyle( this.textElement.nativeElement, 'color', this.textColor ); this.renderer.setStyle( this.textElement.nativeElement, 'font-size', this.fontSize ); this.renderer.setStyle(this.textElement.nativeElement, 'padding', '0.1em'); this.renderer.setStyle( this.blinkElement.nativeElement, 'border-right-width', this.blinkWidth ); this.renderer.setStyle( this.blinkElement.nativeElement, 'border-right-color', this.textColor ); this.renderer.setStyle( this.blinkElement.nativeElement, 'font-size', this.fontSize ); } /** * Typing effect : Types the string letter after letter with {@link typinSpeed} * delay. Once the string is totally written, call {@link deletingEffect}. */ typingEffect(): void { const word = this.textArray[this.i].split(''); const loopTyping = () => { if (word.length > 0) { this.textElement.nativeElement.innerHTML += word.shift(); } else { setTimeout(() => { this.deletingEffect(); }, this.deleteDelay); return; } setTimeout(loopTyping, this.typingSpeed); }; loopTyping(); } /** * Deleting effect : Deletes the string letter after letter with * {@link deleteSpeed} delay. Once the string is totally removed, type again * after having changed the index of the current element being display * {@link i} */ deletingEffect(): void { const word = this.textArray[this.i].split(''); const loopDeleting = () => { if (word.length > 0) { word.pop(); this.textElement.nativeElement.innerHTML = word.join(''); } else { this.i = this.textArray.length > this.i + 1 ? this.i + 1 : 0; this.typingEffect(); return; } setTimeout(loopDeleting, this.deleteSpeed); }; loopDeleting(); } } |