Angular 2:canvas 和变化检测

Angular 2: canvas and change detection

我想在 Angular 2 中创建一个圆形进度条,使用 canvas 元素。我在 Angular 1 中完成了多个项目,但我对 Angular 2 和 TypeScript 是全新的,所以我有点缓慢...

我创建了一个 ProgressCircle 组件,并使其可注入,以便我可以在其他组件中使用它。此进度组件从父组件接收几个属性,这些属性确定进度圈的外观以及填充方式。

注意:我从一个旧的 ES5 项目中提取了这段代码。这可能会显示。

import { Component, Input, Injectable, ElementRef, AfterViewInit } from '@angular/core';

@Injectable()
@Component({
    selector: 'progress-circle',
    template: `
        <div class="progress-indicator" [style.width]="size + 'px'" [style.height]="size + 'px'">
            <canvas [attr.width]="size" [attr.height]="size"></canvas>
            <span class="progress-text-container">
                <span class="progress-text">{{ progress }}</span>
            </span>
        </div>
    `
})
export class ProgressCircle implements AfterViewInit {
    @Input() size:number;
    @Input() progress:number;
    @Input() lineWidth:number;
    @Input() rotate:number;
    @Input() color:string;
    @Input() trackColor:string;
    @Input() bgColor:string;
    @Input() borderWidth:number;
    @Input() animate:boolean;

    constructor(private el:ElementRef) {}

    ngAfterViewInit() {
        this.draw();
    }

    draw() {
        let canvas = this.el.nativeElement.getElementsByTagName('canvas')[0],
            ctx    = canvas.getContext('2d'),

            options = {
                percent: this.progress || 0,
                size: this.size || 90,
                lineWidth: this.lineWidth || (this.size / 10) || 10,
                rotate: this.rotate || 0,
                color: this.color || '#000',
                trackColor: this.trackColor || '#e6e6e6',
                bgColor: this.bgColor || 'transparent',
                borderWidth: this.borderWidth || 0,
                doAnimate: this.animate
            },

            radius = (options.size - options.lineWidth) / 2,
            progress = 0,
            fillColor:string = undefined,

            animFrame:number, bg:ImageData;

        let drawCircle = (strokeColor:string, fillColor:string, percent:number, hasBorder:boolean = false) => {
            // logic to draw circle goes here
        }

        let animateProgess = () => {
            if (++progress <= options.percent) { // we need to animate
                // draw circle
                animFrame = window.requestAnimationFrame(animateProgess);
            }
        }

        ctx.translate(options.size / 2, options.size / 2);
        ctx.rotate((-.5 + (options.rotate / 180)) * Math.PI);

        if (options.doAnimate) {
            bg = ctx.getImageData(0, 0, canvas.width, canvas.height);
            animFrame = window.requestAnimationFrame(animateProgess);
        } else {
            drawCircle(options.color, fillColor, options.percent);
            this.progress = Math.floor(options.percent); 
        }
    }
}

现在,这工作正常。但是来自父组件的 progress 输入变量可能随时改变。如何在适当的时候触发重绘进度条?

在加载视图之前我无法初始化进度条(因此调用 ngAfterViewInit),否则它看起来很奇怪,因为 canvas 元素尚未完全初始化。我查看了 ngOnChanges 生命周期挂钩,但它没用,因为它在加载视图之前首先触发。

一个想法是让 progress 输入变量可观察,然后在 ngAfterViewInit 挂钩中订阅它,但我不完全确定它是如何工作的。我还想我可以连接到 ngOnChanges 并检查它是否是第一个更改(实际上 SimpleChange class 根据文档有这样的方法),但对我来说感觉有点 hackish。

不胜感激! Angular 2 对我来说越来越重要,但它肯定需要很多时间才能适应 Angular 1/ES5。

使用ngOnChanges。这是当输入 属性 值更改时要通知的 appropriate lifecycle hook to use

至于等到视图初始化之后...我会 ngAfterViewInit 设置一个标志,ngOnChanges 每次运行时都会检查它:

export class ProgressCircle implements AfterViewInit {
   viewInitialized = false;
   ...
   ngOnChanges() {
      if(!viewInitialized)  return;
      ...
   }
   ngAfterViewInit() {
      viewInitialized = true;
      ...
   }

ngOnChanges 可能有一些方法可以确定视图是否已初始化,但我不知道有任何方法(post 如果您知道一种方法,请发表评论,它是比旗帜更有效)。

此外,@Injectable only needs to be used on services 依赖于其他服务。