欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-20 14:59:09
本文实例讲述了angular2使用svg自定义图表(条形图、折线图)组件。分享给大家供大家参考,具体如下: 要求:用户将数据作为参数传进来,通过类型决定渲染何种类型的图标...

本文实例讲述了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   //条形图之间的距离
  };

效果:

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

源代码:

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;