d3.js 项目_渲染问题

问题描述 投票:0回答:1

我有一个 project 建立在 lib

v.7.6.1
.

它托管在 GitHub Pages 上,在我的笔记本电脑上运行完全正常:

但在大多数其他设备上呈现不正确:

y
cy
属性的控制台错误:


我在这里复制了代码。它有什么问题?

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

javascript d3.js visualization github-pages
1个回答
1
投票

您的问题是由

toLocaleTimeString()
引起的。


在以下部分添加

console.log()
时:

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])
你得到
NaN
由于
53 AM


您应该将时间逻辑更改为能够解析 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;
});

这既适用于我的笔记本电脑,也适用于移动设备:

更新的片段:

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

© www.soinside.com 2019 - 2024. All rights reserved.