You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
106 lines
4.1 KiB
106 lines
4.1 KiB
/**
|
|
* Stacked Chart
|
|
*/
|
|
class StackedChart extends BarChart {
|
|
/**
|
|
* Creates a new instance of the StackedChart class
|
|
*
|
|
* @param {HTMLCanvasElement} canvas - The canvas element to draw the chart on
|
|
* @param {Array<Object>} data - The data to visualize on the chart
|
|
* @param {Object} settings - The settings for the chart
|
|
* @param {ZoomManager} zoom - The zoom manager for the chart.
|
|
*/
|
|
constructor(canvas, data, settings, zoom) {
|
|
// Call the constructor of the parent class (Chart)
|
|
super(canvas, data, settings, zoom)
|
|
}
|
|
|
|
/**
|
|
* Draws the stacked bar chart on the canvas.
|
|
*/
|
|
draw() {
|
|
// Calculate the largest total value across all categories for normalization
|
|
let largest = 0
|
|
for (let i = 0; i < this.dataLen; i++) {
|
|
let sum = 0
|
|
this.data.forEach(categ => {
|
|
categ.values[i] = Math.abs(categ.values[i]) || 0
|
|
sum += categ.values[i]
|
|
})
|
|
largest = sum > largest ? sum : largest
|
|
}
|
|
|
|
// Clear the canvas
|
|
this.clear()
|
|
// Draw the axis without displaying axis values
|
|
this.drawAxis(false)
|
|
|
|
// Calculate the size of each bar segment based on the canvas width and number of data points
|
|
let size = this.zoomBounds.width / this.dataLen
|
|
// Calculate the actual width of each bar, making it 70% of the calculated size
|
|
let bar_width = size * 0.7
|
|
|
|
// Iterate over each data point to draw the stacked bars
|
|
for (let i = 0; i < this.dataLen; i++) {
|
|
// The top position of the last stacked bar segment
|
|
let last_top = this.zoomBounds.xAxis
|
|
|
|
this.data.forEach((categ, colId) => {
|
|
// Value of the bar segment
|
|
let value = categ.values[i] || 0
|
|
// The height of the bar segment relative to the largest total value
|
|
let bar_height = value / largest * this.zoomBounds.height
|
|
// The left position of the bar segment
|
|
let left = this.zoomBounds.left + size * (i + 0.15)
|
|
// The top position of the bar segment
|
|
let top = last_top - bar_height
|
|
|
|
// Update the last top position for the next iteration
|
|
last_top = top
|
|
|
|
// Create a new Rectangle object representing the current bar
|
|
let newObject = new Rectangle(this.ctx, value, colId, left, top, bar_width, bar_height)
|
|
// Add the new Rectangle object to the list of objects
|
|
this.objects.push(newObject)
|
|
|
|
// Check if the center of the new bar is within the bounds
|
|
if (this.isInBounds(newObject.getCenter())) {
|
|
// Set the fill color to the category color
|
|
this.ctx.fillStyle = categ.color
|
|
// Set the line width to 0 to prevent drawing the border
|
|
this.ctx.lineWidth = 0
|
|
// Draw the filled rectangle representing the current bar
|
|
newObject.draw()
|
|
}
|
|
})
|
|
}
|
|
|
|
// Draw x-axis labels if enabled
|
|
if (this.settings.displayAxisValues) {
|
|
// Restore canvas state to undo region clipping
|
|
this.ctx.restore()
|
|
// Loop through each data point to draw the labels
|
|
for (let i = 0; i < this.dataLen; i++) {
|
|
let text = (i + 1).toString()
|
|
|
|
// Begin drawing the text
|
|
this.ctx.beginPath()
|
|
this.ctx.font = "16px Arial"
|
|
this.ctx.fillStyle = "black"
|
|
this.ctx.textAlign = "center"
|
|
|
|
// Calculate the position of the label
|
|
let x = this.zoomBounds.left + this.zoomBounds.width / this.dataLen * i + size / 2
|
|
let y = this.bounds.bottom + 15
|
|
|
|
// Draw the label text
|
|
this.ctx.fillText(text, x, y)
|
|
// Stroke the text
|
|
this.ctx.stroke()
|
|
// Close the path
|
|
this.ctx.closePath()
|
|
}
|
|
}
|
|
}
|
|
}
|