代码之家  ›  专栏  ›  技术社区  ›  Atticus29

Angular 2+获取动态生成的HTML元素的引用,用于画布操作

  •  2
  • Atticus29  · 技术社区  · 6 年前

    我在Angular中提供了绘图服务,其中有几个方法采用各种与绘图相关的参数,还有一个参数引用画布元素的动态生成Id,如本文中的drawLizard方法:

    图纸服务:

    import { Injectable } from '@angular/core';
    import { Genotype } from './genotype.model';
    
    @Injectable({
      providedIn: 'root'
    })
    export class DrawingService {
      constructor() {
      }
    
      drawArc(x: number, y: number, radius: number, startAngle:number, endAngle: number, elementId: string, color: string, fillStatus: boolean) {
        let canvas = <HTMLCanvasElement> document.getElementById(elementId);
        // console.log(canvas); //TODO fix
        if (canvas) {
          let ctx = canvas.getContext('2d');
          ctx.beginPath();
          ctx.arc(x, y, radius, startAngle, endAngle);
          if (fillStatus) {
            ctx.fillStyle = color;
            ctx.fill();
          } else {
            ctx.lineWidth = 1;
            ctx.strokeStyle = color;
            ctx.stroke();
          }
        } else {
          //TODO handle error here
        }
      }
    
      drawTriangle(x1: number, y1: number, x2: number, y2: number, x3: number, y3:number, elementId: string, color: string, fillStatus: boolean){
    
        // let canvasElementTest = <HTMLCanvasElement>document.getElementById("first");
        // console.log(canvasElementTest); //TODO fix
        let canvasElement = <HTMLCanvasElement>document.getElementById(elementId);
        // let canvasElement: Element = <HTMLCanvasElement>document.getElementsByClassName(elementId);
        console.log(canvasElement); //TODO fix
        if (canvasElement){
          let context = canvasElement.getContext("2d");
          context.beginPath();
          context.moveTo(x1, y1);
          context.lineTo(x2, y2);
          context.lineTo(x3, y3);
          context.closePath();
          context.lineWidth = 1;
          context.strokeStyle = color;
          context.stroke();
          if(fillStatus){
            context.fillStyle = color;
            context.fill();
        }
      } else{
        //TODO throw/handle error here
      }
    }
    
    drawEllipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle:number, endAngle:number, elementId: string, color: string, fillStatus: boolean){
      let canvas = <HTMLCanvasElement> document.getElementById(elementId);
      if(canvas){
        let ctx = canvas.getContext('2d');
        // ctx.setLineDash([])
        ctx.beginPath();
        ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, false); //x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise
        ctx.stroke();
        if(fillStatus){
          ctx.fillStyle = color;
          ctx.fill();
        }
        ctx.closePath();
      } else{
        //TODO handle error here
      }
    
    }
    
    drawLizard(canvasId: string, genotype: Genotype){
      //head
      this.drawTriangle(37.5,87.5,37.5,112.5,25,100, canvasId,"black", true);
      this.drawArc(37.5, 100, 12.5, 3*Math.PI/2, Math.PI/2, canvasId,"black", true);
      // console.log("mark got here, too"); //TODO fix
    
      //eyes
      this.drawArc(37.5, 106.25, 2, 0, 2*Math.PI, canvasId,"white", true);
      this.drawArc(37.5, 93.75, 2, 0, 2*Math.PI, canvasId,"white", true);
      this.drawArc(37.5, 106.25, 0.5, 0, 2*Math.PI, canvasId,"black", true);
      this.drawArc(37.5, 93.75, 0.5, 0, 2*Math.PI, canvasId,"black", true);
    
      //body
      this.drawEllipse(100, 100, 50, 10, 0, 0, 2 * Math.PI, canvasId, 'black', true);
    
      //legs
      this.drawEllipse(140, 110, 30, 3, 0.65, 0, 2 * Math.PI, canvasId, 'black', true);
      this.drawEllipse(140, 90, 30, 3, 0.9+Math.PI/2, 0, 2 * Math.PI, canvasId, 'black', true);
      this.drawEllipse(70, 110, 30, 3, -Math.PI/6, 0, 2 * Math.PI, canvasId, 'black', true);
      this.drawEllipse(70, 90, 30, 3, Math.PI/6, 0, 2 * Math.PI, canvasId, 'black', true);
    
      //random polka dots
      // let colorArray = new Array<string>("blue", "pink", "orange", "#FF00FF", "red", "#00FFFF", "#800000", "#00FF00", "#008000", "#00FFFF", "#008080", "#BFBFFE", "#800080");
      // this.drawArc(77, 95, 3, 0, 2*Math.PI, canvasId, colorArray[Math.floor(Math.random()*colorArray.length)], true);
    
      this.drawArc(60, 101, 3, 0, 2*Math.PI, canvasId, genotype.getAllele1(), true);
      this.drawArc(85, 102, 3, 0, 2*Math.PI, canvasId, genotype.getAllele2(), true);
      this.drawArc(109, 94, 3, 0, 2*Math.PI, canvasId, genotype.getAllele1(), true);
      this.drawArc(120, 102, 3, 0, 2*Math.PI, canvasId, genotype.getAllele2(), true);
    }
    }
    

    我有几个这样的画布元素动态生成在我的蜥蜴-显示.component.html:

    ...
    <mat-card small class="example-card" *ngFor="let individual of individuals; let i=index">
    ...
    <canvas id="lizard-canvas{{i}}" width="200" height="200" style="border:1px solid #c3c3c3;">
        Your browser does not support the canvas element.
    </canvas>
    ...
    </mat-card>
    ...
    

    在我的蜥蜴身上-显示.component.ts文件中,我生成了一个蜥蜴示例并尝试绘制它:

    import { OnInit, Component } from '@angular/core';
    import { DrawingService } from '../drawing.service';
    import { Genotype } from '../genotype.model';
    import { Gene } from '../gene.model';
    import { Organism } from '../organism.model';
    import { ColorNameService } from '../color-name.service';
    import { IndividualGenerationService } from '../individual-generation.service';
    
    @Component({
      selector: 'app-lizard-display',
      templateUrl: './lizard-display.component.html',
      styleUrls: ['./lizard-display.component.css'],
      providers: [DrawingService]
    })
    export class LizardDisplayComponent implements OnInit {
      private individuals: Array<Organism> = new Array<Organism>();
    
      constructor(private ds: DrawingService, private cns: ColorNameService, private individualGenService: IndividualGenerationService) { }
    
      ngOnInit() {
    
          //TODO delete me after fleshed out more
          let testIndividual: Organism = this.individualGenService.makeIndividual("green", "blue");
          let genotype: Genotype = testIndividual.getGeneByName("spot color").getGenotype();
          this.individuals.push(testIndividual);
    
          this.ds.drawLizard('lizard-canvas0', genotype);
      } 
    }
    

    有很多解决方案(例如。, here )似乎为那些不需要申请的人工作 .getContext('2d') 他们的元素。

    drawTriangle 方法调用者 drawLizard 在绘图服务中)在SO上使用这些解决方案是空的。

    我希望能找到类似Angular2+的解决方案。

    git clone https://github.com/Atticus29/population-fragmentation.git
    cd population-fragmentation
    git checkout dynamicCanvasSO
    npm install
    ng serve
    

    http://localhost:4200/ 在浏览器中

    1 回复  |  直到 6 年前
        1
  •  3
  •   bryan60    6 年前

    获取html元素引用的方法是使用ViewChildren,如下所示:

    export class LizardDisplayComponent implements OnInit, AfterViewInit {
      @ViewChildren('canvases') canvases: QueryList<ElementRef>;
      private individuals: Array<Organism> = new Array<Organism>();
    
      constructor(private ds: DrawingService, private cns: ColorNameService, private individualGenService: IndividualGenerationService) { }
    
      ngOnInit() { } 
    
      ngAfterViewInit() { // view children arent available till this hook
         this.canvases.forEach(canvas => console.log(canvas)); 
         //here you'll see each has a nativeElement property that is a reference to that element, you can theoretically pass that to your service to be drawn on, this is just looping over everything with the '#canvases' tag applied to it
    
         let testIndividual: Organism = this.individualGenService.makeIndividual("green", "blue");
         let genotype: Genotype = testIndividual.getGeneByName("spot color").getGenotype();
         this.individuals.push(testIndividual);
    
         //you can pass them to your service like this instead of the id
         this.ds.drawLizard(this.canvases.toArray()[0], genotype);
      }
    }
    

    ...
    <mat-card small class="example-card" *ngFor="let individual of individuals; let i=index">
    ...
    <canvas #canvases id="lizard-canvas{{i}}" width="200" height="200" style="border:1px solid #c3c3c3;">
        Your browser does not support the canvas element.
    </canvas>
    ...
    </mat-card>
    ...
    

    然后您可以将其传递给您的服务:

    drawLizard(canvasRef: ElementRef, genotype: Genotype){
      this.drawTriangle(37.5,87.5,37.5,112.5,25,100, canvasRef,"black", true);
      ...
    }
    
    drawTriangle(x1: number, y1: number, x2: number, y2: number, x3: number, y3:number, elementRef: ElementRef, color: string, fillStatus: boolean){
      let canvasElement = <HTMLCanvasElement>elementRef.nativeElement;
      ...
    }