1

canvas 要素を使用して、Angular 2 で円形のプログレス バーを作成したいと考えています。私は 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 への呼び出し)。そうしないと、キャンバス要素がまだ完全に初期化されていないため、すべてファンキーに見えます。ngOnChanges ライフサイクル フックを調べましたが、ビューが読み込まれる前に最初に起動するため、役に立ちません。

私が考えていたのは、progress入力変数を監視可能にしてから、フックでサブスクライブすることngAfterViewInitでしたが、それがどのように機能するかは完全にはわかりません。また、フックしngOnChangesて最初の変更かどうかを確認できると思います (実際には、SimpleChange クラスにはドキュメントによるとそのようなメソッドがあります)。

ポインタをいただければ幸いです!Angular 2 は私にとって成長しつつありますが、Angular 1/ES5 からの移行に慣れるにはかなりの時間がかかることは間違いありません。

4

1 に答える 1