简体   繁体   中英

Simple Real Time D3 Line chart

I am new to D3 and trying to create a line chart which is able to add new data and update the chart. So far I have this code which is able to draw a static chart given data array:

import * as d3 from 'd3';
import CreateData from './CreateData';
import { svg } from 'd3';

const MARGIN = {TOP: 50, BOTTOM: 90, LEFT: 75, RIGHT: 50};
const WIDTH = window.innerWidth - MARGIN.LEFT - MARGIN.RIGHT;
const HEIGHT = window.innerHeight    - MARGIN.TOP - MARGIN.BOTTOM;
const duration = 500;
const max = 10;
const step = 10;
const dataClass = new CreateData(10);
let data = (dataClass).createTimeData();    /*Data aray containing 10 elements (each element is {time: DateObject, value: "Random value between 0, 10"})*/
let minData = d3.min(data, function(c) { return c.time});

export default class LiveD3Chart {
// element is the parent DOM element where you want data to be displayed
constructor (element) {
    console.log(data);
    this.svg = this.getSvgElement(element);
    this.xScale = this.getScaleX();
    this.yScale = this.getScaleY();
    this.xAxis = this.getAxisX();
    this.yAxis = this.getAxisY();
    this.line = this.getLine();
    this.chartGroup = this.svg.append('g');
    this.path = this.chartGroup.append('path');
}
getSvgElement(parent) {
    const svg = (d3.select(parent)).append('svg')
                .attr('width', WIDTH + MARGIN.LEFT + MARGIN.RIGHT)
                .attr('height', HEIGHT + MARGIN.TOP + MARGIN.BOTTOM)
                .append('g')
                .attr('transform', `translate(${MARGIN.LEFT}, ${MARGIN.TOP})`);

    return svg;
}

getScaleX() {
    const xScale = d3.scaleTime()
                .domain(d3.extent(data, function(d) {return (d.time).getTime() /*- (500 * 2)*/}))
                .range([0, WIDTH]);

    return xScale;
}

getScaleY() {
    const yScale = d3.scaleLinear()
                .domain([0, max])
                .range([HEIGHT, 0]);

    return yScale;
}

getAxisX() {


    const xAxis = this.svg.append('g')
                .attr('class', 'x-axis')
                .attr('transform', `translate(0, ${HEIGHT})`)
                .call(d3.axisBottom(this.xScale));

    return xAxis;
}

getAxisY() {
    const yAxis = this.svg.append('g')
                .attr('class', 'y-axis')
                .call(d3.axisLeft(this.yScale));

    return yAxis;
}

getLine() {
    const line = d3.line()
                .x((d) => this.xScale(d.time))
                .y(d => this.yScale(d.data));

    return line;
}

run() {
    console.log(d3.min(data, function(c) { return c.time}));
    this.path.datum(data)
                .attr('class', 'data-line glowed')
                .style('stroke', '#D073BA')
                .style('stroke-width', 2)
                .style('fill', 'none')
                .attr('transform', null)
                .attr('d', this.line);

    this.svg.append('text')
                .attr("transform", `translate(${WIDTH / 2} , ${HEIGHT + MARGIN.TOP})`)
                .style('text-anchor', 'middle')
                .text('Label X');

    this.svg.append('text')
                .attr('transform', function() {
                    return `translate(${-MARGIN.LEFT / 2}, ${HEIGHT / 2}) rotate(-90)`;
                })
                .style('text-anchor', 'middle')
                .text('Values');

    const circles = this.svg.selectAll('circle')
                .data(data)
                .enter()
                .append('circle')
                .attr('class', 'circle')
                .attr('cx', (d, i) => this.xScale(d.time))
                .attr('cy', d => this.yScale(d.data))
                .attr('r', 4)
                .style('fill', '#D073BA')
                .style('stroke', '#11141C')
                .style('stroke-width', 2);
}

Example of Output chart:

在此处输入图像描述

Following the example here: http://bl.ocks.org/Sohalt/9715be30ba57e00f2275d49247fa7118/43a24a4dfa44738a58788d05230407294ab7a348 , I was trying to implement live functionality. More specifically, I am having trouble understanding the purpose of this part: "x.domain([globalX - (max - step), globalX]);" and how it will be implemented in my case.

Would appreciate any help on the matter.

domain represents boundries of you data for instance if you chart contains prices from 500$ to 800$ your domain is: domain(500, 800)

range represents boundries of visual medium you are displaing data on so lets say you want to display that domain in pixels and you have 600 pixels that would be `range(0, 600)

scale then defines how is domain calculated into range. As we have linear scale 300 posible prices and 600 pixels we will map 1 dolar value per 2 pixels.

So value 500$ will be at pixel 0; 501$ at pixel 2; 502$ at pixel 4.... 800$ at pixel 600

So in your example your range is always the same (we still display on the same amount of pixels) but your domain is changing with time (every new data point is further to the right and they would eventualy run out of frame) so we need to change domain we want to display and you can do it by moving both numbers in domain by predefined step.

We have domain(0, 400) , range(0, 400) with linear scale 1:1 and we want new data point to be always in middle.

So we will give it value 200 . Now 200 transalte to 200 in our range so it will be drown in middle as we want.

Next data point must be further right so we have our step lets say 20 . We add our step to the value and we have 220 that would translate to 220 in our range but that is not middle.To get it to the middle we need 220 in our domain to translate to 200 on our range. We can acomplish that by moving our domain by one step to domain(20, 420) (now d(20) = r(0); d(40) = r(20)... d(220) = r(200)... d(420) = r(400) So 220 domain trnslates to 200 range what is middle and our previous point move to 180 what is movement to the left you see in the graph.

Another data poin we move another step further to 240 and domain(40, 440) (now d(40) = r(0); d(60) = r(20)... d(240) = r(200)... d(420) = r(400) So 240 domain trnslates to 200 scale, our first poin translates to 160 and second to 180

And this goes on and on.

So in your example x.domain([globalX - (max - step), globalX]); changes domain of your x scale so the 0(point where first data point is drown) is at the far right corner. Domain(-500, 0) and range(0, 500) will translate 0(d) to 500(r) what is far right corner and then you increas your value by 10 and shift your domain by 10 on every tick to drow data poin at seemingly 'same place '

.domain([0 - (500 - 10), 0])

.domain([10 - (500 - 10), 10])

.domain([20 - (500 - 10), 20])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM