import { Component, OnInit, Input, OnChanges, SimpleChanges } from '@angular/core'; import { FProgressType, FProgressStatusType, FProgressFormatter, FProgressGapPositionType, FProgressCirclePath, FProgressStrokeColorType, FProgressColorGradient, FProgressGradientProgress, FProgressStrokeLinecapType } from './progress-options'; import { handleLinearGradient, handleCircleGradient } from './progress-utils'; const defaultFormatter: FProgressFormatter = (p: number): string => `${p}%`; let gradientIdSeed = 0; const statusColorMap = new Map([ ['normal', '#388fff'], ['exception', '#f24645'], ['success', '#51bd78'] ]); @Component({ selector: 'farris-progress', templateUrl:'./progress.component.html', styleUrls:[ './progress.component.scss' ] }) export class ProgressComponent implements OnInit, OnChanges{ //进度条类型 @Input() type:FProgressType = 'line'; //进度条宽度 @Input() strokeWidth:number; //进度条尺寸 'default' | 'small' @Input() size = 'default'; //是否显示进度条信息 @Input() showInfo:boolean = true; //进度条状态 默认'normal' @Input() status:FProgressStatusType; //进度条信息格式化 @Input() format?: FProgressFormatter; //已完成的分段百分比 @Input() successPercent: number; //进度条颜色 @Input() strokeColor:FProgressStrokeColorType; //圆形进度条宽度 @Input() width:number; //仪表盘缺口位置 @Input() gapPosition:FProgressGapPositionType = 'top'; //仪表盘缺口角度 默认是75 @Input() gapDegree:number;//仪表盘上角度 0-360度 //进度条短点形状 默认圆角 可选直角square @Input() strokeLinecap:FProgressStrokeLinecapType = 'round'; //进度条百分比 _percent:number = 0; @Input() get percent(){ return this._percent; } set percent(percent){ this._percent = percent; } //存储的状态 private cachedStatus: FProgressStatusType = 'normal'; //计算的状态 private inferredStatus: FProgressStatusType = 'normal'; pathString:string; trailPathStyle: any; //圆环路径 progressCirclePath:FProgressCirclePath[] = []; //是否渐变 isGradient = false; gradientId = gradientIdSeed++; lineGradient: string | null = null; circleGradient: Array<{ offset: string; color: string }>; //获得进度条状态 get progressStatus():FProgressStatusType{ return this.status || this.inferredStatus; } //进度条信息格式化 get progressFormatter(): FProgressFormatter { return this.format || defaultFormatter; } //计算进度条宽度 默认8px small为6px get strokeW(): number { return this.strokeWidth || (this.type === 'line' && this.size !== 'small' ? 8 : 6); } //判断是否为环形进度条 get isCircleStyle(): boolean { return this.type === 'circle' || this.type === 'dashboard'; } trackByFn = (index: number) => `${index}`; constructor() { } ngOnInit() { //圆环宽度判断 if(this.type === 'circle' || this.type === 'dashboard'){ this.width = this.width || (this.size !== 'small' ? 80 : 50); } this.setStrokeColor(); this.getCirclePaths(); } ngOnChanges(changes: SimpleChanges): void { const { gapPosition, strokeLinecap, strokeColor, gapDegree, type, status, percent, successPercent } = changes; //状态变化 if (status) { this.cachedStatus = this.status || this.cachedStatus; } //进度 已完成的分段百分比 变化 if (percent || successPercent) { const fillAll = parseInt(this.percent.toString(), 10) >= 100; if (fillAll) { if ((this.isNotNil(this.successPercent) && this.successPercent! >= 100) || this.successPercent === undefined) { this.inferredStatus = 'success'; // this.status = this.inferredStatus; } } else { this.inferredStatus = this.cachedStatus; } this.status = this.inferredStatus; } //边线颜色变化 if (strokeColor) { this.setStrokeColor(); } //圆环一些属性变化 if (gapPosition || strokeLinecap || gapDegree || type || percent || strokeColor) { this.getCirclePaths(); } } private getCirclePaths(): void { if (!this.isCircleStyle) { return; } const values = this.isNotNil(this.successPercent) ? [this.successPercent!, this.percent] : [this.percent]; const radius = 50 - this.strokeW / 2; const gapPosition = this.gapPosition || (this.type === 'circle' ? 'top' : 'bottom'); const len = Math.PI * 2 * radius; const gapDegree = this.gapDegree || (this.type === 'circle' ? 0 : 75); let beginPositionX = 0; let beginPositionY = -radius; let endPositionX = 0; let endPositionY = radius * -2; switch (gapPosition) { case 'left': beginPositionX = -radius; beginPositionY = 0; endPositionX = radius * 2; endPositionY = 0; break; case 'right': beginPositionX = radius; beginPositionY = 0; endPositionX = radius * -2; endPositionY = 0; break; case 'bottom': beginPositionY = radius; endPositionY = radius * 2; break; default: } this.pathString = `M 50,50 m ${beginPositionX},${beginPositionY} a ${radius},${radius} 0 1 1 ${endPositionX},${-endPositionY} a ${radius},${radius} 0 1 1 ${-endPositionX},${endPositionY}`; this.trailPathStyle = { strokeDasharray: `${len - gapDegree}px ${len}px`, strokeDashoffset: `-${gapDegree / 2}px`, transition: 'stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s' }; // Calculate styles for each path. this.progressCirclePath = values .map((value, index) => { const isSuccessPercent = values.length === 2 && index === 0; return { stroke: this.isGradient && !isSuccessPercent ? `url(#gradient-${this.gradientId})` : null, strokePathStyle: { stroke: !this.isGradient ? (isSuccessPercent ? statusColorMap.get('success') : (this.strokeColor as string)) : null, transition: 'stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s', strokeDasharray: `${((value || 0) / 100) * (len - gapDegree)}px ${len}px`, strokeDashoffset: `-${gapDegree / 2}px` } }; }) .reverse(); } private setStrokeColor(): void { const color = this.strokeColor; //判断是否为渐变 const isGradient = (this.isGradient = !!color && typeof color !== 'string'); //线性渐变 if (isGradient && !this.isCircleStyle) { this.lineGradient = handleLinearGradient(color as FProgressColorGradient); } //圆环渐变 else if (isGradient && this.isCircleStyle) { this.circleGradient = handleCircleGradient(this.strokeColor as FProgressGradientProgress); } //不是渐变 else { this.lineGradient = null; this.circleGradient = []; } } isNotNil(value: T): value is NonNullable { return typeof value !== 'undefined' && value !== null; } }