Angular 自定义组件-圆环进度条

|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

1.使用 canvas 绘制圆环进度条,实时更新进度。

在 Angular 中创建子组件:ring-app,用于制作圆环进度条,这里我就不另行创建父组件了,直接用 Angular 的默认起始页:app.component.html 来加载子组件。

①子组件中包含:

  •   ring-app.component.css
  •   ring-app.component.html
  •   ring-app.component.ts
  •   ring-app.component.spec.ts

这里只用到了 ring-app.component.html 和 ring-app.component.ts ;因为使用 canvas 2d 来绘制,对应的 css 在 .ts 文件中就已经赋值了。

code:

ring-app.component.html

<div class="container">
    <canvas #canvas id='canvas' width="width" height="height" lineWidth="lineWidth" fontSize="fontSize" fontBold='fontBold'></canvas>
</div>

我将一些基本属性开放出来,以便父组件中可以很好的控制它的形状,当然也给了默认值,属性说明如下:

  • width:圆环的宽度
  • height:圆环的高度
  • lineWidth:圆环的粗细
  • fontSize:圆环中间字体的大小
  • fontBold:字体的粗细

ring-app.component.ts

import { Component, OnInit, ViewChild, ElementRef, Input} from '@angular/core';
@Component({
  selector: 'app-ring-app',
  templateUrl: './ring-app.component.html',
  styleUrls: ['./ring-app.component.css']
})
export class RingAppComponent implements OnInit {
  @ViewChild('canvas', { static: true }) canvasElement: ElementRef;

  constructor() {}

  @Input() width = 80; //圆环的默认宽度
  @Input() height = 80; //圆环的默认高度
  @Input() lineWidth = 10; //圆环的默认粗细
  @Input() fontSize = 14; //默认字体大小
  @Input() fontBold = 500; //默认字体粗细 (100-900)
  public canvas: any;
  public context: any;

  ngOnInit(): void {
    this.drawCanvasPercent(0); //初始化进度0
  }

  drawCanvasPercent(text:number): void {
    this.canvas = this.canvasElement.nativeElement;
    this.canvas.style.width = this.width + "px";
    this.canvas.style.height = this.height + "px";
    this.canvas.height = this.height * window.devicePixelRatio;
    this.canvas.width = this.width * window.devicePixelRatio;
    if(this.canvas.getContext){
      this.context = this.canvas.getContext('2d');
      this.context.strokeStyle = "#334960";  //圆环线条的颜色
      this.context.lineWidth = this.lineWidth * window.devicePixelRatio; //圆环的粗细
      this.context.lineCap = 'square'; //圆环结束断点的样式  butt为平直边缘 round为圆形线帽  square为正方形线帽
      this.context.beginPath();
      //Math.PI  圆周率 π, 圆的周长与直径的比例,约为 3.141592653589793
      this.context.arc(this.canvas.width/2, this.canvas.height/2, this.canvas.width/2 -10 * window.devicePixelRatio, (Math.PI / 180) * 0, (Math.PI / 180) * 360 - 0.2, false);
      this.context.stroke();//绘制路径
      this.context.closePath();
      this.drawArc(text);
    }
  }

  drawArc(text:number) {
    const startAngle = -90;
    const endAngle = (text / 100) * 360 - 90;
    this.context.beginPath();
    this.context.arc(this.canvas.width/2, this.canvas.height/2, this.canvas.width/2 -10 * window.devicePixelRatio, (Math.PI / 180) * startAngle, (Math.PI / 180) * endAngle, false);
    this.context.lineWidth = (this.lineWidth + 1) * window.devicePixelRatio;
    if(text < 30) {
      this.context.strokeStyle = 'red';
    }
    else if(text < 60) {
      this.context.strokeStyle = 'yellow';
    }
    else {
      this.context.strokeStyle = "#2FD487";
    }
    this.context.font = 'normal '+ this.fontBold +' '+ this.fontSize +'px sans-serif'; // 字体大小
    this.context.fillStyle = '#334960';  // 字体颜色
    this.context.textAlign = "center";  // 字体位置
    this.context.textBaseline = "middle"; // 字体对齐方式
    this.context.fillText(text + '%', this.canvas.width/2, this.canvas.height/2);//圆环内部展示进度值
    this.context.stroke();
    this.context.closePath();
    //window.devicePixelRatio 当前显示设备的物理像素分辨率与 CSS 像素分辨率的比率 (处理毛边)
    this.context.scale(window.devicePixelRatio, window.devicePixelRatio);
  }
}

这个时候呢,我们的子组件已经完善了,接下来就是在初始页中应用它。

②父组件包含:

  •    app.component.css
  •    app.component.html
  •    app.component.ts
  •    app.component.spec.ts

这里我只用到了app.component.html 和 app.component.ts ,app.component.html 用来引用子组件,app.component.ts 用来动态更新进度;

code:

app.component.html

<app-ring-app #ring [width]="100" [height]="100" [lineWidth]="9" [fontSize]="14" [fontBold] ='600'></app-ring-app>

app.component.ts

import { Component,  Input, OnInit, Output, EventEmitter,ViewChild} from '@angular/core';
import { RingAppComponent } from './ring-app/ring-app.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit  {
  title = 'my-ring-app';
  @ViewChild('ring') ring:RingAppComponent;
  @Input() ringText = 10;

  ngOnInit(): void {
    setInterval(() => {
      if(this.ringText < 100)
      {
        this.ringText += 5;
        this.ring.drawCanvasPercent(this.ringText);
      }
      else 
      {
        this.ringText = 10;
      }
    }, 3000);
  }
}

  父组件中写了一个定时刷新(3秒刷一次,每次增加百分之五的进度),主要目的就是看看效果;

  以上代码的实现就已经完成了,这里主要是个人记录一下,如果能够帮到小伙伴们,那更好,不喜勿喷!谢谢!

  ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

2. 使用 SVG 绘制圆环进度条,实时更新进度。

子组件与父组件同上,实现效果一样,下面是Code 输出;

Code:

ring-app.component.html

<div [ngStyle]="ring" ringWidth="width" ringHeight="height">
    <svg viewBox="0 0 100 100">
        <path d="M 50 50 m -40 0 a 40 40 0 1 0 80 0  a 40 40 0 1 0 -80 0"
            fill="none"
            stroke="#334960"
            [attr.stroke-width]="lineWidth">
        </path>
        <path d="M 50 50 m -40 0 a 40 40 0 1 0 80 0  a 40 40 0 1 0 -80 0"
            fill="none"
            [attr.stroke]='ringStroke'
            stroke-linecap="butt"
            class="my-svg-path"
            [ngStyle]="svgPath"
            [attr.stroke-width]="lineWidth+1">
        </path>
    </svg>
    <div class="ringTextDiv">
        <label class="ringLabel" [ngStyle]="ringLabelStyle">{{ringText}}</label>
    </div>
</div>

 ring-app.component.css

.ringTextDiv {
    float:left;
}

.ringLabel {
    text-align:center;
    position:absolute;
}

.my-svg-path {
    stroke-dasharray: 252.2px;
    transform: rotateZ(90deg);
    transform-origin: 50% 50%;
}

  ring-app.component.ts

import { Component, OnInit, ViewChild, ElementRef, Input} from '@angular/core';
@Component({
  selector: 'app-ring-app',
  templateUrl: './ring-app.component.html',
  styleUrls: ['./ring-app.component.css']
})
export class RingAppComponent implements OnInit {
  @ViewChild('canvas', { static: true }) canvasElement: ElementRef;

  constructor() {}

  @Input() width = 80;
  @Input() height = 80;
  @Input() lineWidth = 10;
  @Input() fontSize = 14;
  @Input() fontBold = 500;
 
  @Input() ring:any;
  @Input() svgPath:any;
  @Input() ringStroke :string;
  @Input() ringText = "0%";
  @Input() ringLabelStyle:any;

  ngOnInit(): void {
    this.ringSvgStyle();
    this.loadSvgPercent(0);
  }

ringSvgStyle()
{
  this.ring = {"width": this.width + 'px',"height": this.height + 'px'}
  this.ringLabelStyle = {"font-size": this.fontSize + 'px',"font-weight": this.fontBold,"width": this.width + 'px',"line-height": this.height + 'px'}
  this.ringStroke = "#2FD487";
}

loadSvgPercent(text:number) {
  this.ringText = (text) + '%';
   var dashoffset = -252.2 + (text / 100) * 252.2;
   if(dashoffset <= 0){
    if(text < 30) {
      this.ringStroke = 'red';
    }
    else if(text < 60) {
      this.ringStroke = 'yellow';
    }
    else {
      this.ringStroke = "#2FD487";
    }
    this.svgPath = {"stroke-dashoffset": dashoffset + 'px'}
   }
}
}

app.component.html

<app-ring-app #ring [width]="100" [height]="100" [lineWidth]="10" [fontSize]="14" [fontBold] ='600'></app-ring-app>

app.component.ts

import { Component,  Input, OnInit, Output, EventEmitter,ViewChild} from '@angular/core';
import { RingAppComponent } from './ring-app/ring-app.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit  {
  title = 'my-ring-app';
  @ViewChild('ring') ring:RingAppComponent;
  @Input() ringText = 10;

  ngOnInit(): void {
    setInterval(() => {
      if(this.ringText < 100)
      {
        this.ringText += 5;
        this.ring.loadSvgPercent(this.ringText);
      }
      else 
      {
        this.ringText = 10;
      }
    }, 300);
  }
}

|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值