英文:
d3.js Project_Problem with Rendering
问题
以下是您要翻译的内容:
我有一个建立在 d3 库 v.7.6.1 上的项目。
它托管在 GitHub 页面上,在我的笔记本电脑上完全正常:
[![enter image description here][2]][2]
但在大多数其他设备上呈现不正确:
[![enter image description here][3]][3]
控制台上出现了`y`和`cy`属性的错误:
[![enter image description here][4]][4]
----------
我在这里复制了代码。它可能有什么问题?
```javascript
async function drawLineChart() {
const pathToCsv = 'https://raw.githubusercontent.com/dsibi/portfolio/main/projects/line-graph-2/data/time_entries.csv';
let rawDataset = await d3.dsv(";", pathToCsv);
const record = {
date: '',
duration: ''
};
let dataset = [];
for (let i = 0; i < rawDataset.length; i++) {
let currRecord = Object.create(record);
const [day, month, year] = rawDataset[i]['Start date'].split('.');
currRecord.date = new Date(+year, +month - 1, +day);
const [hours, minutes, seconds] = rawDataset[i]['Duration'].split(':');
currRecord.duration = new Date(+year, +month - 1, +day, +hours, +minutes, +seconds);
dataset.push(currRecord);
}
dataset.forEach(function(element) {
let timeString = element.duration.toLocaleTimeString();
let timeEl = timeString.split(':');
element.durationSeconds = (+timeEl[0]) * 60 * 60 + (+timeEl[1]) * 60 + (+timeEl[2]);
});
var groupedDataset = [];
dataset.reduce(function(res, value) {
if (!res[value.date]) {
res[value.date] = {
date: value.date,
totalDurationSeconds: 0
};
groupedDataset.push(res[value.date])
}
res[value.date].totalDurationSeconds += value.durationSeconds;
return res;
}, {});
const xAccessor = d => d.date;
const formatHours = d3.format(".2f");
const yAccessor = d => +formatHours(d['totalDurationSeconds'] / 3600);
const yAccessorLine = d => d['meanDurationHours'];
let datasetWeeks = downsampleData(groupedDataset, xAccessor, yAccessor);
const vacation = [{
name: 'vacation',
start: new Date('2022-06-16'),
end: new Date('2022-06-26'),
}, ];
let dimensions = {
width: window.innerWidth * 0.8,
height: 400,
margin: {
top: 15,
right: 40,
bottom: 40,
left: 40,
},
}
dimensions.boundedWidth = dimensions.width - dimensions.margin.left - dimensions.margin.right
dimensions.boundedHeight = dimensions.height - dimensions.margin.top - dimensions.margin.bottom
// 3. 画布
const wrapper = d3.select("#wrapper")
.append("svg")
.attr("width", dimensions.width)
.attr("height", dimensions.height);
const bounds = wrapper.append("g")
.style("transform", `translate(${dimensions.margin.left}px, ${dimensions.margin.top}px)`);
// 4. 比例尺
const xScale = d3.scaleTime()
.domain(d3.extent(groupedDataset, xAccessor))
.range([0, dimensions.boundedWidth]);
const yScale = d3.scaleLinear()
.domain(d3.extent(groupedDataset, yAccessor))
.range([dimensions.boundedHeight, 0])
.nice();
const meanHours = d3.mean(groupedDataset, yAccessor);
bounds.append('line').attr('class', 'mean');
const meanLine = bounds.select('.mean')
.attr('x1', 0)
.attr('x2', dimensions.boundedWidth)
.attr('y1', yScale(meanHours))
.attr('y2', yScale(meanHours));
const xAxisGenerator = d3.axisBottom()
.scale(xScale);
const xAxis = bounds.append("g")
.attr("class", "x-axis")
.style("transform", `translateY(${dimensions.boundedHeight}px)`)
.call(xAxisGenerator);
// 5. 画数据
// 点
const dots = bounds.selectAll(".dot")
.data(groupedDataset)
.enter()
.append("circle")
.attr("cx", d => xScale(xAccessor(d)))
.attr("cy", d => yScale(yAccessor(d)))
.attr("r", 2)
.attr("class", "dot");
// 线
const lineGenerator = d3.line()
.x(function(d) {
return xScale(xAccessor(d))
})
.y(d => yScale(yAccessorLine(d)))
.curve(d3.curveCatmullRom.alpha(.5));
const line = bounds.append("path")
.attr("class", "line")
.attr("d", lineGenerator(datasetWeeks))
// 6. 画周边
const yAxisGenerator = d3.axisLeft()
.scale(yScale)
.ticks(7);
const yAxis = bounds.append("g")
.attr("class", "y-axis")
.call(yAxisGenerator);
};
drawLineChart();
function downsampleData(data, xAccessor, yAccessor) {
const weeks = d3.timeWeeks(xAccessor(data[0]), xAccessor(data[data.length - 1]))
return weeks.map((week, index) => {
const weekEnd = weeks[index + 1] || new Date()
const days = data.filter(d => xAccessor(d) > week && xAccessor(d) <= weekEnd)
const meanTotalDurationHours = d3.mean(days, yAccessor)
const meanDurationHours = meanTotalDurationHours === undefined ? 0 : d3.mean(days, yAccessor)
return {
date: week,
meanDurationHours: meanDurationHours
}
})
};
.line {
fill: none;
stroke: #eb4511;
stroke-width: 3;
}
.mean {
stroke: #7d82b8;
stroke-dasharray: 2px 4px;
}
.y-axis-label {
fill: black;
font-size: 1.4em;
text-anchor: middle;
}
.x-axis-label {
fill: black;
font-size: 1.4em;
text-anchor: middle;
}
.dot {
fill: #9c9b98;
}
.mean_text,
.vacation_text {
fill: #7d82b8;
font-size: .8em;
font-weight: 800;
text-anchor: start;
}
.x-axis line,
.y-axis
<details>
<summary>英文:</summary>
I have a [project][1] built on [tag:d3] lib `v.7.6.1`.
It's hosted on GitHub Pages and works totally Ok on my laptop:
[![enter image description here][2]][2]
But renders incorrectly on most of the other devices:
[![enter image description here][3]][3]
With error in console for `y` and `cy` attributes:
[![enter image description here][4]][4]
----------
I've reproduced code here. What can be wrong with it?
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
async function drawLineChart() {
const pathToCsv = 'https://raw.githubusercontent.com/dsibi/portfolio/main/projects/line-graph-2/data/time_entries.csv';
let rawDataset = await d3.dsv(";", pathToCsv);
const record = {
date: '',
duration: ''
};
let dataset = [];
for (let i = 0; i < rawDataset.length; i++) {
let currRecord = Object.create(record);
const [day, month, year] = rawDataset[i]['Start date'].split('.');
currRecord.date = new Date(+year, +month - 1, +day);
const [hours, minutes, seconds] = rawDataset[i]['Duration'].split(':');
currRecord.duration = new Date(+year, +month - 1, +day, +hours, +minutes, +seconds);
dataset.push(currRecord);
}
dataset.forEach(function(element) {
let timeString = element.duration.toLocaleTimeString();
let timeEl = timeString.split(':');
element.durationSeconds = (+timeEl[0]) * 60 * 60 + (+timeEl[1]) * 60 + (+timeEl[2]);
});
var groupedDataset = [];
dataset.reduce(function(res, value) {
if (!res[value.date]) {
res[value.date] = {
date: value.date,
totalDurationSeconds: 0
};
groupedDataset.push(res[value.date])
}
res[value.date].totalDurationSeconds += value.durationSeconds;
return res;
}, {});
const xAccessor = d => d.date;
const formatHours = d3.format(".2f");
const yAccessor = d => +formatHours(d['totalDurationSeconds'] / 3600);
const yAccessorLine = d => d['meanDurationHours'];
let datasetWeeks = downsampleData(groupedDataset, xAccessor, yAccessor);
const vacation = [{
name: 'vacation',
start: new Date('2022-06-16'),
end: new Date('2022-06-26'),
}, ];
let dimensions = {
width: window.innerWidth * 0.8,
height: 400,
margin: {
top: 15,
right: 40,
bottom: 40,
left: 40,
},
}
dimensions.boundedWidth = dimensions.width - dimensions.margin.left - dimensions.margin.right
dimensions.boundedHeight = dimensions.height - dimensions.margin.top - dimensions.margin.bottom
// 3. Draw Canvas
const wrapper = d3.select("#wrapper")
.append("svg")
.attr("width", dimensions.width)
.attr("height", dimensions.height);
const bounds = wrapper.append("g")
.style("transform", `translate(${dimensions.margin.left}px, ${dimensions.margin.top}px)`);
// 4. Scales
const xScale = d3.scaleTime()
.domain(d3.extent(groupedDataset, xAccessor))
.range([0, dimensions.boundedWidth]);
const yScale = d3.scaleLinear()
.domain(d3.extent(groupedDataset, yAccessor))
.range([dimensions.boundedHeight, 0])
.nice();
const meanHours = d3.mean(groupedDataset, yAccessor);
bounds.append('line').attr('class', 'mean');
const meanLine = bounds.select('.mean')
.attr('x1', 0)
.attr('x2', dimensions.boundedWidth)
.attr('y1', yScale(meanHours))
.attr('y2', yScale(meanHours));
const xAxisGenerator = d3.axisBottom()
.scale(xScale);
const xAxis = bounds.append("g")
.attr("class", "x-axis")
.style("transform", `translateY(${dimensions.boundedHeight}px)`)
.call(xAxisGenerator);
// 5. Draw Data
//dots
const dots = bounds.selectAll(".dot")
.data(groupedDataset)
.enter()
.append("circle")
.attr("cx", d => xScale(xAccessor(d)))
.attr("cy", d => yScale(yAccessor(d)))
.attr("r", 2)
.attr("class", "dot");
//line
const lineGenerator = d3.line()
.x(function(d) {
// console.log(xScale(xAccessor(d)))
return xScale(xAccessor(d))
})
.y(d => yScale(yAccessorLine(d)))
.curve(d3.curveCatmullRom.alpha(.5));
// .curve(d3.curveMonotoneX);
const line = bounds.append("path")
.attr("class", "line")
.attr("d", lineGenerator(datasetWeeks))
// 6. Draw Peripherals
const yAxisGenerator = d3.axisLeft()
.scale(yScale)
.ticks(7);
const yAxis = bounds.append("g")
.attr("class", "y-axis")
.call(yAxisGenerator);
};
drawLineChart();
function downsampleData(data, xAccessor, yAccessor) {
const weeks = d3.timeWeeks(xAccessor(data[0]), xAccessor(data[data.length - 1]))
return weeks.map((week, index) => {
const weekEnd = weeks[index + 1] || new Date()
const days = data.filter(d => xAccessor(d) > week && xAccessor(d) <= weekEnd)
const meanTotalDurationHours = d3.mean(days, yAccessor)
const meanDurationHours = meanTotalDurationHours === undefined ? 0 : d3.mean(days, yAccessor)
return {
date: week,
meanDurationHours: meanDurationHours
}
})
};
<!-- language: lang-css -->
.line {
fill: none;
stroke: #eb4511;
stroke-width: 3;
}
.mean {
stroke: #7d82b8;
stroke-dasharray: 2px 4px;
}
.y-axis-label {
fill: black;
font-size: 1.4em;
text-anchor: middle;
}
.x-axis-label {
fill: black;
font-size: 1.4em;
text-anchor: middle;
}
.dot {
fill: #9c9b98;
}
.mean_text,
.vacation_text {
fill: #7d82b8;
font-size: .8em;
font-weight: 800;
text-anchor: start;
}
.x-axis line,
.y-axis line,
.domain {
stroke: gray;
}
.tick text,
.y-axis-label {
fill: gray
}
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js"></script>
<html lang="en">
<body>
<div id="wrapper">
</div>
</body>
</html>
<!-- end snippet -->
[1]: https://dsibi.github.io/portfolio/projects/line-graph-2/line-graph-2.html
[2]: https://i.stack.imgur.com/uMYq3.png
[3]: https://i.stack.imgur.com/kwSAC.png
[4]: https://i.stack.imgur.com/hHGgw.png
</details>
# 答案1
**得分**: 2
以下是代码部分的中文翻译:
你的问题是由 `toLocaleTimeString()` 引起的。
在以下部分添加 `console.log()` 后:
```javascript
dataset.forEach(function(element) {
let timeString = element.duration.toLocaleTimeString();
let timeEl = timeString.split(':');
console.log(timeString, timeEl)
element.durationSeconds = (+timeEl[0]) * 60 * 60 + (+timeEl[1]) * 60 + (+timeEl[2]);
});
桌面显示为:
00:19:34 (3) ['00', '19', '34']
但我的移动设备(使用 Chrome 远程检查器)显示为:
12:06:53 AM (3) ['12', '06', '53 AM']
所以当你执行 * 60 + (+timeEl[2])
时,因为 53 AM
的原因,你会得到 NaN
。
你应该将时间逻辑更改为能够解析 24 小时和 12 小时制时间,例如使用:
以下是尝试修复此问题的部分翻译:
我使用了 new Date()
将其转换为日期对象。然后使用 valueOf()
获取Unix时间戳。现在我们只需将它们相减以获得 durationSeconds
。
dataset.forEach(function(element) {
let secondsDiff = new Date(element.duration).valueOf() - new Date(element.date).valueOf();
element.durationSeconds = secondsDiff;
});
这在我的笔记本电脑和移动设备上都有效。
更新后的代码片段如下:
// ...(代码截取)
希望这些翻译对你有帮助。如果有其他问题,请随时提出。
英文:
Your issue is caused by toLocaleTimeString()
.
When adding a console.log()
to the following part:
dataset.forEach(function(element) {
let timeString = element.duration.toLocaleTimeString();
let timeEl = timeString.split(':');
console.log(timeString, timeEl)
element.durationSeconds = (+timeEl[0]) * 60 * 60 + (+timeEl[1]) * 60 + (+timeEl[2]);
});
Dekstop shows:
00:19:34 (3) ['00', '19', '34']
But my mobile (using chrome remote inspector) shows:
12:06:53 AM (3) ['12', '06', '53 AM']
So when you're doing * 60 + (+timeEl[2])
you're getting NaN
due the 53 AM
You should change the time-logic to both be able to parse 24 and 12h format time, eg, using:
- <https://stackoverflow.com/questions/55478610/how-to-use-tolocaletimestring-12-hours-time-without-am-pm-abbreviations>
Here's my attempt to fix this.
I've used new Date()
to convert it to a date object. Then using valueOf()
to get the unix timestamp. Now we just need to substract those from each-other to get the durationSeconds
dataset.forEach(function(element) {
let secondsDiff = new Date(element.duration).valueOf() - new Date(element.date).valueOf();
element.durationSeconds = secondsDiff;
});
This works both on my laptop as mobile:
Updated snippet:
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
async function drawLineChart() {
const pathToCsv = 'https://raw.githubusercontent.com/dsibi/portfolio/main/projects/line-graph-2/data/time_entries.csv';
let rawDataset = await d3.dsv(";", pathToCsv);
const record = {
date: '',
duration: ''
};
let dataset = [];
for (let i = 0; i < rawDataset.length; i++) {
let currRecord = Object.create(record);
const [day, month, year] = rawDataset[i]['Start date'].split('.');
currRecord.date = new Date(+year, +month - 1, +day);
const [hours, minutes, seconds] = rawDataset[i]['Duration'].split(':');
currRecord.duration = new Date(+year, +month - 1, +day, +hours, +minutes, +seconds);
dataset.push(currRecord);
}
dataset.forEach(function(element) {
let secondsDiff = new Date(element.duration).valueOf() - new Date(element.date).valueOf();
element.durationSeconds = secondsDiff;
});
var groupedDataset = [];
dataset.reduce(function(res, value) {
if (!res[value.date]) {
res[value.date] = {
date: value.date,
totalDurationSeconds: 0
};
groupedDataset.push(res[value.date])
}
res[value.date].totalDurationSeconds += value.durationSeconds;
return res;
}, {});
const xAccessor = d => d.date;
const formatHours = d3.format(".2f");
const yAccessor = d => +formatHours(d['totalDurationSeconds'] / 3600);
const yAccessorLine = d => d['meanDurationHours'];
let datasetWeeks = downsampleData(groupedDataset, xAccessor, yAccessor);
const vacation = [{
name: 'vacation',
start: new Date('2022-06-16'),
end: new Date('2022-06-26'),
}, ];
let dimensions = {
width: window.innerWidth * 0.8,
height: 400,
margin: {
top: 15,
right: 40,
bottom: 40,
left: 40,
},
}
dimensions.boundedWidth = dimensions.width - dimensions.margin.left - dimensions.margin.right
dimensions.boundedHeight = dimensions.height - dimensions.margin.top - dimensions.margin.bottom
// 3. Draw Canvas
const wrapper = d3.select("#wrapper")
.append("svg")
.attr("width", dimensions.width)
.attr("height", dimensions.height);
const bounds = wrapper.append("g")
.style("transform", `translate(${dimensions.margin.left}px, ${dimensions.margin.top}px)`);
// 4. Scales
const xScale = d3.scaleTime()
.domain(d3.extent(groupedDataset, xAccessor))
.range([0, dimensions.boundedWidth]);
const yScale = d3.scaleLinear()
.domain(d3.extent(groupedDataset, yAccessor))
.range([dimensions.boundedHeight, 0])
.nice();
const meanHours = d3.mean(groupedDataset, yAccessor);
bounds.append('line').attr('class', 'mean');
const meanLine = bounds.select('.mean')
.attr('x1', 0)
.attr('x2', dimensions.boundedWidth)
.attr('y1', yScale(meanHours))
.attr('y2', yScale(meanHours));
const xAxisGenerator = d3.axisBottom()
.scale(xScale);
const xAxis = bounds.append("g")
.attr("class", "x-axis")
.style("transform", `translateY(${dimensions.boundedHeight}px)`)
.call(xAxisGenerator);
// 5. Draw Data
//dots
const dots = bounds.selectAll(".dot")
.data(groupedDataset)
.enter()
.append("circle")
.attr("cx", d => xScale(xAccessor(d)))
.attr("cy", d => yScale(yAccessor(d)))
.attr("r", 2)
.attr("class", "dot");
//line
const lineGenerator = d3.line()
.x(function(d) {
// console.log(xScale(xAccessor(d)))
return xScale(xAccessor(d))
})
.y(d => yScale(yAccessorLine(d)))
.curve(d3.curveCatmullRom.alpha(.5));
// .curve(d3.curveMonotoneX);
const line = bounds.append("path")
.attr("class", "line")
.attr("d", lineGenerator(datasetWeeks))
// 6. Draw Peripherals
const yAxisGenerator = d3.axisLeft()
.scale(yScale)
.ticks(7);
const yAxis = bounds.append("g")
.attr("class", "y-axis")
.call(yAxisGenerator);
};
drawLineChart();
function downsampleData(data, xAccessor, yAccessor) {
const weeks = d3.timeWeeks(xAccessor(data[0]), xAccessor(data[data.length - 1]))
return weeks.map((week, index) => {
const weekEnd = weeks[index + 1] || new Date()
const days = data.filter(d => xAccessor(d) > week && xAccessor(d) <= weekEnd)
const meanTotalDurationHours = d3.mean(days, yAccessor)
const meanDurationHours = meanTotalDurationHours === undefined ? 0 : d3.mean(days, yAccessor)
return {
date: week,
meanDurationHours: meanDurationHours
}
})
};
<!-- language: lang-css -->
.line {
fill: none;
stroke: #eb4511;
stroke-width: 3;
}
.mean {
stroke: #7d82b8;
stroke-dasharray: 2px 4px;
}
.y-axis-label {
fill: black;
font-size: 1.4em;
text-anchor: middle;
}
.x-axis-label {
fill: black;
font-size: 1.4em;
text-anchor: middle;
}
.dot {
fill: #9c9b98;
}
.mean_text,
.vacation_text {
fill: #7d82b8;
font-size: .8em;
font-weight: 800;
text-anchor: start;
}
.x-axis line,
.y-axis line,
.domain {
stroke: gray;
}
.tick text,
.y-axis-label {
fill: gray
}
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js"></script>
<html lang="en">
<body>
<div id="wrapper">
</div>
</body>
</html>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论