UNPKG

abstruse

Version:
142 lines (115 loc) 3.6 kB
import { Component, ElementRef, OnChanges, OnDestroy, SimpleChanges, Input } from '@angular/core'; import { select, pie, arc, interpolate, easeCubic } from 'd3'; @Component({ selector: 'app-progress-chart', templateUrl: 'app-progress-chart.component.html' }) export class AppProgressChartComponent implements OnChanges, OnDestroy { @Input() percent: number; el: HTMLElement; pathChart: any; middleCount: any; arcLine: any; ratio: number; lastCount: number; constructor(private elementRef: ElementRef) { this.lastCount = 0; } ngOnChanges(changes: SimpleChanges) { if ('percent' in changes) { if (!this.el) { this.render(); } this.animate(); } } ngOnDestroy() { } render(): void { this.el = this.elementRef.nativeElement.querySelector('.progress-chart'); this.ratio = this.percent / 100; const p = pie().value((d: any) => d).sort(null); const w = this.el.clientWidth; const h = this.el.clientHeight; const outerRadius = (w / 2) - 10; const innerRadius = 100; const color = ['#5AD946', '#2BB415', '#E2E7EE']; const svg = select(this.el) .append('svg') .attr('width', w) .attr('height', h); const g = svg .append('g') .attr('transform', `translate(${w / 2}, ${h / 2})`); this.createGradient(svg, color[0], color[1], 'progressGradient'); const a = arc() .innerRadius(innerRadius) .outerRadius(outerRadius) .startAngle(0) .endAngle(2 * Math.PI); this.arcLine = arc() .innerRadius(innerRadius) .outerRadius(outerRadius) .startAngle(0); const pathBackground = g.append('path') .attr('d', a) .style('fill', color[2]); this.pathChart = g.append('path') .datum({ endAngle: 0 }) .attr('d', this.arcLine) .style('fill', `url(${document.location.href}#progressGradient)`); this.middleCount = g.append('text') .text((d: any) => d) .attr('class', 'middle-text') .attr('text-anchor', 'middle') .attr('dy', 30) .attr('dx', -10) .style('fill', color[1]) .style('font-size', '60px'); g.append('text') .text('%') .attr('class', 'percent') .attr('text-anchor', 'middle') .attr('dx', 35) .attr('dy', 0) .style('fill', color[1]) .style('font-size', '40px'); } arcTween = (transition: any, newAngle: number) => { transition.attrTween('d', (d: any) => { const i = interpolate(d.endAngle, newAngle); const iCount = interpolate(this.lastCount, this.percent); return (t) => { d.startAngle = d.endAngle; d.endAngle = i(t); this.lastCount = Math.floor(iCount(t)); this.middleCount.text(this.lastCount); return this.arcLine(d); }; }); } animate = () => { this.ratio = this.percent / 100; this.pathChart.transition() .duration(250) .ease(easeCubic) .call(this.arcTween, ((2 * Math.PI)) * this.ratio); } createGradient(svg: any, color1: string, color2: string, id: string): void { const defs = svg.append('defs'); const gradient = defs.append('linearGradient') .attr('id', id) .attr('x1', '0%') .attr('y1', '0%') .attr('x2', '50%') .attr('y2', '100%') .attr('spreadMethod', 'pad'); gradient.append('stop') .attr('offset', '50%') .attr('stop-color', color1) .attr('stop-opacity', 1); gradient.append('stop') .attr('offset', '100%') .attr('stop-color', color2) .attr('stop-opacity', 1); } }