Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert angularjs directive to angular 10

I want to use this code in my angular project:

'use strict';

/**
 * Floating text animation (random)
 */
angular.module('g1b.text-animation', []).
directive('textAnimation', ['$document', '$interval', '$timeout', function ($document, $interval, $timeout) {
  return {
    restrict: 'A',
    compile: function () {
      return {
        pre: function () {},
        post: function (scope, element) {
          var chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
          $interval(function () {
            for ( var i = 0; i < Math.floor(Math.random() * 5); i++ ) {
              var character = chars[Math.floor(Math.random() * chars.length)];
              var duration = Math.floor(Math.random() * 15);
              var offset = Math.floor(Math.random() * (45 - duration * 3)) + 3;
              var size = 12 + (15 - duration);
              var span = angular.element('<span class="animated-text" style="right:'+offset+'vw; font-size: '+size+'px; animation-duration:'+duration+'s">'+character+'</span>');
              element.append(span);
              $timeout(function (span) {
                span.remove();
              }, duration * 1000, false, span);
            }
          }, 250);
        }
      };
    }
  };
}]);

it also has a CSS file with it. this code is basically a text animation. my problem is I don't know where to start.

this is what I'm trying to achieve: https://rawgit.com/g1eb/angular-text-animation/master/

this is the npm of it: https://github.com/g1eb/angular-text-animation

Update:

I've tried it on my own and this is what I have:

@ViewChildren('styleDiv', {read: ElementRef}) children: QueryList<ElementRef>;

  constructor(private renderer: Renderer2, private host: ElementRef) {
  }

  ngOnInit(): void {
  }

  ngAfterViewInit(): void {
    this.animateBackground();
  }

  private animateBackground(): void {
    const renderer = this.renderer;
    const children = this.children;
    const host = this.host;
    const chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
      'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
    setInterval(() => {
      for (let i = 0; i < Math.floor(Math.random() * 5); i++) {
        const character = chars[Math.floor(Math.random() * chars.length)];
        const duration = Math.floor(Math.random() * 15);
        const offset = Math.floor(Math.random() * (45 - duration * 3)) + 3;
        const size = 12 + (15 - duration);
        const span = '<span class="animated-text" style="right:' + offset + 'vw; font-size: ' +
          +size + 'px; animation-duration:' + duration + 's">' + character + '</span>';
        this.children.first.nativeElement.insertAdjacentHTML('beforeend', span);
        setTimeout(() => {
          // renderer.removeChild(children.first.nativeElement.parentNode, children.first.nativeElement);
        }, duration * 1000, false, host, children, renderer);
      }
    }, 250, host, children, renderer);
  }

it works, but I do have a problem inside the set timeout function. I'm able to add the span to the dom, but not able to remove it.

like image 644
Liraz Shaka Amir Avatar asked Jul 07 '26 10:07

Liraz Shaka Amir


2 Answers

After talking with the guy who wrote this directive we decided I would write a new directive for angular 2+ and he should approve the pull request in the next couple of days. the new code should be available soon in his repository: https://github.com/g1eb/angular-text-animation

but for now, here is the new directive:

import {AfterViewInit, Directive, ElementRef, Input, Renderer2} from '@angular/core';

@Directive({
  selector: '[sbzTextAnimation]'
})
export class NgxSbzTextAnimationDirective implements AfterViewInit {
  @Input() maxFontSize = 20;
  @Input() colorSchemeArray: string[];
  @Input() position: 'left' | 'right' = 'right';

  constructor(private elementRef: ElementRef, private renderer: Renderer2) {
  }

  ngAfterViewInit(): void {
    this.init();
    this.animateBackground();
  }

  private init(): void {
    this.colorSchemeArray = this.colorSchemeArray
      ? this.colorSchemeArray
      : [
        '#63b598', '#ce7d78', '#ea9e70', '#a48a9e', '#c6e1e8', '#648177', '#0d5ac1',
        '#f205e6', '#1c0365', '#14a9ad', '#4ca2f9', '#a4e43f', '#d298e2', '#6119d0',
        '#d2737d', '#c0a43c', '#f2510e', '#651be6', '#79806e', '#61da5e', '#cd2f00',
        '#9348af', '#01ac53', '#c5a4fb', '#996635', '#b11573', '#4bb473', '#75d89e',
        '#2f3f94', '#2f7b99', '#da967d', '#34891f', '#b0d87b', '#ca4751', '#7e50a8',
      ];
  }

  private animateBackground(): void {
    // need to access them in the setTimeout function
    const renderer = this.renderer;
    const elementRef = this.elementRef;

    const chars = [...Array(26)].map((e, i) => (i + 10).toString(36));

    setInterval(() => {
      for (let i = 0; i < Math.floor(Math.random() * 5); i++) {
        const duration = Math.floor(Math.random() * 15);
        const offset = Math.floor(Math.random() * (45 - duration * 3)) + 3;
        const size = 12 + (this.maxFontSize - duration);
        const color = this.colorSchemeArray[Math.floor(Math.random() * this.colorSchemeArray.length)];

        const span = renderer.createElement('span');
        span.innerText = chars[Math.floor(Math.random() * chars.length)];
        renderer.addClass(span, 'animated-text');

        renderer.setStyle(span, 'color', color);
        renderer.setStyle(span, this.position, `${offset}vw`);
        renderer.setStyle(span, 'font-size', `${size}px`);
        renderer.setStyle(span, 'animation-duration', `${duration}s`);
        renderer.setStyle(span, 'color', color);

        renderer.appendChild(elementRef.nativeElement, span);
        setTimeout(() => {
          renderer.removeChild(elementRef.nativeElement, elementRef.nativeElement.firstChild);
        }, duration * 1000, false, elementRef, renderer);
      }
    }, 250);
  }
}

I have also made some changes:

  1. the text is now rainbow colors.
  2. added an input for max font size to be customizable use case: [maxFontSize]="30"
  3. added an input for rainbow colors. if the user only wants one color he can pass one color or different scheme like this: [colorSchemeArray]="['#000"]
  4. added an input for the position of the floating text in the background.

this is the CSS for the directive (should be in the CSS file of the component using the directive):

/**
 * Floating text animation styles
 */
.animated-text {
  cursor: default;
  color: #68C2A3;
  font-family: 'VT323', monospace, sans-serif;
  text-shadow: 0 0 1px #ffffff;
  font-size: 24px;
  position: fixed;
  top: -50px;
  user-select: none;
  -ms-user-select: none;
  -moz-user-select: none;
  -khtml-user-select: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  animation-name: float-animation;
  animation-timing-function: ease-out;
}

@keyframes float-animation {
  0% {
    top: 100vh;
    opacity: 0;
  }
  25% {
    opacity: 1;
  }
  50% {
    opacity: 1;
  }
  75% {
    opacity: 0;
  }
  100% {
    top: -25px;
    opacity: 0;
  }
}

here is a full working demo: https://stackblitz.com/edit/floating-text-animation?file=src/app/app.component.html

Update: here is the link to the final directive on npm: https://www.npmjs.com/package/ngx-sbz-text-animation

like image 137
Liraz Shaka Amir Avatar answered Jul 09 '26 02:07

Liraz Shaka Amir


you can use a rxjs as well with scheduler which does smooth out the animation a little.

 interval(250)
   .pipe(
    // tap(_ => console.log("started..")),
    mergeMap(_ =>
      from([...Array(Math.floor(Math.random() * 5))]).pipe(
        observeOn(asyncScheduler)
      )
    ),
    map(_ => this.getMap()),
    tap(({ span }) =>
      this.elementRef.nativeElement.insertAdjacentHTML("beforeend", span)
    ),
    mergeMap(({ duration }) =>
      of(null).pipe(
        delay(duration * 1000),
        tap(_ =>
          renderer.removeChild(
            this.elementRef.nativeElement,
            this.elementRef.nativeElement.firstChild
          )
        )
      )
    ),
    takeUntil(this._destroy$)
  )
  .subscribe();

added a method:

getMap(): { span: string; duration: number } {
  const character = this.chars[Math.floor(Math.random() * this.chars.length)];
  const duration = Math.floor(Math.random() * 15);
  const offset = Math.floor(Math.random() * (45 - duration * 3)) + 3;
  const size = 12 + (this.maxFontSize - duration);
  const color = this.colorSchemeArray[
    Math.floor(Math.random() * this.colorSchemeArray.length)
  ];
  return {
    duration,
    span: `<span class="animated-text" style="color: ${color};${
      this.position
    }: ${offset}vw; font-size: ${size}px; animation-duration:${
     duration}s">${character}</span>`
};

}

make a subject _destory$ and OnDestroy method: to use in takeUntil to for garbage collection the scheduler I used was asyncScheduler or animationFrameScheduler. animationFrameScheduler is better, but removal needs to be tweaked

like image 42
mr.vea Avatar answered Jul 09 '26 03:07

mr.vea



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!