【问题标题】:Simple Real Time D3 Line chart简单的实时 D3 折线图
【发布时间】:2020-08-29 18:37:26
【问题描述】:

我是 D3 的新手,正在尝试创建一个能够添加新数据和更新图表的折线图。到目前为止,我有这段代码能够在给定数据数组的情况下绘制静态图表:

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);
}

输出图表示例:

按照此处的示例:http://bl.ocks.org/Sohalt/9715be30ba57e00f2275d49247fa7118/43a24a4dfa44738a58788d05230407294ab7a348,我试图实现实时功能。更具体地说,我无法理解这部分的目的:“x.domain([globalX - (max - step), globalX]);”以及在我的情况下将如何实施。

对于此事的任何帮助将不胜感激。

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    domain 代表您数据的边界,例如,如果您的图表包含从 500 美元到 800 美元的价格,您的域是:domain(500, 800)

    range 表示您要在其上显示数据的视觉介质的边界,因此假设您想以像素为单位显示该域,并且您有 600 个像素,即 `range(0, 600)

    scale 然后定义域如何计算到范围内。由于我们有 300 个可能的价格和 600 个像素的线性比例,我们将每 2 个像素映射 1 个美元值。

    所以值 500$ 将位于像素 0;像素 2 处为 501 美元;像素 4 处为 502 美元 .... 像素 600 处为 800 美元

    因此,在您的示例中,您的 range 始终相同(我们仍然显示相同数量的像素),但您的域会随着时间而变化(每个新数据点都更靠右,它们最终会用完框架)所以我们需要更改我们想要显示的域,您可以通过预定义的步骤移动域中的两个数字来完成。

    我们有 domain(0, 400)range(0, 400) 的线性比例为 1:1,我们希望新数据点始终位于中间。

    所以我们会给它赋值200。现在200 在我们的范围内转换为200,所以它会被淹没在我们想要的中间。

    下一个数据点必须更靠右,所以我们有我们的step,比如说20。我们将我们的步骤添加到值中,我们有220,它将在我们的范围内转换为220,但这不是中间。要使其到达中间,我们需要在我们的域中将220 转换为200 on我们的范围。我们可以通过将我们的域移动一个 stepdomain(20, 420) 来实现这一点(现在 d(20) = r(0); d(40) = r(20) ... d(220) = r(200) ... d(420) = r(400) 所以220 域转换为200 范围,中间是什么,我们之前的点移动到180,你在图中看到的向左移动。

    另一个数据点,我们进一步移动到240domain(40, 440)(现在 d(40) = r(0); d(60) = r(20) ... d(240) = r( 200) ... d(420) = r(400) 所以240 域转换为200 规模,我们的第一个点转换为160,第二个转换为180

    这种情况一直持续下去。

    所以在你的例子中x.domain([globalX - (max - step), globalX]); 更改 x 比例的domain,因此 0(第一个数据点被淹没的点)位于最右上角。 Domain(-500, 0) 和 range(0, 500) 会将 0(d) 转换为 500(r) 最右边的角落,然后你将你的值增加 10 并将你的域移动 10 在每个刻度上移动数据以获取数据指向看似“同一个地方”

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

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

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

    【讨论】:

    • 有道理!感谢您的详细解释。
    猜你喜欢
    • 2013-09-28
    • 2012-11-19
    • 2017-04-07
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 2022-12-09
    • 1970-01-01
    • 2017-06-24
    相关资源
    最近更新 更多