I am in the process of writing a resize function for my chart. So far everything works quite well.
However, I noticed the following bug when using the resize function.
After I have rendered the chart and then resize the window, my heatmap chart extends some "px" to the right. This causes the chart to be cut off on the right side.
Can someone please explain why this bug occurs and how I can fix it?
I would be grateful for any advice.
//Datasource
const data1= [
{id: 1,group:"A",variable:"v1",value: 98},{id: 2,group:"A",variable:"v2",value:95},
{id: 3,group:"A",variable:"v3",value: 22},{id: 4,group:"A",variable:"v4",value:14},
{id: 11,group:"B",variable:"v1",value: 37},{id: 12,group:"B",variable:"v2",value:50},
{id: 13,group:"B",variable:"v3",value: 81},{id: 14,group:"B",variable:"v4",value:79},
{id: 21,group:"C",variable:"v1",value: 96},{id: 22,group:"C",variable:"v2",value:13},
{id: 23,group:"C",variable:"v3",value: 98},{id: 24,group:"C",variable:"v4",value:10},
{id: 31,group:"D",variable:"v1",value: 75},{id: 32,group:"D",variable:"v2",value:18},
{id: 33,group:"D",variable:"v3",value: 92},{id: 34,group:"D",variable:"v4",value:43},
{id: 41,group:"E",variable:"v1",value: 44},{id: 42,group:"E",variable:"v2",value:29},
{id: 43,group:"E",variable:"v3",value: 58},{id: 44,group:"E",variable:"v4",value:55},
{id: 51,group:"F",variable:"v1",value: 35},{id: 52,group:"F",variable:"v2",value:80},
{id: 53,group:"F",variable:"v3",value: 8},{id: 54,group:"F",variable:"v4",value:46}
];
var i_region_static_id = "heatmap"
var i_height = 450;
var margin = {top: 0, right: 50, bottom: 25, left: 70};
var width_client = document.body.clientWidth;
var height_client = document.body.clientHeight;
const innerWidth = width_client - margin.left - margin.right;
const innerHeight = i_height - margin.top - margin.bottom;
var colorMidValue = 20;
const labelGroup = "X-Label"
const labelVars = "Y-Label"
const color_start = "#f9f9f9"
const colorEnd = "#6e69b3"
const colorMid = "#bd326c"
// Funktion zum Aufrunden der Y-Axis auf 5
function updateColorAxis(a,b){
if(b%5 !== 0){
b = b+(5-(b%5));
}
return [a,b];
}
// Funktion zum Berechnen der Anzahl der Ticks
function setColorAxisTicks(a,b){
if(b%5 === 0){
return b/5;
}
return b/10;
}
// Returns Black/White depending on node color
function calculateContrast(values){
var rgb = values.match(/\d+/g);
// Wenn rgb leer, dann das erste sonst das zweite
const brightness = (rgb != undefined) ? Math.round(((parseInt(rgb[0]) * 299) + (parseInt(rgb[1]) * 587) + (parseInt(rgb[2]) * 114)) /1000) : 0;
return brightness < 150 ? 'white' : 'black'
}
//-----------------------------------------------------------------------------------
// Anfügen von svg Elementen zu #heatmap
const svg = d3.select('#svg_heatmap')
// Attribute svg
.attr("height", innerHeight)
.attr("width", width_client + margin.left + margin.right)
const g_area = d3.select("#svg_" + i_region_static_id)
// Anfügen einer Gruppe an das SVG Element
.append("g")
.attr("id", i_region_static_id + "_area")
.attr("transform", `translate(${margin.left},${margin.top})`);
const g_nodes = g_area
.append("g")
.attr("id", i_region_static_id + "_nodes")
//-----------------------------------------------------------------------------------
// X-Skalierung
const x_scale = d3.scaleBand()
.range([0, innerWidth])
.padding(0.01);
const xAxisCall = d3.axisBottom(x_scale)
// X-Achse erstellen
const xAxis = g_area
.append("g")
.attr("id", i_region_static_id + "_x_axis")
// Einfügen xAxis Values
xAxis.append("g")
.attr("id", i_region_static_id + "_x_axis_value")
.attr("class", "x axis")
.attr("transform", `translate(0, ${innerHeight})`)
// Einfügen xAxis Label
xAxis.append("text")
.attr("id", i_region_static_id + "_x_axis_label")
.attr("class", "x label")
.attr("x", innerWidth / 2)
.attr("y", innerHeight + 50)
.attr("text-anchor", "middle")
.text(labelGroup);
//-----------------------------------------------------------------------------------
// Y-Skalierung
const y_scale = d3.scaleBand()
.range([innerHeight, 0])
.padding(0.03);
const yAxisCall = d3.axisLeft(y_scale)
// Y-Achse erstellen
const yAxis = g_area
.append("g")
.attr("id", i_region_static_id + "_y_axis")
// Einfügen yAxis Value
yAxis.append("g")
.attr("id", i_region_static_id + "_y_axis_value")
.attr("class", "y axis")
// Einfügen yAxis Label
yAxis.append("text")
.attr("id", i_region_static_id + "_y_axis_label")
.attr("class", "y label")
.attr("y", - 45) // Y ist hierbei die horizontale ausrichtung
.attr("x", - innerHeight / 2) // X ist hierbei die vertikale ausrichtung
.attr("transform", "rotate(-90)")
.attr("text-anchor", "middle")
.text(labelVars)
//-----------------------------------------------------------------------------------
// Erstelle yColorscale
const yColorscale = d3.scaleLinear()
.range([innerHeight - 70, 0]);
// ColorScale Legend
const g_colorScale = g_area
.append("g")
.attr("id", i_region_static_id + "_colorLegend")
// Einfügen der Color Achse
g_colorScale.append("g")
.attr("id" , i_region_static_id + "_colorscale_axis")
.attr("transform", `translate(${innerWidth + 27},${35})`)
// Einfügen des Farbbalkens
g_colorScale.append("rect")
.attr("id", i_region_static_id + "_colorScale_legend")
.attr("x", innerWidth + 15)
.attr("y", 35)
.attr("rx", "4px")
.attr("width", 12)
.attr("height", innerHeight - 70)
.attr("stroke", "black")
.attr("stroke-width", "0.5px")
.attr("text-anchor", "middle")
.style("fill", "url(#"+ i_region_static_id + "_grad)");
const g_def = g_colorScale.append("defs")
.append("linearGradient")
.attr("id", i_region_static_id + "_grad")
.attr("x1", "0%")
.attr("x2", "0%")
.attr("y1", "0%")
.attr("y2", "100%");
//-----------------------------------------------------------------------------------
// Funktion für das Create, Update or Delete der D3 nodes
function update_heatmap(data){
const myId = data.map(function(d){return d.id});
const myGroups = data.map(function(d){return d.group});
const myVars = data.map(function(d){return d.variable});
const value = data.map(function(d){return d.value});
const extent = d3.extent(data, function(d){;return d.value});
var colorArrayValue = updateColorAxis(extent[0], extent[1]);
colorArrayValue.push(colorMidValue);
colorArrayValue.sort(function(a, b){return a - b});
const myColor = d3.scaleLinear()
.range([color_start, colorMid, colorEnd])
.domain(colorArrayValue)
const GradColors = [myColor(updateColorAxis(extent[0], extent[1])[1]), myColor(colorMidValue), myColor(updateColorAxis(extent[0], extent[1])[0])];
//-----------------------------------------------------------------------------------
// Dynamisches Update der X-Achse
x_scale.domain(myGroups)
const xAxisGroup = xAxis.select("#" + i_region_static_id + "_x_axis_value")
.call(xAxisCall);
//-----------------------------------------------------------------------------------
// Dynamisches Update der Y-Achse
y_scale.domain(myVars)
const yAxisGroup = yAxis.select("#" + i_region_static_id + "_y_axis_value")
.call(yAxisCall);
//-----------------------------------------------------------------------------------
// ColorScale Legend
yColorscale.domain(updateColorAxis(extent[0], extent[1]))
const yAxisColorCall = d3.axisRight(yColorscale)
.ticks(setColorAxisTicks(updateColorAxis(extent[0], extent[1])[0], updateColorAxis(extent[0], extent[1])[1]))
const yAxisColorGroup = g_colorScale.select("#" + i_region_static_id + "_colorscale_axis")
.call(yAxisColorCall);
g_def.selectAll(i_region_static_id + "stop")
.data(GradColors)
.enter()
.append("stop")
.style("stop-color", function(d){return d;})
.attr("offset", function(d,i){
return 100 * (i / (GradColors.length - 1)) + "%";
})
//-----------------------------------------------------------------------------------
// Create, Delete and Update
const rect_nodes = g_nodes.selectAll("." + i_region_static_id + "_node_grp")
.data(data)
const rect_node_grp = rect_nodes.enter()
.append("g")
/*each() für Create Sektion*/
/*Mit der Each() wird alles in der gruppe geupdated*/
.each(function(d, i) {
//Append Elemente an g
d3.select(this).append('rect')
.style("fill", function(d) {return myColor(d.value)})
d3.select(this).append('text')
})
.merge(rect_nodes)
.attr("class", i_region_static_id + "_node_grp")
//jedes Rect eine ID zu ordnen
.attr("id", function (d){return i_region_static_id + "_node_grp_" + d.id})
.attr("group", function(d) {return d.group})
.attr("variable", function(d) {return d.variable})
.attr("value", function(d) {return d.value})
/*each() für Update Sektion*/
.each(function(d, i) {
// Update meine Elemente in g tag
d3.select(this).select('rect')
.attr("class", i_region_static_id + "_node")
.transition()
.delay(50)
.attr("x", function(d) {return x_scale(d.group)})
.attr("y", function(d) {return y_scale(d.variable)})
.attr("width", x_scale.bandwidth())
.attr("height", y_scale.bandwidth())
.style("fill", function(d) {return myColor(d.value)})
d3.select(this).select('text').attr("class", i_region_static_id + "_label")
.transition()
.delay(50)
.attr("x", function(d) {return x_scale(d.group)})
.attr("y", function(d) { return y_scale(d.variable)})
.attr("dx", x_scale.bandwidth() / 2)
.attr("dy", y_scale.bandwidth() / 2)
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.attr("fill", function(d){return calculateContrast(myColor(d.value))})
.style("font-size", "14px")
.style("font-family", "sans-serif")
.text(function(d) {return (d.value)})
})
rect_nodes.exit().remove()
}
function resize() {
var innerWidth = parseInt(d3.select("#svg_" + i_region_static_id).style("width")) - margin.left - margin.right,
innerHeight
if(parseInt(d3.select("#svg_" + i_region_static_id).style("height")) < i_height){
innerHeight = parseInt(d3.select("#svg_heatmap").style("height")) + margin.top + margin.bottom;
}else{
innerHeight = i_height - margin.top - margin.bottom;
}
// SVG Dimension
svg.attr("height", innerHeight)
.attr("width", innerWidth)
// Aktualisieren der Range vom scale mit neuer breite und Höhe
x_scale.range([0, innerWidth], 0.1);
y_scale.range([innerHeight, 0]);
// Position der X-Achse und X-Label
xAxis
.call(xAxisCall)
.select("#" + i_region_static_id + "_x_axis_label")
.attr("x", innerWidth / 2)
.attr("y", innerHeight + 50);
// Position der Y-Achse und X-Label
yAxis
.call(yAxisCall)
.attr("transform", "translate(0," + 0 + ")")
.select("#" + i_region_static_id + "_y_axis_label")
.attr("y", - 45)
.attr("x", - innerHeight / 2)
// Position ColorSkale Farb-Balken
g_colorScale
.select("#" + i_region_static_id + "_colorScale_legend")
.attr("x", innerWidth + 15)
.attr("y", 35)
.attr("height", innerHeight - 70)
// Position ColorSkale Achse
g_colorScale
.select("#" + i_region_static_id + "_colorscale_axis")
.attr("transform", `translate(${innerWidth + 27},${35})`)
// D3 Kästchen dimension wird neu kalkuliert
g_nodes.selectAll("." + i_region_static_id + "_node")
.attr("x", function(d) {return x_scale(d.group)})
.attr("y", function(d) {return y_scale(d.variable)})
.attr("width", x_scale.bandwidth())
.attr("height", y_scale.bandwidth())
// D3 Kästchen Label wir neu Positioniert
g_nodes.selectAll("." + i_region_static_id + "_label")
.attr("x", function(d) {return x_scale(d.group)})
.attr("y", function(d) { return y_scale(d.variable)})
.attr("dx", x_scale.bandwidth() / 2)
.attr("dy", y_scale.bandwidth() / 2)
};
//-----------------------------------------------------------------------------------
d3.select(window).on('resize', resize);
update_heatmap(data1)
<style>
.label{
fill: rgba(0, 0, 0, 0.65);
font-size: 18px;
font-weight: bold;
font-family: sans-serif;
}
.axis{
color: rgba(0, 0, 0, 0.65);
font-size: 16px;
font-family: arial;
}
#svg_heatmap {
width: 100%;
height: 100%;
position: absolute;
}
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title> 1.Grundgerüst </title>
</head>
<body>
<svg id="svg_heatmap"></svg>
<script src=".v7.js" charset="utf-8"></script>
<script src="" charset="utf-8"> </script>
</body>
</html>