javascript - 路径和图形元素未在 D3 中显示
问题描述
我想根据我的数据集绘制一系列线。很难弄清楚出了什么问题,因为我没有收到任何错误消息。我希望比我更有经验的人熟悉一些常见的陷阱。
这是对两个示例的改编:使用 es6 类构建 d3 代码和d3 多线图
以下是相关部分(我认为出现问题的地方):
createScales(){
this.keynames = d3.scaleOrdinal();
this.keynames.domain(Object.keys(this.data[0]).filter(key => key!=='date'));
this.keymap = this.keynames.domain().map(
keyname => ({name: keyname, values: this.data.map(
d => ({date: d.date, key: +d[keyname]})
)})
);
const m = this.margin;
const xExtent = d3.extent(this.keymap, d => d.date);
const yExtent = [0,d3.max(this.keymap, d => Math.max(d.values, function(v){ return v.key }) )];
this.xScale = d3.scaleTime()
.range([0, this.width-m.right])
.domain(xExtent).nice();
this.yScale = d3.scaleLinear()
.range([this.height-(m.top+m.bottom), 0])
.domain(yExtent).nice();
}
addAxes(){
const m = this.margin;
const xAxis = d3.axisBottom()
.scale(this.xScale)
.ticks(8);
const yAxis = d3.axisLeft()
.scale(this.yScale)
.ticks(4);
this.plot.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${this.height-(m.top+m.bottom)})`)
.call(xAxis.ticks(8)); //more than 8 ticks shown
this.plot.append("g")
.attr("class", "y axis")
.call(yAxis.ticks(4)) //more than 4 ticks shown, rest of chain not working
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("$USD");
}
addLine(){
const line = d3.line()
.x(function(d){console.log(d); return this.xScale(d.date)}) //no log, no line
.y(function(d){return this.yScale(d.key)});
this.plot.append('path')
.datum(this.keymap)
.classed('line', true)
.attr('d', function(d){return line(d.values)}) //logs correct values, no line(data structure outlined below(1))
.style('stroke', this.lineColor || 'red')
.style('fill', 'none');
}
(1)
此控制台日志中显示的数据示例:
Array(4):
0: {name: aapl, values: Array(691)}
values: [0 .. 99]:
0: {date: Monday etc.., key: 38}
1: {date: Tuesday etc.., key: 39}
2: {date: Wednesday etc.., key: 38} ... etc
1: {name: tsla, values: Array(691)}
etc.
我看不出问题出在哪里.. 我的数据结构与示例中的完全相同,而且我之前的无类实现效果很好.. 未显示的轴元素只是蛋糕,但也许他们指出了一个更大的问题。谢谢你。
完整的代码和数据:文件1:plot.js
const chart = new Chart({element: document.querySelector('#graph')});
let getData = d3.csv('d1.csv', function(d){
function removeNaN(e,c){
if (e>0) {return e} else {return c}
}
return { date: d3.timeParse("%Y-%m-%d")(d.Date),
aapl : d.AAPL, tsla : d.TSLA,
aapl_sma: removeNaN(+d.SMA_AAPL,d.AAPL),
tsla_sma: removeNaN(+d.SMA_TSLA,d.TSLA)
}
}).then(init);
function init(data){
chart.setData(data);
}
文件 2:chart.js
class Chart{
constructor(opts){
this.data = opts.data;
this.element = opts.element;
}
draw(){
this.width = this.element.offsetWidth;
this.height = this.width/2;
this.padding = 50;
this.margin = {
top : 20,
bottom : 20,
left : 30,
right : 50
};
this.element.innerHTML = '';
const svg = d3.select(this.element).append('svg');
svg.attr('width', this.width);
svg.attr('height', this.height);
this.plot = svg.append('g')
.attr('transform', `translate(${this.margin.left},${this.margin.top})`);
this.createScales();
this.addAxes();
this.addLine();
}
createScales(){
this.keynames = d3.scaleOrdinal();
this.keynames.domain(Object.keys(this.data[0]).filter(key => key!=='date'));
this.keymap = this.keynames.domain().map(
keyname => ({name: keyname, values: this.data.map(
d => ({date: d.date, key: +d[keyname]})
)})
);
const m = this.margin;
const xExtent = d3.extent(this.data, d => d.date);
const yExtent = [0,d3.max(this.keymap, d => d3.max(d.values, function(v){ return v.key }) )];
this.xScale = d3.scaleTime()
.range([0, this.width-m.right])
.domain(xExtent).nice();
this.yScale = d3.scaleLinear()
.range([this.height-(m.top+m.bottom), 0])
.domain(yExtent).nice();
}
addAxes(){
const m = this.margin;
const xAxis = d3.axisBottom()
.scale(this.xScale);
const yAxis = d3.axisLeft()
.scale(this.yScale);
this.plot.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${this.height-(m.top+m.bottom)})`)
.call(xAxis.ticks(8));
this.plot.append("g")
.attr("class", "y axis")
.call(yAxis.ticks(4))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("$USD");
}
addLine(){
const line = d3.line()
.x(function(d){console.log(d); return this.xScale(d.date)})
.y(function(d){return this.yScale(d.key)});
this.plot.append('path')
.datum(this.keymap)
.classed('line', true)
.attr('d', function(d){console.log(d); return line(d.values)})
.style('stroke', this.lineColor || 'red')
.style('fill', 'none');
console.log(this.plot)
}
setColor(newColor){
this.plot.select('.line')
.style('stroke', newColor);
this.lineColor = newColor;
}
setData(data){
this.data = data;
this.draw();
}
}
文件 3:index.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Graphs of the US Economy</title>
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@200;400;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
<style>
</style>
</head>
<body>
<div id="container" class="container">
<div id='graph'></div>
<div id="sections">
<div><h1>THIS IS A TEST SECTION THAT I'M USING TO FIGURE OUT HOW MY CODE WORKS</h1></div>
<div><h1>THIS IS A SECOND TEST SECTION THAT I'M USING TO FIGURE OUT HOW MY CODE WORKS</h1></div>
<div><h1>THIS IS A THIRD TEST SECTION THAT I'M USING TO FIGURE OUT HOW MY CODE WORKS</h1></div>
<div><h1>THIS IS A FOURTH TEST SECTION THAT I'M USING TO FIGURE OUT HOW MY CODE WORKS</h1></div>
<div><h1>THIS IS A FIFTH TEST SECTION THAT I'M USING TO FIGURE OUT HOW MY CODE WORKS</h1></div>
<div><h1>THIS IS A SIXTH TEST SECTION THAT I'M USING TO FIGURE OUT HOW MY CODE WORKS</h1></div>
</div>
</div>
<script src="https://d3js.org/d3.v6.js"></script>
<script src="graph-scroll.js"></script>
<script src="chart.js"></script>
<script src="plot.js"></script>
<script>
</script>
</body>
</html>
文件 4:d1.csv,39 行:
Date,AAPL,SMA_AAPL,TSLA,SMA_TSLA
2018-12-31,38.33848571777344,,66.55999755859375,
2019-01-02,38.382225036621094,,62.02399826049805,
2019-01-03,34.55907440185547,,60.071998596191406,
2019-01-04,36.03437805175781,,63.53799819946289,
2019-01-07,35.95417022705078,,66.99199676513672,
2019-01-08,36.63956832885742,,67.06999969482422,
2019-01-09,37.26177215576172,,67.70600128173828,
2019-01-10,37.380863189697266,,68.99400329589844,
2019-01-11,37.013858795166016,,69.4520034790039,
2019-01-14,36.4572868347168,,66.87999725341797,
2019-01-15,37.20343780517578,,68.88600158691406,
2019-01-16,37.657936096191406,,69.20999908447266,
2019-01-17,37.88154602050781,,69.46199798583984,
2019-01-18,38.11487579345703,,60.45199966430664,
2019-01-22,37.259342193603516,,59.784000396728516,
2019-01-23,37.410030364990234,,57.518001556396484,
2019-01-24,37.113521575927734,,58.301998138427734,
2019-01-25,38.34333801269531,,59.40800094604492,
2019-01-28,37.988487243652344,,59.2760009765625,
2019-01-29,37.59474182128906,,59.492000579833984,
2019-01-30,40.16377258300781,,61.75400161743164,
2019-01-31,40.453006744384766,,61.40399932861328,
2019-02-01,40.472450256347656,,62.44200134277344,
2019-02-04,41.622066497802734,,62.577999114990234,
2019-02-05,42.33420181274414,,64.2699966430664,
2019-02-06,42.34878158569336,,63.444000244140625,
2019-02-07,41.546722412109375,,61.50199890136719,
2019-02-08,41.59553909301758,,61.15999984741211,
2019-02-11,41.35633087158203,,62.56800079345703,
2019-02-12,41.71269989013672,38.606483713785806,62.36199951171875,63.48539975484212
2019-02-13,41.539398193359375,38.71318079630534,61.63399887084961,63.32119979858398
2019-02-14,41.69073486328125,38.823464457194014,60.75400161743164,63.278866577148435
2019-02-15,41.59797286987305,39.05809440612793,61.57600021362305,63.32899996439616
2019-02-19,41.72246551513672,39.247697321573895,61.12799835205078,63.24866663614909
2019-02-20,41.990962982177734,39.44892374674479,60.512001037597656,63.032666778564455
2019-02-21,41.75419616699219,39.619411341349284,58.24599838256836,62.738533401489256
2019-02-22,42.22041702270508,39.78469950358073,58.94200134277344,62.44640007019043
2019-02-25,42.5279655456543,39.95626958211263,59.75400161743164,62.13840001424153
解决方案
主要问题出在addLine
方法上。首先,尝试将 a 添加console.log(this)
到您传递给的函数中.x()
:
const line = d3.line()
.x(function(d){console.log('this:', this); return this.xScale(d.date)})
.y(function(d){return this.yScale(d.key)});
您会在此函数中看到this
is undefined
,因此您无法访问this.xScale
. 这是由于this函数内部的行为。
考虑这个例子:
class Example {
constructor(x) {
this.x = x;
}
print() {
console.log('in print method:', this);
function normal() {
console.log('in normal function:', this);
}
const arrow = () => console.log('in arrow function:', this);
normal();
arrow();
}
}
const ex = new Example(4);
ex.print();
this
在普通函数中未定义,但在箭头函数中,this
指的是对象。因此,我们可以调整线生成器以使用箭头函数:
const line = d3.line()
.x(d => this.xScale(d.date))
.y(d => this.yScale(d.key));
其次,您想为 中的每个元素绘制一条线this.keymap
,因此您需要进行数据连接:
this.plot.append('g')
.selectAll('path')
.data(this.keymap)
.join('path')
.classed('line', true)
.attr('d', function (d) { return line(d.values) })
.style('stroke', this.lineColor || 'red')
.style('fill', 'none');
这是一个完整的例子。我已经更改了数据传递到图表中的方式,以便我可以将它们全部放在一个片段中。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://d3js.org/d3.v7.js"></script>
</head>
<body>
<div id="graph"></div>
<script>
class Chart {
constructor(opts) {
this.data = opts.data;
this.element = opts.element;
}
draw() {
this.width = this.element.offsetWidth;
this.height = this.width / 2;
this.padding = 50;
this.margin = {
top: 20,
bottom: 20,
left: 30,
right: 50
};
this.element.innerHTML = '';
const svg = d3.select(this.element).append('svg');
svg.attr('width', this.width);
svg.attr('height', this.height);
this.plot = svg.append('g')
.attr('transform', `translate(${this.margin.left},${this.margin.top})`);
this.createScales();
this.addAxes();
this.addLine();
}
createScales() {
this.keynames = d3.scaleOrdinal();
this.keynames.domain(Object.keys(this.data[0]).filter(key => key !== 'date'));
this.keymap = this.keynames.domain().map(
keyname => ({
name: keyname, values: this.data.map(
d => ({ date: d.date, key: +d[keyname] })
)
})
);
const m = this.margin;
const xExtent = d3.extent(this.data, d => d.date);
const yExtent = [0, d3.max(this.keymap, d => d3.max(d.values, function (v) { return v.key }))];
this.xScale = d3.scaleTime()
.range([0, this.width - m.right])
.domain(xExtent).nice();
this.yScale = d3.scaleLinear()
.range([this.height - (m.top + m.bottom), 0])
.domain(yExtent).nice();
}
addAxes() {
const m = this.margin;
const xAxis = d3.axisBottom()
.scale(this.xScale);
const yAxis = d3.axisLeft()
.scale(this.yScale);
this.plot.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${this.height - (m.top + m.bottom)})`)
.call(xAxis.ticks(8));
this.plot.append("g")
.attr("class", "y axis")
.call(yAxis.ticks(4))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.attr("fill", "black")
.style("text-anchor", "end")
.text("$USD");
}
addLine() {
const line = d3.line()
.x(d => this.xScale(d.date))
.y(d => this.yScale(d.key));
this.plot.append('g')
.selectAll('path')
.data(this.keymap)
.join('path')
.classed('line', true)
.attr('d', function (d) { return line(d.values) })
.style('stroke', this.lineColor || 'red')
.style('fill', 'none');
}
setColor(newColor) {
this.plot.select('.line')
.style('stroke', newColor);
this.lineColor = newColor;
}
setData(data) {
this.data = data;
this.draw();
}
}
const chart = new Chart({ element: document.querySelector('#graph') });
const data = d3.csvParse(`Date,AAPL,SMA_AAPL,TSLA,SMA_TSLA
2018-12-31,38.33848571777344,,66.55999755859375,
2019-01-02,38.382225036621094,,62.02399826049805,
2019-01-03,34.55907440185547,,60.071998596191406,
2019-01-04,36.03437805175781,,63.53799819946289,
2019-01-07,35.95417022705078,,66.99199676513672,
2019-01-08,36.63956832885742,,67.06999969482422,
2019-01-09,37.26177215576172,,67.70600128173828,
2019-01-10,37.380863189697266,,68.99400329589844,
2019-01-11,37.013858795166016,,69.4520034790039,
2019-01-14,36.4572868347168,,66.87999725341797,
2019-01-15,37.20343780517578,,68.88600158691406,
2019-01-16,37.657936096191406,,69.20999908447266,
2019-01-17,37.88154602050781,,69.46199798583984,
2019-01-18,38.11487579345703,,60.45199966430664,
2019-01-22,37.259342193603516,,59.784000396728516,
2019-01-23,37.410030364990234,,57.518001556396484,
2019-01-24,37.113521575927734,,58.301998138427734,
2019-01-25,38.34333801269531,,59.40800094604492,
2019-01-28,37.988487243652344,,59.2760009765625,
2019-01-29,37.59474182128906,,59.492000579833984,
2019-01-30,40.16377258300781,,61.75400161743164,
2019-01-31,40.453006744384766,,61.40399932861328,
2019-02-01,40.472450256347656,,62.44200134277344,
2019-02-04,41.622066497802734,,62.577999114990234,
2019-02-05,42.33420181274414,,64.2699966430664,
2019-02-06,42.34878158569336,,63.444000244140625,
2019-02-07,41.546722412109375,,61.50199890136719,
2019-02-08,41.59553909301758,,61.15999984741211,
2019-02-11,41.35633087158203,,62.56800079345703,
2019-02-12,41.71269989013672,38.606483713785806,62.36199951171875,63.48539975484212
2019-02-13,41.539398193359375,38.71318079630534,61.63399887084961,63.32119979858398
2019-02-14,41.69073486328125,38.823464457194014,60.75400161743164,63.278866577148435
2019-02-15,41.59797286987305,39.05809440612793,61.57600021362305,63.32899996439616
2019-02-19,41.72246551513672,39.247697321573895,61.12799835205078,63.24866663614909
2019-02-20,41.990962982177734,39.44892374674479,60.512001037597656,63.032666778564455
2019-02-21,41.75419616699219,39.619411341349284,58.24599838256836,62.738533401489256
2019-02-22,42.22041702270508,39.78469950358073,58.94200134277344,62.44640007019043
2019-02-25,42.5279655456543,39.95626958211263,59.75400161743164,62.13840001424153`, function (d) {
function removeNaN(e, c) {
if (e > 0) { return e; } else { return c; }
}
return {
date: d3.timeParse("%Y-%m-%d")(d.Date),
aapl: +d.AAPL,
tsla: +d.TSLA,
aapl_sma: removeNaN(+d.SMA_AAPL, +d.AAPL),
tsla_sma: removeNaN(+d.SMA_TSLA, +d.TSLA)
};
});
chart.setData(data);
</script>
</body>
</html>
推荐阅读
- python - 由于 numpy,Python 脚本未与 Windows 任务调度程序一起运行
- javascript - 如何在两条多段三次贝塞尔样条线之间绘制等距的法线?
- java - Spring Boot 和 Keycloak - 401 状态码,即使资源不存在
- python - Pytest - 从多个返回值中获取一项
- kotlin - Kotlin 错误:我是 Kotlin 编程的初学者并提出了这个错误
- html - 如何以适当的间距从网站中提取描述部分?
- php - 如何使用 PDO 准备语句/位置占位符来运行此 PHP 注册脚本?
- java - 无法将 CSS 类添加到我的 JavaFX 标签
- spring - 在我的 spring mvc 项目中添加 requestmapping 注释
- python - Altair - 在 x = y 的绘图中画一条线