angular + material实现栅格条纹式chart表

图下示:


Grid Chart
啊啊啊啊

设计稿到手一脸懵逼,
但是!
甲方爸爸的需求怎么能轻易放弃呢~哼哼哈嘿

Chart.js- --想了3秒钟,还是默默放弃🤦‍♀️🤦‍♀️🤦‍♀️
D3 ----搜啊搜啊搜,就是木有咱们这样滴表格系统😭😭😭
给跪了~~~~~~


向大佬低头

用到的技术

Angular5+
Angular- Material
SCSS
父子信息传递 @Input()
mock数据
ngClass三元判断
*ngFor + index索引的运用
*ngStyle
ngOnInit运用
如何遍历数字次数

一、配置(angular + material)

ng  new app-chart  --style=scss//创建angular项目
ng g c chart //创建chart组件
npm install --save @angular/material @angular/cdk //安装angular-material
npm install --save @angular/animations //安装material对应的animations

二、思路

  • 项目中我们从后台获取的数据mockData是readonly模式,我们前台通过JSON.parse+ JSON.stringfy对新变量newMockData进行操作
  • 一共50个小格子,按照百分比划分分对应格子数,百分比都是整数那好说,不是整数就单独拎出来小数判断,小数最大的进一位,其他的舍弃小数,
  • angular如果想遍历数字,那也要通过转换成数组的形式
    • 比如数字是8,表示我们想遍历8次,可以通过for循环push 进去一个空数据8个值,这样就达到效果了(具体见ts文件的setBar方法)
  • 通过ngClass进行三元判断给奇数相添加class,从而实现奇数时stick在下面,偶数在上面
  • 如果占比小于一个格子的话,至少我们也要保证显示一个格子,在setBar里Math.floor如果是获取对应数据占比格子数是0就进1位,
  • 如果传来的数据里有num=0的情况,在一开始获取mockData的时候就进行filter过滤掉(setbar)
  • 如果所有的数据num都是0,就改成了默认全灰


    grey

三、项目html + scss + ts代码

  • 挂载子组件并传递数据
// app.component.html 
<app-chart [mockData]="mockData"></app-chart>
  • 父组件mock数据
// app.component.ts 
 mockData = {
    propertion: [
      {
        name: 'Review required',  // 每个bar的文本信息
        color: '#F1A636',  //各自对应的color和background-color
        bars: [], // 计算,用来存放判断后最终分得的格子数
        num: 30,  // 各自的数量
        total: 120,  //总数量
      },
      {
        name: 'Issue confirmed',
        color: '#398F90',
        bars: [],
        num: 25,
        total: 120,
      },
      {
        name: 'No issue',
        color: '#8DA656',
        bars: [],
        num: 65,
        total: 120,
      },
    ]
  };
  • 子组件input获取数据
// chart.component.ts
import { Component, OnInit, Input } from '@angular/core';
@Input() mockData;
  • 子组件html
<div class="chart clearfix">
  <!-- 当数据不为空 -->
  <div class="not-empty" *ngIf="!isEmpty">
      <!-- 奇数时添加class="odd" stick在下面 -->
    <div class="item" [ngClass]="{'odd': index %2 !==0}" *ngFor="let item of newMockData; index as index">
      <div class="stick" [ngStyle]="{'background-color':item.color}">
        <div class="item-text" [ngStyle]="{'color':item.color}">{{item.name}} </div>
        <div class="item-cent" [ngStyle]="{'color':item.color}">{{item.cent }}%
          <span class="item-showCent">({{item.num }}/{{item.total}})</span>
        </div>
      </div>
      <div class="bars clearfix">
<!-- 根据各自分得的小格子数,遍历显示在页面 -->
        <div class="bar" *ngFor="let i of item.bars" [ngStyle]="{'background-color':item.color}"></div>
      </div>
    </div>
  </div>
  <div class="empty" *ngIf="isEmpty">
    <div class="empty-bars bar" *ngFor="let i of emptyArray" [class.empty-bg]="isEmpty">
    </div>
  </div>
</div>
  • 子组件scss样式
.chart {
  display: inline-block;
  height: 40px;
  position: relative;
  right: -40px;
  top: 70px;
  font-family: 'Roboto', Helvetica, Arial, sans-serif;
  .clearfix:after {
    content: '';
    display: block;
    visibility: hidden;
    line-height: 0;
    font-size: 0;
    height: 0;
    clear: none;
  }
  .bar {
    width: 5px;
    height: 40px;
    float: left;
    margin-right: 5px;
  }
  .item {
    display: inline-block;
  }
  .item-text {
    position: absolute;
    top: 2px;
    left: 10px;
    white-space: nowrap; // 禁止字体换行
    font-size: 18px;
  }
  .item-cent {
    position: absolute;
    top: 25px;
    left: 10px;
    white-space: nowrap;
    font-size: 18px;
    font-weight: 700;
  }
  .item-showCent {
    font-weight: 300;
  }
  .stick {
    width: 5px;
    height: 60px;
    position: absolute;
    top: -59px;
  }
  .odd .stick {
    top: 40px;
  }
}
// 数据全空时的小格子颜色
.empty-bg {
  background-color: rgba(40, 14, 0, 0.1);
}
  • 子组件ts文件
export class ChartComponent implements OnInit {
  @Input() mockData;
  constructor() { }
  totalBars = 50; // 一共50个小格子
  newMockData: any;
  realData = [];
  isEmpty = false;
  emptyArray = [];
  ngOnInit() {
    // this.mockData.propertion[0].num = 100;
    this.cloneMockData();
    this.setCent();
    this.setBar();
  }
  cloneMockData() {
    // 如果传来的参数为0就直接filter掉不进入后续数据操作
    this.newMockData = JSON.parse(
      JSON.stringify(this.mockData),
    ).propertion.filter(el => {
      return el.num !== 0;
    });
    console.log('newmock data: ', this.newMockData);
  }
  // 获取每个值的百分比
  setCent() {
    this.newMockData.propertion.forEach(ele => {
      ele.cent = ((ele.num / ele.total) * 100).toFixed(0);
    });
  }
  setBar() {
    // 如果数据是空的,就显示灰色条条
    if (this.newMockData.length === 0) {
      this.isEmpty = true;
      for (let i = 0; i < this.totalBars; i++) {
        this.emptyArray.push(1);
      }
      return;
    } else {
      // [10.5, 20.3, 19.2]
      const barsAssign = this.newMockData.map(ele => {
        return (ele.num / ele.total) * this.totalBars;
      });
      let bars = []; // [10, 20, 19];
      const points = []; // [0.5, 0.3, 0.2];
      let sum = 0;

      barsAssign.forEach(ele => {
        let integer = Math.floor(ele);
        points.push(ele - integer);
        if (integer === 0) {
          integer = 1;
        }
        bars.push(integer);
        sum += integer;
      });
      while (sum < this.totalBars) {
        // 获取最大的小数位在points的索引值;进1位,
        const maxPoint = Math.max(...points);
        const index = points.indexOf(maxPoint);
        bars[index] = bars[index] + 1; // [11, 20, 19];
        points[index] = 0; // 最大points对应的bars的加1之后,就置为0,下次就不会再加。
        sum++;
      }

      // angular想遍历数字 ===》要通过转成数组方式
      bars = bars.map(ele => {
        const temp = [];
        for (let i = 0; i < ele; i++) {
          temp.push(1);
        }
        return temp;
      });
      // 填充newMockData数据里的bar作为格子数
      bars.forEach((bar, i) => {
        this.newMockData[i].bars = bar;
      });
      console.log(this.newMockData);
    }
  }
最终效果

四、复用性

  • 基本上只要后台给多少数据,都能按照这种模式自动往后添加表格


    reusability

github帐号:slwzero
github项目链接戳此处鸭

star我鸭~
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容