저번에 연결한 MSW에 요청을 보내고 받은 데이터로 D3.js 라이브러리를 이용해 선그래프를 만들고 그래프를 갱신하는 기능을 만들거임.
ng g c test
로 라인그래프를 만들 test컴포넌트를 생성한다.
<h2>timeSeries!</h2>
<div id="timeSeries" [attr.width]="width" [attr.height]="height" >
</div>
app 컴포넌트에 test컴포넌트를 넣고 라디오버튼으로 데이터를 선택해서 그래프를 그릴 예정이다.
[(ngModel)]="startTime"로 value를 test.component.ts의 start변수에 전달해주고 ngOnChanges(){}안에 코드를 넣어 라디오버튼을 선택해 start값이 변경되면 그에 맞는 그래프를 그려주게 할 예정이다.
<div>
<input type="radio" id="10min" value="600000" [(ngModel)]="startTime" name="selector" checked>
<label for="10min">10분</label>
<input type="radio" id="30min" value="1800000" [(ngModel)]="startTime" name="selector" >
<label for="30min">30분</label>
<input type="radio" id="hour" value="3600000" [(ngModel)]="startTime" name="selector" >
<label for="hour">1시간</label>
</div>
<app-test [start]="startTime" ></app-test>
<app-pie [start]="startTime" ></app-pie>
<app-value [start]="startTime"></app-value>
timeSeries Div 안에 svg태그를 붙이고 크기를 설정한다.
그리고 svg에 g태그(요소를 그룹화함)를 붙이고 위치를 조정한다.
this.svg = d3.select('#timeSeries')
.append("svg")
.attr("width", this.width+this.margin.left+this.margin.right)
.attr("height", this.height+this.margin.top+this.margin.bottom)
.append('g')
.attr('transform', 'translate(' + -1+ ',' + this.margin.top + ')');
x축, y축의 값을 설정한다.
x축은 시작시간과 끝시간을 domain으로 설정하고 range의 영역안에 domain 스케일을 맞춘다.
range와 translate는 직접 축을 생성해가며 +,-를 조절하여 위치를 잡았다.
y축의 범위도 0~가장큰값으로 설정한다.
x축은 아래쪽, y축은 왼쪽으로 설정하고 ticks는 눈금의 개수임
위에서 생성한 svg에 x,y축을 생성한다.
let xScale = d3.scaleTime().domain([new Date(this.data[0].x), new Date(this.data[msg.times.length-1].x)]).range([80, this.width]);
let yScale = d3.scaleLinear().domain([0, max]).range([300, 30]);
const xAxis = d3.axisBottom(xScale).ticks(12)
const yAxis = d3.axisLeft(yScale).ticks(10);
const xAxisSVG = this.svg.append("g").attr("class","xAxis").attr("transform", "translate(0,"+330+ ")")
.style("font-size","15px").attr("stroke-width","2")
.call(xAxis);
const yAxisSVG = this.svg.append("g").attr("class","yAxis").attr("transform", "translate(80,0)")
.style("font-size","15px").attr("stroke-width","2")
.call(yAxis)
.call((g:any) => g.select(".domain").remove())
.call((g:any) => g.selectAll(".tick line").clone()
.attr("x2", this.width)
.attr("stroke-opacity", 0.1))
이제 svg에 선을 그릴 차례
데이터 형식을 [{x: 1677512170000,y:8006603}, ....] 처럼 hash형식으로 만들었음. 따라서 x축에는 x값, y축에는 y값을 매핑시킨다.
그리고 svg에 path태그를 붙여 선을 그려준다.
let linearGenerator = d3.line()
.curve(d3.curveLinear)
.x((d:any)=>xScale(new Date(d.x)))
.y((d:any)=>yScale(d.y))
this.svg
.append("path")
.attr("class", "line")
.attr("d", linearGenerator(this.data))
.attr("fill", "none")
.attr("stroke-width", 1)
.attr("stroke", "steelblue")
10분 전부터 현재시간까지 데이터
30분 전부터 현재시간까지 데이터
1시간 전부터 현재시간까지 데이터
import { Component, Input, OnInit, OnChanges } from '@angular/core';
import { DataService } from '../data.service';
import * as d3 from 'd3';
@Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit, OnChanges {
@Input() start = '';
margin = {top: 10, right: 30, bottom: 30, left: 50}
width =1000
height=350;
data : any[]=[]
svg: any
constructor(private dataService: DataService){
}
ngOnChanges(){
d3.select("#timeSeries").selectChildren().remove()
this.drawchart(+this.start)
}
drawchart = (time:Number) =>{
var now = Date.now()
this.dataService.getData(now- +time, now,"timeseries").subscribe(
msg=>{
this.data=[]
let max=0
for(let i=0; i<msg.data.length; i++){
if(msg.data[i] > max){
max=msg.data[i]
}
this.data.push({
x:msg.times[i],
y:msg.data[i]
})
}
this.svg = d3.select('#timeSeries')
.append("svg")
.attr("width", this.width+this.margin.left+this.margin.right)
.attr("height", this.height+this.margin.top+this.margin.bottom)
.append('g')
.attr('transform', 'translate(' + -1+ ',' + this.margin.top + ')');
let xScale = d3.scaleTime().domain([new Date(this.data[0].x), new Date(this.data[msg.times.length-1].x)]).range([80, this.width]);
let yScale = d3.scaleLinear().domain([0, max]).range([300, 30]);
const xAxis = d3.axisBottom(xScale).ticks(12)
const yAxis = d3.axisLeft(yScale).ticks(10);
const xAxisSVG = this.svg.append("g").attr("class","xAxis").attr("transform", "translate(0,"+330+ ")")
.style("font-size","15px").attr("stroke-width","2")
.call(xAxis);
const yAxisSVG = this.svg.append("g").attr("class","yAxis").attr("transform", "translate(80,0)")
.style("font-size","15px").attr("stroke-width","2")
.call(yAxis)
.call((g:any) => g.select(".domain").remove())
.call((g:any) => g.selectAll(".tick line").clone()
.attr("x2", this.width)
.attr("stroke-opacity", 0.1))
let linearGenerator = d3.line()
.curve(d3.curveLinear)
.x((d:any)=>xScale(new Date(d.x)))
.y((d:any)=>yScale(d.y))
this.svg
.append("path")
.attr("class", "line")
.attr("d", linearGenerator(this.data))
.attr("fill", "none")
.attr("stroke-width", 1)
.attr("stroke", "steelblue")
});
}
}