Angular 4 - 使用HTMLCanvasElement

时间:2017-11-17 14:26:12

标签: html5 angular canvas

每个人,

我开始学习Angular,我决定从Langton Ant算法的实现开始。

项目

我使用HTML5画布来显示步行。

模板

<p>Langton ant</p>

<canvas #canvas_box width='420' height='420' style="background: #fff; margin:20px"></canvas>

组件

import { LangtonGridService } from './langton.grid.service';
import { CellCoordinate } from './grid/grid.coordinate'
import { Component, ViewChild, AfterViewInit } from '@angular/core'
import { LangtonStep } from './langton.utils'


@Component({
  selector: 'langton-main',
  templateUrl: "template/langton.template.html",
})
export class LangtonMainComponent  implements AfterViewInit{
  DIRECTION_UP:number=2;
  DIRECTION_RIGHT:number=1;
  DIRECTION_LEFT:number=-1;
  DIRECTION_DOWN:number=-2;

  COLOR_FOG:string='white';
  COLOR_GRID:string='gainsboro';
  COLOR_FLAG:string='red';
  COLOR_FREE:string='aqua';
  COLOR_CURRENT:string='black';

  MAX_ITERATION:number=15000;
  STEP_TIMER:number=100;

  grid:LangtonGridService;

  @ViewChild('canvas_box') 
  canvas:HTMLCanvasElement;

  lineCount:number;
  columnCount:number;
  state:any={};
  roadmap:Array<LangtonStep>=[];

  ngAfterViewInit(){
    this.setGrid(new LangtonGridService(this.getCanvas()));
    this.getGrid().drawBoard(this.COLOR_GRID);
    this.walk();
  }

  private getCanvas():HTMLCanvasElement{
    return this.canvas;
  }

  private getGrid():LangtonGridService{
    return this.grid;
  }

  private setGrid(grid:LangtonGridService):void{
    this.grid = grid;
  }

  private getState():any{
    return this.state;
  }

  private getRoadmap():Array<LangtonStep>{
    return this.roadmap;
  }

  private setLineCount():void{
    this.lineCount = this.getGrid().computeLines().length;
  }

  private setColumnCount():void{
    this.columnCount = this.getGrid().computeColumns().length;
  }

  private getLineCount():number{
    return this.lineCount;
  }

  private getColumnCount():number{
    return this.columnCount;
  }

  private getStart():LangtonStep{
    var middleColumn:number = Math.round(this.getColumnCount()/2);
    var middleLine:number = Math.round(this.getColumnCount()/2);
    var start:CellCoordinate=new CellCoordinate(middleColumn, middleLine);
    var color=this.COLOR_FLAG;
    var direction=this.DIRECTION_UP;
    var step:LangtonStep = new LangtonStep(start, color, direction);
    return step;
  };

  private memorize(step:LangtonStep):void{
    this.getState()[step.getAddress()]=step;
  }

  private track(step:LangtonStep):void{
    this.getRoadmap().push(step);
  }

  private tracked(step:LangtonStep):boolean{
    var state:string=typeof(this.getState()[step.getAddress()]);
    var exist:boolean='undefined'.localeCompare(state)!==0;
    return exist;
  }

  private turnRight(step:LangtonStep):LangtonStep{
    var cellCoordinate:CellCoordinate;
    var direction:number;
    var color:string;
    var next:LangtonStep;
    switch(step.direction){

      case this.DIRECTION_UP:
      cellCoordinate=new CellCoordinate(step.coordinate.column+1, step.coordinate.line);
      direction = this.DIRECTION_RIGHT;
      break;

      case this.DIRECTION_DOWN:
      cellCoordinate=new CellCoordinate(step.coordinate.column-1, step.coordinate.line);
      direction = this.DIRECTION_LEFT;
      break;

      case this.DIRECTION_LEFT:
      cellCoordinate=new CellCoordinate(step.coordinate.column, step.coordinate.line+1);
      direction = this.DIRECTION_UP;
      break;

      case this.DIRECTION_RIGHT:
      cellCoordinate=new CellCoordinate(step.coordinate.column, step.coordinate.line-1);
      direction = this.DIRECTION_RIGHT;
      break;

      default:
      console.error('erreur dans la direction');
    };
    color=this.COLOR_FLAG;
    next = new LangtonStep(cellCoordinate,color,direction);
    return next;
  }

  private turnLeft(step:LangtonStep):LangtonStep{
    var cellCoordinate:CellCoordinate;
    var direction:number;
    var color:string;
    var next:LangtonStep;
    switch(step.direction){

      case this.DIRECTION_UP:
      cellCoordinate=new CellCoordinate(step.coordinate.column-1, step.coordinate.line);
      direction = this.DIRECTION_RIGHT;
      break;

      case this.DIRECTION_DOWN:
      cellCoordinate=new CellCoordinate(step.coordinate.column+1, step.coordinate.line);
      direction = this.DIRECTION_LEFT;
      break;

      case this.DIRECTION_LEFT:
      cellCoordinate=new CellCoordinate(step.coordinate.column, step.coordinate.line-1);
      direction = this.DIRECTION_UP;
      break;

      case this.DIRECTION_RIGHT:
      cellCoordinate=new CellCoordinate(step.coordinate.column, step.coordinate.line+1);
      direction = this.DIRECTION_RIGHT;
      break;

      default:
      console.error('erreur dans la direction');
    };
    color=this.COLOR_FREE;
    next = new LangtonStep(cellCoordinate,color,direction);
    return next;
  };

  private getColorFromMemory(step:LangtonStep):string{
    var color:string;
    if(this.tracked(step)){
      color=this.getState()[step.getAddress()].color;
    } else {
      color = this.COLOR_FOG;
    }
    return color;
  };

  private print(step:LangtonStep):void{
    this.getGrid().fillCell(step.coordinate, step.color);
  };

  private enlight(cell:CellCoordinate):void{
    this.getGrid().strokeCell(cell, this.COLOR_CURRENT);
  }

  private darken(cell:CellCoordinate):void{
    this.getGrid().strokeCell(cell, this.COLOR_GRID);
  }

  private turn(step:LangtonStep):LangtonStep{
    var next:LangtonStep;
    switch(step.color){

      case this.COLOR_FOG:
      next=this.turnRight(step);
      break;

      case this.COLOR_FLAG:
      next=this.turnLeft(step);
      break;

      case this.COLOR_FREE:
      next=this.turnRight(step);
      break;

      default:
      console.error('La couleur venue du ciel');
      break;
    }
    next.setColor(this.getColorFromMemory(next));
    return next;
  }

  private walk(step:LangtonStep=this.getStart()):void{
    var self:LangtonMainComponent=this;
    var forward:Function=function(step:LangtonStep):LangtonStep{
      var next:LangtonStep=self.turn(step);
      self.print(step);
      self.darken(step.coordinate);
      self.enlight(next.coordinate);
      self.memorize(step);
      self.track(step);
      return next;
    }

    var iteration=0;

    var run:number = setInterval(function():void{
      if(self.MAX_ITERATION===iteration++){
        clearInterval(run);
      } else{
        step=forward(step);
      }
    }, self.STEP_TIMER);
  }
}

问题

ERROR TypeError: Cannot read property 'getContext' of undefined
    at GridDrawer.getContext (grid.drawer.ts:17)
    at GridDrawer.openLayer (grid.drawer.ts:21)
    at LangtonGridService.drawBoard (langton.grid.service.ts:109)
    at LangtonMainComponent.ngAfterViewInit (langton.main.component.ts:51)
    at callProviderLifecycles (provider.js:585)
    at callElementProvidersLifecycles (provider.js:556)
    at callLifecycleHooksChildrenFirst (provider.js:540)
    at checkAndUpdateView (view.js:390)
    at callViewAction (view.js:732)
    at execComponentViewsAction (view.js:661)

问题

我的第一个尝试是在组件构造函数中设置画布。 经过一些搜索后,我了解到我要导入AfterViewInit界面才能使用。

但在每种情况下,我都会遇到与未定义画布相同的错误。

我很确定解决方案很明显,但我找不到它。

非常感谢你的帮助,祝你有个美好的一天。

艾蒂安。

1 个答案:

答案 0 :(得分:1)

我终于找到了解决方案。问题来自HTMLCanvasElement,我使用ElementRef来解决绑定问题。

模板

<canvas #canvas_box width='420' height='420' style="background: #fff; margin:20px"></canvas>

组件

export class LangtonMainComponent  implements AfterViewInit{

  /* ... */

  @ViewChild('canvas_box') canvas:ElementRef;

  ngAfterViewInit(){
    this.canvas.nativeElement.width = this.grid.getCanvasWidth();
    this.canvas.nativeElement.height = this.grid.getCanvasHeight();

    this.drawBoard();
    this.walk();
  }

  /* ... */
}