Using chartjs, how to define relative width and height of canvas? - css

Using Chart.js version 4.2.0, I try to display a chart on a maximized window on Windows 11 where height of chart is 100% of screen's height and width of chart is 80% of screen's width.
I tried with following code
<body>
<div height="100%" width="80%">
<canvas id="canvas">
But this simple attributes don't do the job.
Finally, I have found this solution
<body>
<div>
<canvas id="canvas">
</canvas>
</div>
, options:
{ responsive: true
, maintainAspectRatio: true
, aspectRatio: 1.65
var ctx = document.getElementById("canvas").getContext("2d");
var myChart = new Chart(ctx, config);
myChart.canvas.parentNode.style.width = '80%';
This works correctly but is a little tricky because width (not height) is set dynamically in Javascript code and aspectRatio must be manually fixed in options.
Is there a simple solution to define width to 80% of screen and height to 100% of screen ?
My current screen size are 1920x1080.
What I obtain is
When I suppress aspectRatio in options, I obtains following chart

It seems that the standard way for a chart.js plot to fill a certain area with a size set in css, is to do these:
include the canvas in a div that doesn't contain anything else
set the size in the style of the div, not the canvas (that is already done in your code)
have at least one size in absolute units, (that is not both in %) - in your case
<div style="height:100vh; width:80vw">
<canvas id="myChart"></canvas>
</div>
See also this comment from the docs.
set chart options maintainAspectRatio: false and responsive: true - the latter for the chart to be redrawn when you resize the window.
Here's a code snippet doing these, including a plugin I wrote that displays the current sizes of the canvas, div and window
const plugin = {
id: 'canvasSizeMonitor',
currentWidth: 0,
currentHeight: 0,
resizing: false,
displaySizes(chart){
const canvas = chart.canvas,
div = canvas.parentElement;
document.querySelector('#sizes').innerText+=
`div: ${div.offsetWidth}x${div.offsetHeight}\n`+
`canvas: ${canvas.offsetWidth}x${canvas.offsetHeight}\n`+
`window:${window.innerWidth}x${window.innerHeight}\n`+
`0.8 * ${window.innerWidth} = ${Math.round(0.8*window.innerWidth)}\n`+
'---------\n'//`aspRatio: ${chart.options.aspectRatio.toFixed(3).replace(/[.]?0*$/, '')}\n\n`;
},
afterRender(chart){
if(!plugin.resizing &&
(chart.canvas.offsetWidth !== plugin.currentWidth ||
chart.canvas.offsetHeight !== plugin.currentHeight)){
plugin.resizing = true;
setTimeout(
function(){
plugin.resizing = false;
plugin.currentWidth = chart.canvas.offsetWidth;
plugin.currentHeight = chart.canvas.offsetHeight;
plugin.displaySizes(chart);
}, 500
)
}
}
};
chart = new Chart(document.getElementById("myChart"), {
type: 'line',
data: {
datasets: Array.from({length: 8}, (_, i)=>({
label: `k = ${i+1}`,
data: Array.from({length: 100}, (_, j)=>({
x: j/50, y: Math.exp(-j/10)*Math.cos((i+1)*j*Math.PI/100)
}))
}))
},
options: {
parsing: {
xAxisKey: 'x',
yAxisKey: 'y'
},
pointStyle: false,
borderWidth: 1,
responsive: true,
maintainAspectRatio: false,
//aspectRatio: 1,
scales: {
x: {
type: 'linear',
grid: {
drawOnChartArea: true,
lineWidth: 1
},
border:{
color: '#000',
},
ticks: {
display: true,
color: '#000',
padding: 10
},
title: {
display: true,
text: 'x',
align: 'end'
}
},
y: {
type: 'linear',
ticks: {
padding: 10,
color: '#000',
},
grid: {
drawOnChartArea: true,
lineWidth: 1
},
border:{
color: '#000',
},
title: {
display: true,
text: 'f[k](x)',
align: 'end'
}
}
},
plugins:{
legend:{
position: 'right'
}
},
animation: {duration: 0}
},
plugins: [plugin]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.1.2/chart.umd.js"
integrity="sha512-t41WshQCxr9T3SWH3DBZoDnAT9gfVLtQS+NKO60fdAwScoB37rXtdxT/oKe986G0BFnP4mtGzXxuYpHrMoMJLA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<body style="margin: 0">
<pre id="sizes" style="position: absolute; top: 0; right: 10px; text-align: right;background-color:rgba(255, 200, 100,0.4)"></pre>
<div style="height:100vh; width:80vw; padding:0; margin: 0; background: red">
<canvas style="background: #ddd" id="myChart"></canvas>
</div>
</body>

Related

Remove chart.js canvas padding and margin

I am using chart.js to show a line chart in an Angular component. The code does not have any padding or margins added for the chart. I added styling to set the padding and margin to zero both in the create the chart in the .ts file and in the style sheets and nothing makes any difference. When I inspect the element in Chrome developer mode it does not show any padding or margins, so I think the styling is internal to chart.js. Below is a screenshot with the chart element highlighted in Chrome developer mode.
As you can see, there is a margin or border on the left, bottom and right of the chart canvas element, but strangely not at the top. The spacing between the Y-Axis labels and the left of the chart are especially bad when viewed on a mobile phone so I really need to remove it. I have spent hours setting every element I can think of to have 0 padding and 0 margins and trying different changes to the code and style sheets and searching through other articles on the Internet including Stackoverflow but none of them seem to answer this specific question.
I also tried adding box-sizing:border-box; to the div container containing the chart but it did not make any difference, i.e. as suggested in this article:
Why is chart.js canvas not respecting the padding of the container element?
Here is my chart configuration:
this.myChart = new Chart("chartCanvas", {
type: 'line',
data: {
labels: labelVars,
datasets: datasets
},
options: {
elements: {
point: {
radius: 0
},
},
responsive: true,
maintainAspectRatio: false,
tooltips: {
mode: 'index',
intersect: false,
enabled: width > 640
},
hover: {
mode: 'nearest',
intersect: true
},
legend: {
display: false,
position: 'bottom',
labels: {
}
},
scales: {
xAxes: [{
type: 'time',
time: {
unit: 'week',
tooltipFormat: 'MM/DD/YYYY'
},
display: true,
scaleLabel: {
display: true,
labelString: ''
},
ticks: {
autoSkip: true,
padding: 0,
maxTicksLimit: this.getTickLimit(),
maxRotation: this.getRotation(),
minRotation: this.getRotation()
},
gridLines: {
display: false,
offsetGridLines: true
}
}],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: ''
},
ticks: {
maxTicksLimit: 6,
beginAtZero: false
}
}]
}
}
});
Here is the current html:
<div class="chartContainer">
<canvas *ngIf="displayChart" id="chartCanvas"></canvas>
</div>
And here is the current css:
#chartCanvas {
padding-left: 0;
margin-left: 0;
}
.chartContainer {
height: 500px;
margin: 0;
padding: 0;
align-items: left;
align-self: left;
align-content: left;
}
Has anyone else encountered this problem?
I found the problem. I had the scaleLabel turned on but was not showing a scale label, which was adding extra space. I changed scaleLabel display to false as shown below and the space went away:
yAxes: [{
display: true,
scaleLabel: {
display: false,
labelString: ''
},
Depending on your chartjs config, you just have to set the padding of the chart.js config.
Info: Offset's in the canvas cannot be altered with css, since the chart is "drawn" on it.
(details: in the documentation)
For a example:
const config = {
type: 'line',
data: data,
options: {
...
plugins: {
....
},
scales:{
x: {
ticks:{ padding:0 }
}
},
}
};

Vis Network + Vuetify Tabs - Networks won't fill screen

I am having some problems getting some Vis JS Networks to fill the available space under the App Bar correctly in a '' element. The graph element will fill the space, but the 100% I height I have had to apply to both '.v-tabs-items' and '.v-window-item' seems to ignore the padding and creates a scroll-bar, I have also set 100% width and there is no horizontal scroll.
It also doesn't adjust properly when resizing the window, requiring a tab switch to readjust to the correct size.
If I remove the 100% Height and Width then the tab content is constrained to a small rectange.
I have simplified the relevant code and it is below, there are two parts, the App.Vue file which contains the main screen and also the GraphView.Vue file which contains the Component that is used to draw the graph.
App.Vue
<template>
<v-app>
<v-app-bar app>
<v-toolbar-title class="headline text-uppercase">
<span>Test</span>
</v-toolbar-title>
<template v-slot:extension>
<v-tabs v-model="graphTab">
<v-tab v-for="(graph, title) in getGraphData()" :key="title">
{{ title }}
<span style="display: none"> {{ graph }}</span>
</v-tab>
</v-tabs>
</template>
</v-app-bar>
<v-content>
<v-container fluid fill-height>
<v-tabs-items v-model="graphTab">
<v-tab-item v-for="(graph, title) in getGraphData()" :key="title">
<GraphView
:nodes="graph.nodes"
:edges="graph.edges"
:options="defaultVisOptions"
></GraphView>
</v-tab-item>
</v-tabs-items>
</v-container>
</v-content>
</v-app>
</template>
<script>
import GraphView from "./components/GraphView";
import { DataSet } from "vis-network";
const defaultVisOptions = {
autoResize: true,
height: "100%",
width: "100%",
edges: {
arrows: "to"
},
nodes: {
shape: "circle"
}
};
export default {
name: "App",
components: {
GraphView
},
data: () => ({
defaultVisOptions: defaultVisOptions,
graphTab: null
}),
methods: {
getGraphData: function() {
return {
"Graph 1": {
nodes: new DataSet([
{ id: 1, label: "Node 1" },
{ id: 2, label: "Node 2" },
{ id: 3, label: "Node 3" },
{ id: 4, label: "Node 4" },
{ id: 5, label: "Node 5" }
]),
edges: new DataSet([
{ from: 1, to: 3 },
{ from: 1, to: 2 },
{ from: 2, to: 4 },
{ from: 2, to: 5 }
])
},
"Graph 2": {
nodes: new DataSet([
{ id: 1, label: "Node 1" },
{ id: 2, label: "Node 2" },
{ id: 3, label: "Node 3" },
{ id: 4, label: "Node 4" },
{ id: 5, label: "Node 5" }
]),
edges: new DataSet([
{ from: 1, to: 2 },
{ from: 2, to: 3 },
{ from: 3, to: 4 },
{ from: 4, to: 5 }
])
}
};
}
}
};
</script>
<style scoped>
.v-tabs-items {
height: 100%;
width: 100%;
}
.v-window-item {
height: 100%;
width: 100%;
}
</style>
GraphView.Vue
<template>
<div class="graphContainer" ref="graphContainer"></div>
</template>
<script>
import { Network } from "vis-network";
export default {
name: "GraphView",
props: ["nodes", "edges", "options"],
mounted() {
this.network = new Network(
this.$refs.graphContainer,
{ nodes: this.$props.nodes, edges: this.$props.edges },
this.$props.options
);
}
};
</script>
<style scoped>
.graphContainer {
width: 100%;
height: 100%;
}
</style>
As explained above, the expected result is that the Graphs fill the available space without creating scroll bars but this doesn't happen.
I didn't test it with your code but the container should be styled like:
<div style="position: relative; width: 100%; height: 100%;">
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
ref="graphContainer"
/>
</div>
This way it will fill all available space (you may need to set position: relative on the parent) without stretching it any further.

Issue Positioning Radar Chart Using Chart.js

I'm trying to position a radar chart using chart.js. My chart is outputting aligned to the right.
I've tried setting the container to width: 100%, text-align: center and margin: 0 auto but the output is still not correct.
Code:
<div class="chartholder">
<canvas id="myChart" width="400" height="400"></canvas>
</div>
<script>
var ctx = document.getElementById("myChart").getContext('2d');
var marksData = {
labels: ["Digital Product Definition", "Digital Thread", "Digital Twin", "Digital Innovation Platform", "Top Performers", "Others"],
datasets: [{
label: "Your Company",
backgroundColor: "rgba(200,0,0,0.2)",
data: [4.8,7.2,9.6,7.2, 4, 2]
}]
};
var radarChart = new Chart(ctx, {
type: 'radar',
data: marksData
});
var chartOptions = {
scale: {
ticks: {
beginAtZero: true,
min: 0,
max: 10,
stepSize: 1
},
pointLabels: {
fontSize: 18
}
},
legend: {
position: 'left'
},
responsive: true,
};
var radarChart = new Chart(ctx, {
type: 'radar',
data: marksData,
options: chartOptions
});
</script>
CSS
canvas {
margin: 0 auto!important;
width: 100%;
height: auto;
}
.chartholder {
text-align: center;
width: 100%;
margin: 0 auto;
background-color: blue;
}
Screenshot of the output. The yellow is just to show the alignment better.

google chart size issue

Multiple charts are displayed on one page, some users have larger screen and some have small standard screen.
Is there any way to have flexible width so it can fit on any size of monitor screen.
I have tried setting width to 100% but it doesn't work.
<table width="100%">
<tr>
<td>
<div id="chart_GP" class="chart_div">
<div class="chart_preloader"> </div>
</div>
</td>
<td id="td_profit">
<div id="chart_Profit" class="chart_div">
<div class="chart_preloader"> </div>
</div>
</td>
<td>
<div id="chart_Visits" class="chart_div">
<div class="chart_preloader"> </div>
</div>
</td>
</tr>
</table>
CSS
.chart_preloader {
height:250px;
width: 100%;
background:url('/images/pie.gif') center center no-repeat;
background-color : #EEF;
}
.chart_div {
border-color: #006699;
border-style: solid;
border-width: 1px;
}
Google Chart Options
var options = {
title: "Last 12 Months Gross Profit per Month",
animation: {
duration: 1500,
startup: true //This is the new option
},
pointSize: 5,
curveType: 'function',
backgroundColor: chart_background_Color,
colors: [chartLine_Color],
legend: 'none',
width: 385,
height: 250,
tooltip: { isHtml: true },
hAxis: {
slantedText: true,
slantedTextAngle: 90
},
vAxis: {
title: 'Profit',
titleTextStyle: { italic: false, fontName: 'Calibri', fontSize: '12', bold: 'false' }
//format: '#\'%\''
}
};
recommend leaving width settings out of the chart options and use a css layout
which the chart will follow
rarely use tables, so depends on layout you're looking for, following is one example
to make the chart responsive, re-draw the chart when the window resizes...
google.charts.load('current', {
callback: function () {
var data = new google.visualization.DataTable();
data.addColumn('date', 'Time of Day');
data.addColumn('number', 'Rating');
data.addRows([
[new Date(2015, 0, 1), 5],
[new Date(2015, 0, 7), 3],
[new Date(2015, 0, 14), 3],
[new Date(2015, 0, 21), 2],
[new Date(2015, 0, 28), 8]
]);
var options = {
title: "Last 12 Months Gross Profit per Month",
animation: {
duration: 1500,
startup: true //This is the new option
},
pointSize: 5,
curveType: 'function',
backgroundColor: 'cyan',
colors: ['red'],
legend: 'none',
height: 250,
tooltip: { isHtml: true },
hAxis: {
slantedText: true,
slantedTextAngle: 90
},
vAxis: {
title: 'Profit',
titleTextStyle: { italic: false, fontName: 'Calibri', fontSize: '12', bold: 'false' }
//format: '#\'%\''
}
};
var chart1 = new google.visualization.LineChart(document.getElementById('chart_GP'));
drawChart(chart1);
var chart2 = new google.visualization.LineChart(document.getElementById('chart_Profit'));
drawChart(chart2);
var chart3 = new google.visualization.LineChart(document.getElementById('chart_Visits'));
drawChart(chart3);
// make chart responsive
window.addEventListener('resize', function () {
drawChart(chart1);
drawChart(chart2);
drawChart(chart3);
}, false);
function drawChart(chart) {
chart.draw(data, options);
}
},
packages:['corechart']
});
.chart_preloader {
height:250px;
width: 100%;
background:url('/images/pie.gif') center center no-repeat;
background-color : #EEF;
}
.dashboard {
text-align: center;
}
#chart_GP {
float: left;
}
#chart_Profit {
display: inline-block;
}
#chart_Visits {
float: right;
}
.chart_div {
border-color: #006699;
border-style: solid;
border-width: 1px;
width: 32%;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div class="dashboard">
<div id="chart_GP" class="chart_div">
<div class="chart_preloader"> </div>
</div>
<div id="chart_Profit" class="chart_div">
<div class="chart_preloader"> </div>
</div>
<div id="chart_Visits" class="chart_div">
<div class="chart_preloader"> </div>
</div>
</div>

remove tooltip space between border and content highcharts

i'm using highcharts for genetating graphs, problem is, highcharts is generating space between border and content of Tooltip and labels of the Pie diagram is visible between that generated space.
can any has solving for my problem.
Please check with below example.
$(function () {
var chart;
$(document).ready(function () {
chart = new Highcharts.Chart({
chart: {
renderTo: 'graf1',
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false
},
title: {
margin: 40,
text: 'Podíl všech potřeb'
},
tooltip: {
borderWidth: 1,
backgroundColor: "rgba(255,255,255,0)",
borderRadius: 0,
shadow: false,
useHTML: true,
percentageDecimals: 2,
backgroundColor: "rgba(255,255,255,1)",
formatter: function () {
return '<div class="tooltop">'+this.point.name + '<br />' + '<b>' + Highcharts.numberFormat(this.y).replace(",", " ") + ' Kč [' + Highcharts.numberFormat(this.percentage, 2) + '%]</b></div>';
}
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
zIndex: 1,
enabled: true,
color: '#000000',
connectorWidth: 2,
useHTML: true,
formatter: function () {
return '<span style="color:' + this.point.color + '"><b>' + this.point.name + '</b></span>';
}
}
}
},
series: [{
type: 'pie',
name: 'Potřeba',
data: [
['Firefox', 45.0],
['IE', 26.8], {
name: 'Chrome',
y: 12.8,
sliced: true,
selected: true
}, ['Safari', 8.5],
['Opera', 6.2],
['Others', 0.7]
]
}]
});
});
});
.label {
z-index: 1!important;
}
.highcharts-tooltip span {
background-color:white;
border:1px solid green;
opacity:1;
z-index:9999!important;
}
.tooltip {
padding:5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<div id="graf1" style="width: 400px; height: 250px; float:left"></div>
tooltip space between order and content highcharts
SVG tooltip elements can be hidden through:
tooltip: {
borderWidth: 0,
backgroundColor: "rgba(255,255,255,0)",
shadow: false,
Example: http://jsfiddle.net/x8Lq0yr9/1/
This will eliminate the space between border and HTML content.
you are using div class="tooltop" where as tooltop class is nowhere. Also, .highcharts-tooltip is used in highcharts at two places . one at datalables and second in tiiptip.
Unless you want specific border color , you can remove that css class from your code and tooltip loks good.
.tooltipSpan {
opacity:1;
z-index:9999!important;
width:100%;
}
See this Fiddle
Highcharts creates these using svg which makes it so the background shows up underneath certain elements. To avoid this undesired behavior the border and background settings have been turned off and all these are controlled, instead, using html in the formatter function. Using HTML makes it so the background does not show up underneath certain elements.
tooltip: {
backgroundColor: undefined,
borderColor: undefined,
borderWidth: 0,
}

Resources