Angular2使用SVG自定义图表(条形图、折线图)组件示例

时间:2021-05-28

本文实例讲述了Angular2使用SVG自定义图表(条形图、折线图)组件。分享给大家供大家参考,具体如下:

要求:用户将数据作为参数传进来,通过类型决定渲染何种类型的图标。

demo:

html:

<ngo-chart [inputParams]="options"></ngo-chart>

ts:

options = { type: 'line', //图表类型 xAxis: { //X轴的数据 data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { //X轴的数据 data: [120, 220, 150, 111, -150, 55, 60], }, width: 600, //宽 height: 500, //高 dataPadding: 8 //条形图之间的距离 };

效果:

源代码:

import { Input, OnInit, ViewChild, Component, ViewEncapsulation, ElementRef, AfterViewInit, ChangeDetectorRef,} from '@angular/core';import { NgoChartSvgParams, Scale, Axis, Chart } from './chart-svg-params';@Component({ selector: 'ngo-chart-svg', templateUrl: './chart-svg.html', styleUrls: ['./chart-svg.scss'], encapsulation: ViewEncapsulation.Native})export class NgoChartSvg implements OnInit, AfterViewInit { @Input() inputParams: NgoChartSvgParams; @ViewChild('svg') svg: ElementRef; @ViewChild('polyline') polyline: ElementRef; params: NgoChartSvgParams; AxisY: Axis; // Y轴 AxisX: Axis; // X轴 valueToPxRatio: number; // 值转px的比率 Y0: number; // 坐标轴 (0,0)的Y轴 Yscale: Array<Scale> = []; // Y轴刻度值 Xscale: Array<Scale> = []; // X轴刻度值 XgapWidth: number; // X轴刻度之间的间隙宽度 data: Array<Chart> = []; color: string; type: string; polyLinePoints: string; polyLineLength: number; constructor( private ele: ElementRef, private cd: ChangeDetectorRef ) { } ... ngOnInit() { this.initParams(); const svg = this.svg.nativeElement; const _width = this.params.width; const _height = this.params.height; svg.setAttribute('width', _width); svg.setAttribute('height', _height); // 绘制 y轴 this.drawAxisY(); this.drawScaleY(); // 绘制 x轴 this.drawAxisX(); this.drawScaleX(); this.drawRect(); if (this.params.type === 'line') { this.drawLine(); } } ngAfterViewInit() { if (this.polyline) { this.polyLineLength = this.polyline.nativeElement.getTotalLength(); this.cd.detectChanges(); } }}

html

<svg #svg> <!-- Y轴 --> <g> <line [attr.x1]="AxisY.x1" [attr.y1]="AxisY.y1+15" [attr.x2]="AxisY.x2" [attr.y2]="AxisY.y2" [attr.stroke]="color" [attr.fill]="color" style="stroke-width:3" /> <polygon [attr.points]="AxisY.arrow" /> <ng-container *ngFor="let scale of Yscale"> <line class="dash" [attr.x1]="scale.x1" [attr.x2]="scale.x2" [attr.y1]="scale.y1" [attr.y2]="scale.y2" stroke="rgba(0,0,0,0.3)" /> <text class="_label" [attr.x]="scale.x1-5" style="text-anchor: end" [attr.y]="scale.y1" [attr.fill]="color" [attr.fill]="color">{{scale.label}}</text> </ng-container> </g> <!-- X轴 --> <g> <line [attr.x1]="AxisX.x1-15" [attr.x2]="AxisX.x2" [attr.y1]="AxisX.y1" [attr.y2]="AxisX.y2" [attr.stroke]="color" [attr.fill]="color" style="stroke-width:3" /> <polygon [attr.points]="AxisX.arrow" /> <ng-container *ngFor="let scale of Xscale"> <line [attr.x1]="scale.x1" [attr.x2]="scale.x2" [attr.y1]="scale.y1" [attr.y2]="scale.y2" [attr.stroke]="color" [attr.fill]="color" style="stroke-width:1" /> <text class="_label" [attr.x]="scale.x1-XgapWidth/2" [attr.y]="AxisY.y1+15" [attr.fill]="color" style="text-anchor: middle;">{{scale.label}}</text> </ng-container> </g> <!-- 矩形 --> <ng-container *ngIf="type==='bar'"> <text x="10" y="20" fill="red">bar</text> <g> <ng-container *ngFor="let item of data"> <ng-container *ngIf="item.value<=0"> <rect class="_rect" [attr.x]="item.x" [attr.y]="item.y" [attr.width]="item.w" [attr.height]="item.h" fill="color"> <animate attributeName="height" [attr.from]="item.h*0.6" [attr.to]="item.h" begin="0s" dur="1.1s" /> </rect> <text [attr.x]="item.x+item.w/2" [attr.y]="item.y+item.h-5" fill="white" style="text-anchor: middle;">{{item.value}}</text> </ng-container> <ng-container *ngIf="item.value>0"> <rect [attr.x]="item.x" [attr.y]="item.y" [attr.width]="item.w" [attr.height]="item.h" fill="color"> <animate attributeName="y" [attr.from]="item.y+item.h*0.4" [attr.to]="item.y" begin="0s" dur="1.1s" /> <animate attributeName="height" [attr.from]="item.h*0.6" [attr.to]="item.h" begin="0s" dur="1.1s" /> </rect> <text class="_label" [attr.x]="item.x+item.w/2" [attr.y]="item.y+18" fill="white" style="text-anchor: middle;">{{item.value}} <animate attributeName="opacity" from="0" to="1" begin="0s" dur="1.1s" /> </text> </ng-container> </ng-container> </g> </ng-container> <!--折线 --> <ng-container *ngIf="type==='line'"> <text x="10" y="20" fill="red">line</text> <g> <polyline #polyline class="_polyline" [attr.points]="polyLinePoints" fill="none" [attr.stroke]='color' [attr.stroke-dasharray]="polyLineLength" [attr.stroke-dashoffset]="polyLineLength" /> <ng-container *ngFor="let item of data"> <circle [attr.cx]="item.x+item.w/2" [attr.cy]="item.y" r="2" [attr.fill]="color" [attr.stroke]='color' /> <text class="_label" [attr.x]="item.x+item.w/2" [attr.y]="item.y+20" fill="white" style="text-anchor: middle;">{{item.value}} <animate attributeName="opacity" from="0" to="1" begin="0s" dur="1.1s" /> </text> </ng-container> </g> </ng-container></svg>

css

svg { background: rgba(0, 0, 0, 0.2); border: 1px solid black;}svg * { position: static; font-size: 16px;}._polyline { fill: none; animation: lineMove 1.5s ease-in-out forwards;}@keyframes lineMove { to { stroke-dashoffset: 0; }}

一、初始化参数

//首先获取传入的参数 @Input() inputParams;//初始化 const _params: NgoChartSvgParams = { xAxis: this.inputParams.xAxis, yAxis: this.inputParams.yAxis, type: this.inputParams.type ? this.inputParams.type : 'bar', width: this.inputParams.width ? this.inputParams.width : 700, height: this.inputParams.height ? this.inputParams.height : 500, dataPadding: this.inputParams.dataPadding !== undefined ? this.inputParams.dataPadding : 8, YscaleNo: this.inputParams.YscaleNo >= 3 ? this.inputParams.YscaleNo : 6,};this.color = 'black';this.type = _params.type;this.params = _params;

二:绘制坐标轴Y轴

const _height = this.params.height;const _pad = this.params.padding;const _arrow = _pad + ',' + (_pad - 5) + ' ' + (_pad - 6) + ',' + (_pad + 12) + ' ' + (_pad + 6) + ',' + (_pad + 12); this.AxisY = { x1: _pad, y1: _height - _pad, x2: _pad, y2: _pad, arrow: _arrow};

三、绘制Y轴的刻度

const _height = this.params.height;const _width = this.params.width;// 显示label的边距const _padding = this.params.padding;const _Ydata = this.params.yAxis.data;// 显示的刻度数const _YscaleNo = this.params.YscaleNo;const _dataMax = this.getMinAndMaxData(_Ydata).dataMax;const _dataMin = this.getMinAndMaxData(_Ydata).dataMin;let _YminValue;let _YgapValue;if (_dataMin < 0) { _YgapValue = Math.ceil((_dataMax - _dataMin) / (_YscaleNo) / 10) * 10; _YminValue = Math.floor(_dataMin / _YgapValue) * _YgapValue;} else { _YgapValue = Math.ceil((_dataMax) / (_YscaleNo) / 10) * 10; _YminValue = 0;}// Y轴坐标点const _y2 = this.AxisY.y2;const _y1 = this.AxisY.y1;const _x1 = this.AxisY.x1;// Y轴刻度的间隙宽度const _YgapWidth = (_y1 - _y2) / (this.params.YscaleNo);this.valueToPxRatio = _YgapValue / _YgapWidth;// 坐标轴(0,0)的Y轴坐标const _Y0 = _y1 - Math.abs(_YminValue / this.valueToPxRatio);this.Y0 = _Y0;for (let i = 0; i < this.params.YscaleNo; i++) { const _obj: Scale = { x1: 0, x2: 0, y1: 0, y2: 0, label: '', value: 0 }; _obj.x1 = _x1; _obj.y1 = _y1 - _YgapWidth * i; _obj.x2 = _x1 + _width - 2 * _padding; _obj.y2 = _y1 - _YgapWidth * i; _obj.label = _YminValue + _YgapValue * i; this.Yscale.push(_obj);}

四、绘制X坐标轴

const _width = this.params.width;// 显示label的边距const _pad = this.params.padding;const _x2 = _width - _pad;const _y2 = this.Y0;const _arrow = (_x2 + 5) + ',' + _y2 + ' ' + (_x2 - 10) + ',' + (_y2 - 6) + ' ' + (_x2 - 10) + ',' + (_y2 + 6);this.AxisX = { x1: _pad, y1: _y2, x2: _x2, y2: _y2, arrow: _arrow};

五、绘制X轴刻度

const _width = this.params.width;const _Xdata = this.params.xAxis.data;const _Ydata = this.params.yAxis.data;const Y0 = this.Y0;const _x1 = this.AxisX.x1;const _x2 = this.AxisX.x2;const XgapWidth = ((_x2 - _x1) / (this.params.xAxis.data.length + 1));this.XgapWidth = XgapWidth;for (let i = 0; i < _Xdata.length; i++) { const _obj: Scale = { x1: 0, x2: 0, y1: 0, y2: 0, value: 0, label: '' }; _obj.y1 = Y0; _obj.y2 = Y0 + 5; _obj.label = _Xdata[i]; _obj.value = _Ydata[i]; _obj.x1 = _x1 + XgapWidth * (i + 1); _obj.x2 = _x1 + XgapWidth * (i + 1); this.Xscale.push(_obj);

六、绘制矩形

const _value = this.params.yAxis.data;const _dataPadding = this.params.dataPadding;const _XgapWidth = this.XgapWidth;for (let i = 0; i < _value.length; i++) { const element = _value[i]; const _obj: Chart = { x: 0, y: 0, w: 0, h: 0, value: 0 }; _obj.w = _XgapWidth - 2 * _dataPadding; _obj.x = this.Xscale[i].x1 - _obj.w - _dataPadding; _obj.h = Math.abs(this.Xscale[i].value / this.valueToPxRatio); _obj.value = this.Xscale[i].value; if (this.Xscale[i].value >= 0) { _obj.y = this.Y0 - (this.Xscale[i].value) / this.valueToPxRatio; } else { _obj.y = this.Y0; } this.data.push(_obj); }}

七、绘制折线

const _data = this.data;let _str = '';_data.forEach(ele => {if (ele.value < 0) { ele.y = ele.y + ele.h;} _str += (ele.x + ele.w / 2) + ',' + ele.y + ' ';});this.polyLinePoints = _str;

更多关于AngularJS相关内容感兴趣的读者可查看本站专题:《AngularJS指令操作技巧总结》、《AngularJS入门与进阶教程》及《AngularJS MVC架构总结》

希望本文所述对大家AngularJS程序设计有所帮助。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章