import { Component , Input , HostBinding , ViewChild , ElementRef , OnDestroy , OnChanges , SimpleChanges , NgZone , TemplateRef , OnInit , HostListener , ViewEncapsulation , Renderer2 , SimpleChange , ChangeDetectionStrategy , ChangeDetectorRef } from '@angular/core' ; import { Subscription } from 'rxjs/Subscription' ; import { Observable } from 'rxjs/Observable' ; import { debounceTime } from 'rxjs/operators' ; import { FromEventObservable } from 'rxjs/observable/FromEventObservable' ; import { coerceNumberProperty } from '@angular/cdk/coercion' ; @ Component ( { selector : 'water-wave' , template : `
    <div [ngStyle]="{'height.px': height, 'width.px': height, 'overflow': 'hidden'}">
        <canvas #container class="canvas-wrap" width="{{height*2}}" height="{{height*2}}"></canvas>
    </div>
    <div class="text" [ngStyle]="{'width.px': height}">
        <ng-container *ngIf="_title; else _titleTpl"><span>{{_title}}</span></ng-container>
        <h4>{{percent}}%</h4>
    </div>` , styleUrls : [ './water-wave.less' ] , changeDetection : ChangeDetectionStrategy . OnPush } ) export class WaterWaveComponent implements OnDestroy , OnChanges , OnInit { _title = '' ; _titleTpl : TemplateRef < any > ; @ Input ( ) set title ( value ) { if ( value instanceof TemplateRef ) { this . _title = null ; this . _titleTpl = value ; } else this . _title = value ; } @ Input ( ) color = '#1890FF' ; @ Input ( ) get height ( ) { return this . _height ; } set height ( value ) { this . _height = coerceNumberProperty ( value ) ; } private _height = 160 ; @ Input ( ) get percent ( ) { return this . _percent ; } set percent ( value ) { this . _percent = coerceNumberProperty ( value ) ; } private _percent : number ; @ ViewChild ( 'container' ) node : ElementRef ; initFlag = false ; timer : any ; constructor ( private el , private renderer , private cd ) { } renderChart ( ) { const data = this . percent / 100 ; if ( ! data ) return ; this . node . nativeElement . innerHTML = '' ; const self = this ; const canvas = this . node . nativeElement as HTMLCanvasElement ; const ctx = canvas . getContext ( '2d' ) ; const canvasWidth = canvas . width ; const canvasHeight = canvas . height ; const radius = canvasWidth / 2 ; const lineWidth = 2 ; const cR = radius - ( lineWidth ) ; ctx . beginPath ( ) ; ctx . lineWidth = lineWidth * 2 ; const axisLength = canvasWidth - ( lineWidth ) ; const unit = axisLength / 8 ; const range = 0.2 ; let currRange = range ; const xOffset = lineWidth ; let sp = 0 ; let currData = 0 ; const waveupsp = 0.005 ; let arcStack = [ ] ; const bR = radius - ( lineWidth ) ; const circleOffset = - ( Math . PI / 2 ) ; let circleLock = true ; for ( let i = circleOffset ; i < circleOffset + ( 2 * Math . PI ) ; i += 1 / ( 8 * Math . PI ) ) { arcStack . push ( [ radius + ( bR * Math . cos ( i ) ) , radius + ( bR * Math . sin ( i ) ) , ] ) ; } const cStartPoint = arcStack . shift ( ) ; ctx . strokeStyle = this . color ; ctx . moveTo ( cStartPoint [ 0 ] , cStartPoint [ 1 ] ) ; function drawSin ( ) { ctx . beginPath ( ) ; ctx . save ( ) ; const sinStack = [ ] ; for ( let i = xOffset ; i <= xOffset + axisLength ; i += 20 / axisLength ) { const x = sp + ( ( xOffset + i ) / unit ) ; const y = Math . sin ( x ) * currRange ; const dx = i ; const dy = ( ( 2 * cR * ( 1 - currData ) ) + ( radius - cR ) ) - ( unit * y ) ; ctx . lineTo ( dx , dy ) ; sinStack . push ( [ dx , dy ] ) ; } const startPoint = sinStack . shift ( ) ; ctx . lineTo ( xOffset + axisLength , canvasHeight ) ; ctx . lineTo ( xOffset , canvasHeight ) ; ctx . lineTo ( startPoint [ 0 ] , startPoint [ 1 ] ) ; const gradient = ctx . createLinearGradient ( 0 , 0 , 0 , canvasHeight ) ; gradient . addColorStop ( 0 , '#ffffff' ) ; gradient . addColorStop ( 1 , '#1890FF' ) ; ctx . fillStyle = gradient ; ctx . fill ( ) ; ctx . restore ( ) ; } function render ( ) { ctx . clearRect ( 0 , 0 , canvasWidth , canvasHeight ) ; if ( circleLock ) { if ( arcStack . length ) { const temp = arcStack . shift ( ) ; ctx . lineTo ( temp [ 0 ] , temp [ 1 ] ) ; ctx . stroke ( ) ; } else { circleLock = false ; ctx . lineTo ( cStartPoint [ 0 ] , cStartPoint [ 1 ] ) ; ctx . stroke ( ) ; arcStack = null ; ctx . globalCompositeOperation = 'destination-over' ; ctx . beginPath ( ) ; ctx . lineWidth = lineWidth ; ctx . arc ( radius , radius , bR , 0 , 2 * Math . PI , true ) ; ctx . beginPath ( ) ; ctx . save ( ) ; ctx . arc ( radius , radius , radius - ( 3 * lineWidth ) , 0 , 2 * Math . PI , true ) ; ctx . restore ( ) ; ctx . clip ( ) ; ctx . fillStyle = '#1890FF' ; } } else { if ( data >= 0.85 ) { if ( currRange > range / 4 ) { const t = range * 0.01 ; currRange -= t ; } } else if ( data <= 0.1 ) { if ( currRange < range * 1.5 ) { const t = range * 0.01 ; currRange += t ; } } else { if ( currRange <= range ) { const t = range * 0.01 ; currRange += t ; } if ( currRange >= range ) { const t = range * 0.01 ; currRange -= t ; } } if ( ( data - currData ) > 0 ) { currData += waveupsp ; } if ( ( data - currData ) < 0 ) { currData -= waveupsp ; } sp += 0.07 ; drawSin ( ) ; } self . timer = requestAnimationFrame ( render ) ; } render ( ) ; } uninstall ( ) { } private updateRadio ( radio ) { this . renderer . setStyle ( this . el . nativeElement , 'transform' , `scale(${ radio })` ) ; } ngOnInit ( ) { this . initFlag = true ; this . updateRadio ( 1 ) ; setTimeout ( ( ) => this . resize ( ) , 100 ) ; this . installResizeEvent ( ) ; } ngOnChanges ( changes ) { if ( this . initFlag ) this . renderChart ( ) ; } ngOnDestroy ( ) { if ( this . timer ) cancelAnimationFrame ( this . timer ) ; this . uninstallResizeEvent ( ) ; this . uninstall ( ) ; } private autoHideXLabels = false ; private scroll$ : Subscription = null ; private installResizeEvent ( ) { if ( this . scroll$ ) return ; this . scroll$ = < any > FromEventObservable . create ( window , 'resize' ) . pipe ( debounceTime ( 500 ) ) . subscribe ( ( ) => this . resize ( ) ) ; } private uninstallResizeEvent ( ) { if ( this . scroll$ ) this . scroll$ . unsubscribe ( ) ; } resize ( ) { const { offsetWidth } = this . el . nativeElement . parentNode ; this . updateRadio ( offsetWidth < this . height ? offsetWidth / this . height : 1 ) ; this . renderChart ( ) ; } }