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   //条形图之间的距离
  };

效果:

源代码:

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程序设计有所帮助。

(0)

相关推荐

  • Angular.js与Bootstrap相结合实现表格分页代码

    先给大家简单介绍angular.js和bootstrap基本概念. AngularJS 是一个 JavaScript 框架.它可通过 <script> 标签添加到 HTML 页面. AngularJS 通过 指令 扩展了 HTML,且通过 表达式 绑定数据到 HTML. Bootstrap,来自 Twitter,是目前最受欢迎的前端框架.Bootstrap 是基于 HTML.CSS.JAVASCRIPT 的,它简洁灵活,使得 Web 开发更加快捷. 最近一直学习Angular.js,在学习过程

  • 详解AngularJS中的表格使用

    表格数据本质上通常是重复的.ng-repeat指令,可以用来方便地绘制表格.下面的示例说明使用ng-repeat指令来绘制表格. <table> <tr> <th>Name</th> <th>Marks</th> </tr> <tr ng-repeat="subject in student.subjects"> <td>{{ subject.name }}</td>

  • 使用angularjs创建简单表格

    初步接手人生的第一个项目,需要用angularjs制作表格和实现各种功能,因此遇到了各种问题和以前不熟悉的知识点,在此记录下来,以供大家学习交流,解决方式可能并不完善或符合规范,如果大家有更好的方式欢迎指出,由于这个表格功能的制作是一点点添加上去的,因此我也分成几个部分介绍,日后如增加了新的功能也会不时更新的 首先,表格采用的是BootStrap样式编辑的,主要使用的是angularjs,为了方便也有jQuery的方法,在测试时需自行引入bootstrap,angularjs和jq的文件. 正文

  • angularjs表格ng-table使用备忘录

    项目中用到angularjs的表格ng-table,功能相当强大,像搜索.排序.checkbox.分页.每页表格显示数目等都有.API,demo什么的也只能参考官网了.这里做个备忘,哪天肯定还会用到. HTML: <!DOCTYPE html> <html> <meta charset="utf-8"/> <head> <script data-require="angular.js@*" data-semver

  • Angular2.0/4.0 使用Echarts图表的示例代码

    前言:在开发中现在需要使用echarts来制作图标,所以就在网络上各种找资料,最后发现ngx-echarts轮子可用.那么就走一波. 方式: 使用echarts和ngx-eachrts两个依赖,借助于ngx..,是因为echarts是基于js编写,没有ts文件.所以就使用ngx-echarts来使用即可. 第一步:安装依赖 npm install echarts --save npm install ngx-echarts --save 第二步:在Module中引入 import { NgxEc

  • Angular实现svg和png图片下载实现

    我经常思考,在面临一个不确定问题时,以往的经验究竟有无辅助作用?如果把经验遗忘会产生何种程度的影响?在上下求索未果之后,如何找回曾经的感觉,恰若灵光一现?凡此种种,终是要思考总结的,这篇文章便是我的反思之作. 本篇文章会记述一些实用的svg与png之间的转换技巧并强调一种思考原则. 概述 技巧 svg和png图片转换和下载 解决chrome data url too large下载问题 解决@ViewChild未及时刷新问题 原则 永远从问题最近的地方开始分析 理解下面这些内容的前提是具备一些A

  • 详解angularjs实现echart图表效果最简洁教程

    本文介绍了详解angularjs实现echart图表效果最简洁教程,分享给大家,具体如下: ehcart是百度做的数据图表,基于原生js.接口和配置都写的很好很易读,还可以用于商用. 一 echart包引用 下载解压,放入lib中. 下载地址:echart_jb51.rar 并在index.html中引用如图两个js文件. app.js中引用angular-echarts 二 页面 html页面 <!--饼图--> <div> <donut-chart config=&quo

  • AngularJS实现表格的增删改查(仅限前端)

    用AngularJS实现对表格的增删改查(仅限前端),具体代码: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>实现表格的增删改查</title> <meta http-equiv="keywords" content="keyword1,keyword2,keywo

  • 最棒的Angular2表格控件

    现在市面上有大量的JavaScript数据表格控件,包括开源的第三方的和自产自销的.可以说Wijmo的Flexgrid是目前适应Angular 2的最好的表格控件. Angular 2数据表格基本要求: 更小.更快.更熟悉. 为了使用Angular 2表格,首先你需要了解表格的基本要求.FlexGrid开始于1996年,当时使用C++为Visual Basic编写的控件.多年来,它不断进化并在多个平台得到完善,尤其是JavaScript平台.FlexGrid 因为Flex的理念而命名,控件应当包

  • Angular中实现树形结构视图实例代码

    近两年当中使用Angular开发过很多项目,其中也涉及到一些树形结构视图的显示,最近的在项目中封装了大量的组件,一些组件也有树形结构的展示,所以写出来总结一下. 相信大家都知道,树结构最典型的例子就是目录结构了吧,一个目录可以包含很多子目录,子目录又可以包含若干个子孙目录,那咱们今天就以目录结构为例来说明一下Angular中树结构的实现. 首先,我们希望封装一个组件,用于显示整个目录的树形机构,代码如下: <!DOCTYPE html> <html ng-app="treeDe

随机推荐