1

I'm developing a web using Angular CLI. I'm trying to applying similar things like this :

https://codepen.io/chrisdoble/pen/WQLLVp

Is there any way to write plain javascript in component.ts.

Here is my JS :

    var image = document.getElementById('hero-bg');
    var imageCanvas = document.createElement('canvas');
    var imageCanvasContext = imageCanvas.getContext('2d');
    var lineCanvas = document.createElement('canvas');
    var lineCanvasContext = lineCanvas.getContext('2d');
    var pointLifetime = 1000;
    var points = [];
    var newImage = document.getElementById('hero')

    if (image.complete) {
        start();
    } else {
        image.onload = start;
    }

    function start() {
        document.addEventListener('mousemove', onMouseMove);
        window.addEventListener('resize', resizeCanvases);
        newImage.appendChild(imageCanvas);
        resizeCanvases();
        tick();
    }

    function onMouseMove(event) {
        points.push({
            time: Date.now(),
            x: event.clientX,
            y: event.clientY
        });
    }

    function resizeCanvases() {
        imageCanvas.width = lineCanvas.width = window.innerWidth;
        imageCanvas.height = lineCanvas.height = window.innerHeight;
    }

    function tick() {
        // Remove old points
        points = points.filter(function(point) {
            var age = Date.now() - point.time;
            return age < pointLifetime;
        });

        drawLineCanvas();
        drawImageCanvas();
        requestAnimationFrame(tick);
    }
    function drawLineCanvas() {
        var minimumLineWidth = 25;
        var maximumLineWidth = 100;
        var lineWidthRange = maximumLineWidth - minimumLineWidth;
        var maximumSpeed = 50;

        lineCanvasContext.clearRect(0, 0, lineCanvas.width, lineCanvas.height);
        lineCanvasContext.lineCap = 'round';
        lineCanvasContext.shadowBlur = 30;
        lineCanvasContext.shadowColor = '#000';

        for (var i = 1; i < points.length; i++) {
            var point = points[i];
            var previousPoint = points[i - 1];

            // Change line width based on speed
            var distance = getDistanceBetween(point, previousPoint);
            var speed = Math.max(0, Math.min(maximumSpeed, distance));
            var percentageLineWidth = (maximumSpeed - speed) / maximumSpeed;
            lineCanvasContext.lineWidth = minimumLineWidth + percentageLineWidth * lineWidthRange;

            // Fade points as they age
            var age = Date.now() - point.time;
            var opacity = (pointLifetime - age) / pointLifetime;
            lineCanvasContext.strokeStyle = 'rgba(0, 0, 0, ' + opacity + ')';

            lineCanvasContext.beginPath();
            lineCanvasContext.moveTo(previousPoint.x, previousPoint.y);
            lineCanvasContext.lineTo(point.x, point.y);
            lineCanvasContext.stroke();
        }
    }

    function getDistanceBetween(a, b) {
        return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
    }

    function drawImageCanvas() {
        // Emulate background-size: cover
        var width = imageCanvas.width;
        var height = imageCanvas.width / image.naturalWidth * image.naturalHeight;

        if (height < imageCanvas.height) {
            width = imageCanvas.height / image.naturalHeight * image.naturalWidth;
            height = imageCanvas.height;
        }

        imageCanvasContext.clearRect(0, 0, imageCanvas.width, imageCanvas.height);
        imageCanvasContext.globalCompositeOperation = 'source-over';
        imageCanvasContext.drawImage(image, 0, 0, width, height);
        imageCanvasContext.globalCompositeOperation = 'destination-in';
        imageCanvasContext.drawImage(lineCanvas, 0, 0);
    }

And here is my Component

import { Component } from '@angular/core';
import * as $ from 'jquery';

@Component({
selector: 'app-hero',
templateUrl: './hero.component.html',
styleUrls: ['./hero.component.scss']
})
export class HeroComponent {


}

The Error i got is this :

  • message: 'Property 'complete' does not exist on type 'HTMLElement'.'
  • message: 'Property 'naturalWidth' does not exist on type 'HTMLElement'.'
  • message: 'Property 'naturalHeight' does not exist on type 'HTMLElement'.'
  • message: 'Argument of type 'HTMLElement' is not assignable to parameter of type 'HTMLCanvasElement | HTMLImageElement | HTMLVideoElement | ImageBitmap'. Type 'HTMLElement' is not assignable to type 'ImageBitmap'. Property 'width' is missing in type 'HTMLElement'.'

Everything works fine if i put the code inline at Index.html file, but i don't want to put it there, i want to apply it in my component.

Please, help me to find a way to implement this plain Javascript in my component.ts. Or maybe if there are any Typescript master here, kindly recode for me. hehehe Thank You guys alot.

Andre Ramadhan
  • 427
  • 2
  • 14

2 Answers2

1

You do not have to use jQuery. Angular have all things in place to interact with a dom.
You have to use https://angular.io/api/core/Renderer2 and https://angular.io/api/core/HostListener to adjust the code the way angular can understand that.

I just converted the code from https://codepen.io/chrisdoble/pen/WQLLVp in to angular way. Here is an example what are you looking for:

export class NativeBlurComponent implements OnInit {
  img = new Image();
  imageCanvas = document.createElement('canvas');
  imageCanvasContext = this.imageCanvas.getContext('2d');
  lineCanvas = document.createElement('canvas');
  lineCanvasContext = this.lineCanvas.getContext('2d');
  pointLifetime = 1000;
  points = [];

  constructor(private renderer: Renderer2) { }

  ngOnInit() {
    this.img.src = document.querySelector('img').src;
    if (this.img.complete) {
      this.start();
    } else {
      this.img.onload = this.start;
    }
  }

  start() {
    this.renderer.appendChild(document.body, this.imageCanvas);
    this.resizeCanvases();
    this.tick();
  }

  @HostListener('document:mousemove', [ '$event' ])
  onMouseMove(event) {
      this.points.push({
          time: Date.now(),
          x: event.clientX,
          y: event.clientY
      });
  }
  @HostListener('window:resize', [ '$event' ])
  resizeCanvases() {
      this.imageCanvas.width = this.lineCanvas.width = window.innerWidth;
      this.imageCanvas.height = this.lineCanvas.height = window.innerHeight;
  }

  tick() {
      // Remove old points
      this.points = this.points.filter(function(point) {
          const age = Date.now() - point.time;
          return age < this.pointLifetime;
      });

      this.drawLineCanvas();
      this.drawImageCanvas();
      requestAnimationFrame(this.tick);
  }
  drawLineCanvas() {
      const minimumLineWidth = 25;
      const maximumLineWidth = 100;
      const lineWidthRange = maximumLineWidth - minimumLineWidth;
      const maximumSpeed = 50;

      this.lineCanvasContext.clearRect(0, 0, this.lineCanvas.width, this.lineCanvas.height);
      this.lineCanvasContext.lineCap = 'round';
      this.lineCanvasContext.shadowBlur = 30;
      this.lineCanvasContext.shadowColor = '#000';

      for (let i = 1; i < this.points.length; i++) {
          const point = this.points[i];
          const previousPoint = this.points[i - 1];

          // Change line width based on speed
          const distance = this.getDistanceBetween(point, previousPoint);
          const speed = Math.max(0, Math.min(maximumSpeed, distance));
          const percentageLineWidth = (maximumSpeed - speed) / maximumSpeed;
          this.lineCanvasContext.lineWidth = minimumLineWidth + percentageLineWidth * lineWidthRange;

          // Fade points as they age
          const age = Date.now() - point.time;
          const opacity = (this.pointLifetime - age) / this.pointLifetime;
          this.lineCanvasContext.strokeStyle = 'rgba(0, 0, 0, ' + opacity + ')';

          this.lineCanvasContext.beginPath();
          this.lineCanvasContext.moveTo(previousPoint.x, previousPoint.y);
          this.lineCanvasContext.lineTo(point.x, point.y);
          this.lineCanvasContext.stroke();
      }
  }

  getDistanceBetween(a, b) {
      return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
  }

  drawImageCanvas() {
      // Emulate background-size: cover
      let width = this.imageCanvas.width;
      let height = this.imageCanvas.width / this.img.naturalWidth * this.img.naturalHeight;

      if (height < this.imageCanvas.height) {
          width = this.imageCanvas.height / this.img.naturalHeight * this.img.naturalWidth;
          height = this.imageCanvas.height;
      }

      this.imageCanvasContext.clearRect(0, 0, this.imageCanvas.width, this.imageCanvas.height);
      this.imageCanvasContext.globalCompositeOperation = 'source-over';
      this.imageCanvasContext.drawImage(this.img, 0, 0, width, height);
      this.imageCanvasContext.globalCompositeOperation = 'destination-in';
      this.imageCanvasContext.drawImage(this.lineCanvas, 0, 0);
  }
}
angularrocks.com
  • 26,767
  • 13
  • 87
  • 104
0

Of course you can.

But you will have to handle with:

ElementRef : https://angular.io/api/core/ElementRef

Renderer : https://angular.io/api/core/Renderer

or Renderer 2.

Typescript need you to give the type of the element for HTMLElement.

for example:

svg: SVGElement = ...

In your case, you have to say that your element has the type: HTMLImageElement.

Wandrille
  • 6,267
  • 3
  • 20
  • 43