Chart.js(chartjs-node-canvas)创建基于日期的浮动条形图

h9a6wy2h  于 2022-11-07  发布在  Chart.js
关注(0)|答案(2)|浏览(166)

我将尽可能清楚地解释我的问题,同时也避免使这个主题太长。我最近发现了Chart.js库,它非常适合我的需要。现在,由于我正在使用Node.js,并且需要一个图形的png,我将使用chartjs-node-canvas库。记住这些信息,我将尝试将我的主题分成多个部分,以便更清楚地理解。

最终目标

在进入问题本身之前,我想先讨论一下我的最终目标。这是对我要做的事情给予一个大致的概念,以便相应地拟合响应。为了简短起见,我有{awardedDate: "2022-06-22T12:21:17.22Z", badgeId: 1234567}形式的数据,其中awardedDate是徽章授予的时间戳。而badgeId是所授予徽章的ID(这与图表无关,但它存在是因为它是数据的一部分)。现在,我有一个包含大约2,787个对象的样本,它们都具有不同的奖励日期日期范围从2016年到2022年。我的目标是将这些徽章按月-年分组,该月-年将显示该年该月获得的徽章数量。然后,我想根据该年该月获得的徽章数量绘制一个瀑布图。目前,没有一个具体的结构来描述它的外观,但是它可以是{"02-2022": 10, "03-2022": 5}这样的对象,也可以是其他任何对象。

实际问题

现在你大概知道我的最终目标是什么了,我真正的问题是,我如何才能使一个浮动的(我们可以将瀑布结构的内容留给另一个主题)条形图。由于数据可以有空白周期(数据集可能会有长达数月的间隔),我无法真正利用标签(除非我说错了什么),所以x-y关系是最好的。我试着使用{x: "2022-06-22T12:21:17.226Z", y: [10, 15]}结构,但没有真正产生任何结果。现在,我正在使用一个示例代码来测试图形如何与数据React。当然,一旦我有了一个成品,我会用实际值替换测试值。

  1. const config = {
  2. type: "bar",
  3. data: {
  4. datasets: [{
  5. label: "Badges",
  6. data: [
  7. {
  8. x: "2022-06-22T12:41:17.226Z",
  9. y: [10, 15]
  10. }
  11. ],
  12. borderColor: "rgb(75, 192, 192)",
  13. borderSkipped: false
  14. }]
  15. },
  16. options: {
  17. plugins: {
  18. legend: {
  19. display: false
  20. },
  21. title: {
  22. display: true,
  23. text: "Test",
  24. color: "#FFFFFF"
  25. }
  26. },
  27. scales: {
  28. x: {
  29. type: 'time',
  30. title: {
  31. display: true,
  32. text: 'Time',
  33. color: "#FFFFFF"
  34. },
  35. min: "2022-06-22T12:21:17.226Z",
  36. max: "2022-06-22T14:21:17.226Z",
  37. grid: {
  38. borderColor: "#FFFFFF",
  39. color: "#FFFFFF"
  40. },
  41. ticks: {
  42. color: "#FFFFFF"
  43. }
  44. },
  45. y: {
  46. title: {
  47. display: true,
  48. text: 'Number of Badges',
  49. borderColor: "#FFFFFF",
  50. color: "#FFFFFF"
  51. },
  52. min: 0,
  53. max: 50,
  54. grid: {
  55. borderColor: "#FFFFFF",
  56. color: "#FFFFFF"
  57. },
  58. ticks: {
  59. color: "#FFFFFF"
  60. }
  61. }
  62. }
  63. },
  64. plugins: [
  65. {
  66. id: 'custom_canvas_background_color',
  67. beforeDraw: (chart) => {
  68. const ctx = chart.ctx;
  69. ctx.save();
  70. ctx.fillStyle = '#303030';
  71. ctx.fillRect(0, 0, chart.width, chart.height);
  72. ctx.restore();
  73. }
  74. }
  75. ]
  76. };
  77. const imageBuffer = await canvasRenderService.renderToBuffer(config)
  78. fs.writeFileSync("./chart2.png", imageBuffer)

这是代码生成的图形:

当然,应该发生的事情是,在开始处会生成一个浮动条,范围从5到10,但是如上所示,什么都没有发生。如果有人能帮助我解决这个问题,那将是非常令人惊讶的。非常感谢你的时间和帮助,我非常感激。

n6lpvg4x

n6lpvg4x1#

受此answer的启发,我提出了以下解决方案。
第一个
如果你也想看到差距,你首先必须用最早和最晚日期之间的后续月份初始化badgesPerMonth,每个月份的值都为零。请看一下这个answer,了解如何完成这个操作。

jfewjypa

jfewjypa2#

在阅读@uminder的回复后,我创建了下面的代码,它解决了我的问题:

  1. dateGroups = Object.fromEntries(
  2. Object.entries(dateGroups).sort(([d1,],[d2,]) => {return (d1 < d2) ? -1 : ((d1 > d2) ? 1 : 0)})
  3. )
  4. const dateTimesConst = Object.keys(dateGroups)
  5. const dateValuesConst = Object.values(dateGroups)
  6. let dateTimes = []
  7. let dateValues = []
  8. let prevLength = 0
  9. let mostBadgesPerMonth = 0
  10. for (let i = 0; i < dateValuesConst.length; i++) {
  11. const currentMonth = new Date(Date.parse(dateTimesConst[i]))
  12. const previousMonth = new Date(Date.UTC(currentMonth.getUTCFullYear(), currentMonth.getUTCMonth() - 1, 1, 0, 0, 0, 0)).toISOString()
  13. const nextMonth = new Date(Date.UTC(currentMonth.getUTCFullYear(), currentMonth.getUTCMonth() + 1, 1, 0, 0, 0, 0)).toISOString()
  14. // if (!dateTimesConst.includes(previousMonth)) prevLength = 0
  15. const length = dateValuesConst[i].length
  16. dateValues.push([prevLength, length])
  17. dateTimes.push(dateTimesConst[i])
  18. prevLength = length
  19. if (length > mostBadgesPerMonth) mostBadgesPerMonth = length
  20. // if (!dateTimesConst.includes(nextMonth) && i !== dateValuesConst.length - 1) {
  21. // dateTimes.push(nextMonth)
  22. // dateValues.push([length, 0])
  23. // prevLength = 0
  24. // }
  25. }
  26. function barColorCode() {
  27. return (ctx) => {
  28. const start = ctx.parsed._custom.start
  29. const end = ctx.parsed._custom.end
  30. return start <= end ? "rgba(50, 168, 82, 1)" : (start > end) ? "rgba(191, 27, 27, 1)" : "black"
  31. }
  32. }
  33. const config = {
  34. type: "bar",
  35. data: {
  36. labels: dateTimes,
  37. datasets: [{
  38. label: "Badges",
  39. data: dateValues,
  40. elements: {
  41. bar: {
  42. backgroundColor: barColorCode()
  43. }
  44. },
  45. barPercentage: 1,
  46. categoryPercentage: 0.95,
  47. borderSkipped: false
  48. }]
  49. },
  50. options: {
  51. plugins: {
  52. legend: {
  53. display: false
  54. },
  55. title: {
  56. display: true,
  57. text: "Test",
  58. color: "#FFFFFF"
  59. }
  60. },
  61. scales: {
  62. x: {
  63. type: 'time',
  64. title: {
  65. display: true,
  66. text: 'Date',
  67. color: "#FFFFFF"
  68. },
  69. time: {
  70. unit: "month",
  71. round: "month"
  72. },
  73. min: dateTimesConst[0],
  74. max: dateTimesConst[dateTimesConst.length - 1],
  75. grid: {
  76. borderColor: "#FFFFFF",
  77. color: "#FFFFFF"
  78. },
  79. ticks: {
  80. color: "#FFFFFF"
  81. }
  82. },
  83. y: {
  84. title: {
  85. display: true,
  86. text: 'Number of Badges',
  87. borderColor: "#FFFFFF",
  88. color: "#FFFFFF"
  89. },
  90. min: 0,
  91. max: mostBadgesPerMonth + 1,
  92. grid: {
  93. borderColor: "#FFFFFF",
  94. color: "#FFFFFF"
  95. },
  96. ticks: {
  97. color: "#FFFFFF"
  98. }
  99. }
  100. }
  101. },
  102. plugins: [
  103. {
  104. id: 'custom_canvas_background_color',
  105. beforeDraw: (chart) => {
  106. const ctx = chart.ctx;
  107. ctx.save();
  108. ctx.fillStyle = '#303030';
  109. ctx.fillRect(0, 0, chart.width, chart.height);
  110. ctx.restore();
  111. }
  112. }
  113. ]
  114. };
  115. const imageBuffer = await canvasRenderService.renderToBuffer(config)
  116. fs.writeFileSync("./chart2.png", imageBuffer)

再次感谢@uminder给我的灵感。

展开查看全部

相关问题