In my code below, I am able to draw a Treemap and also display the tag in each tree cell. But the text is overflowing the tile if it's a long word
I need to ensure the word stays in the tile even If it means putting .... after certain characters. How can I achieve them? Please have a look at the version of chart.js and Treemap I am using before providing the solution. Thanks a lot :)
var topTags = [
{tag:'android',num:42657},{tag:'reactjs',num:38844},{tag:'php',num:34381},{tag:'sql',num:29996},
];
var canvas = document.getElementById("treemap");
var ctx = canvas.getContext("2d");
var chart = window.chart = new Chart(ctx, {
type: "treemap",
data: {
datasets: [{
tree: topTags,
key: "num",
groups: ['tag'],
spacing: 0.5,
borderWidth: 1.5,
fontColor: "black",
borderColor: "grey"
}]
},
options: {
maintainAspectRatio: false,
legend: { display: false },
tooltips: { enabled: false }
}
});
CHART.JS AND TREEMAP VERSION :
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-chart-treemap#0.2.3"></script>
I solved it by splitting the text into multiple lines, starting a new line whenever the max width would be exceeded.
const chart = new Chart(context, {
type: 'treemap',
data: {
datasets: [
{
/* ... */
labels: {
display: true,
formatter(ctx: TreemapScriptableContext) {
if (ctx.type !== 'data') {
return;
}
return splitLabelToFit(ctx.raw["_data"].label, ctx.raw.w*0.9, ctx);
}
}
}
],
},
});
function splitLabelToFit(label: string, maxWidth: number, ctx: TreemapScriptableContext) {
const words = label.split(' ');
const lines = [];
let currentLine = '';
for (let i = 0; i < words.length; i++) {
const word = words[i];
const newLine = currentLine + ' ' + word;
const width = ctx.chart.ctx.measureText(newLine).width;
if (width < maxWidth) {
currentLine = newLine;
} else {
lines.push(currentLine);
currentLine = word;
}
}
lines.push(currentLine);
return lines;
}
Related
EDIT: I have narrowed it down to something like this:
for (i = 0; i < data.length; i++) {
const newCanvas = document.createElement("canvas");
newCanvas.id = data[i].design_name;
const currentDiv = document.getElementById("chartSpace");
var parentDiv = document.getElementById("gridHere");
parentDiv.insertBefore(newCanvas, currentDiv);
createChart([data[i].design_name], [data[i].design_start, data[i].design_end]);
}
With the create chart making the chart id = to the array 'labels':
const myChart = new Chart(
document.getElementById(labels),
config
);
I am attempting to create a tool that creates an 'n' number of charts in ChartJS and save each of them as images. Currently, designButtonClick() sends the 'event_fky' value to
getDesigns(event_fky) in my controller. This method returns all designs with that foreign key. In turn, the chart plots each design on the chart. I need to evolve this into
something that can make a group individual charts for each design based on how many designs there are. My current solution, still conceptual, is to have methods in my controller
create chart variables 'chartData [data here]' and 'labels[datahere]' while looping through the designs returned from getDesigns, and sending those back to the JS script createChart
'n' number of times for each design. It would also send html chart/html element ids based on the design_name attribute to send back to createChart. This way, it is create a unique
chart 'n' number of times.
To save the charts as images, I would use the same set of element ids generated by getDesigns to send the charts to images using JS' toBase64Image() function and saving them to the
user's system.
Is this the best way of solving this problem? Or is this spaghetti, and is there a better method for this? My attempts to find better online answers have only resulted in docs on
updating one chart dynamically, not creating a dynamic number of charts. Much help is appreciated, code is below as well as a screenshot of the current chart output.
JavaScript:
var labels = [];
var cData = [];
function designButtonClick() {
var event_fky = 3;
$.ajax({
url: 'Tree/getDesigns',
type: 'POST',
data: { event_fky }
}).done(function (data) {
for (i = 0; i < data.length; i++) {
labels.push(data[i].design_name);
cData.push([data[i].design_start, data[i].design_end])
}
createChart(labels, cData);
});
}
function createChart(labels, cData) {
const data = {
labels: labels,
datasets: [{
barThickness: 2,
categoryPercentage: .5,
label: 'Design Time',
data: cData,
backgroundColor: [
'rgba(255, 26, 104, 0.2)'
],
borderColor: [
'rgba(255, 26, 104, 1)'
],
borderWidth: 1,
borderSkipped: false,
borderRadius: 20
}]
};
const config = {
type: 'bar',
data,
options: {
indexAxis: 'y',
scales: {
y: {
beginAtZero: true
},
x: {
min: 0,
max: 6000,
ticks: {
stepSize: 1000
}
}
}
}
};
const myChart = new Chart(
document.getElementById('myChart'),
config
);
}
C# Controller:
public ActionResult getDesigns(int? event_fky)
{
var designs = from e in _context.designs
where (event_fky.HasValue ? e.event_fky == event_fky : e.event_fky == null)
select new
{
design_pky = e.design_pky,
design_name = e.design_name,
design_start = e.design_start,
design_end = e.design_end
};
return this.Json(designs, JsonRequestBehavior.AllowGet);
}
Designs Table:
--------Design--------
design_pky |int
event_fky |int
design_name |varchar
design_start |number
design_end |number
Screenshot of Chart
This is a working answer for the javascript:
var eventList = function () {
var tmp = null;
$.ajax({
'async': false,
url: 'Tree/getEventIDs',
type: 'POST',
data: {},
'success': function (data) {
tmp = data;
}
});
return tmp;
}();
for (var i = 0; i < eventList.length; i++) {
event_fky = eventList[i].event_pky;
event_name = eventList[i].event_name;
event_length = eventList[i].event_end;
var designList = function () {
var tmpi = null;
$.ajax({
'async': false,
url: 'Tree/getDesigns',
type: 'POST',
data: {event_fky},
'success': function (data1) {
tmpi = data1;
}
});
console.log(event_fky);
console.log(tmpi);
return tmpi;
}();
var dLabels = [];
var dLengths = [];
for (var j = 0; j < designList.length; j++) {
dLabels.push(designList[j].design_name);
dLengths.push([designList[j].design_start, designList[j].design_end]);
}
const newCanvas = document.createElement("canvas");
newCanvas.id = event_name;
const currentDiv = document.getElementById("chartSpace");
var parentDiv = document.getElementById("gridHere");
parentDiv.insertBefore(newCanvas, currentDiv);
if (dLabels.length != 0) {
createChart(dLabels, dLengths, event_name, event_length);
}
}
}
function createChart(labels, cData, evName, evLen) {
// setup
const data = {
labels: labels,
datasets: [{
barThickness: 4,
categoryPercentage: .5,
label: evName,
data: cData,
backgroundColor: [
'rgba(' + Math.random() * 85 + ', ' + Math.random() * 170 + ', ' + Math.random() * 255 + ', 1)'
],
borderColor: [
'rgba(255, 26, 104, 1)'
],
borderWidth: 0,
borderSkipped: false,
borderRadius: 20
}]
};
// config
const config = {
type: 'bar',
data,
options: {
indexAxis: 'y',
scales: {
y: {
beginAtZero: true
},
x: {
min: 0,
max: evLen,
ticks: {
stepSize: 100
}
}
}
}
};
// render init block
const myChart = new Chart(
document.getElementById(evName),
config
);
return myChart;
}
Good day to all:
Recently I have started working with Vue.js(2.6.12) + Vuetify(2.3.10) and Chart.js(3.0.2). So I'm a newbie (again).
I have created a componenent which wraps the Bar chart that Chart.js allows us to create. Here it's a picture of it:
The only two things that I want to change are the little box that comes near to the legend title and some grid x lines.
In the case of the little legend box is red. I would like that It's aligned with the blue color of the legend title text. As I show you in this picture:
Finally I want to include some color (pink) in the X axis:
I'm unable to achieve this. I have regarded and follow the official documentation and nothing works :S.
Create the function for painting some axis in colors: https://www.chartjs.org/docs/3.0.2/samples/scale-options/grid.html
Change the colors of the legend: https://www.chartjs.org/docs/3.0.2/configuration/legend.html#legend-label-configuration
In the case of the legend title box I have noticed that always takes the color of the first element.
In the case of the axis, the function supported by chart.js doesn't work for me. It doesn't print me the x axis at all.
Things that I have tried:
Upgrade to chart.js 3.3.0 but I got an error like this: ""
The reason I was using 3.0.2 it's because it's the only version which
is working to me from the 3.0.0 versions.
Downgrade to 2.9.3/4. I wasn't able to change the color of the box or the axis lines, but the rest worked fine.
Use a wrapper: https://vue-chartjs.org/. It didn't work
Code of the whole component:
<template>
<div class="container pa-3" fill-height fluid style="width: 100%">
<!-- We create the chart -->
<canvas id="myChart1" />
</div>
</template>
<script>
import Chart from "chart.js/auto";
export default {
name: "Chart",
components: {},
props: {},
data: () => ({
ctx: null,
myChart: null,
type: "bar",
data: {
labels: ["a", "b", "c", "d"],
datasets: [
{
data: [1, 2, 3, 4],
backgroundColor: ["#c30", "#e37609", "#ffda05", "#fffb05"],
},
],
},
options: {
plugins: {
legend: {
display: true,
labels: {
color: "#00a3fb",
},
},
},
scales: {
},
},
}),
methods: {
createChart: function () {
// destroy the previous graph
if (this.myChart != null) this.myChart.destroy();
// create a new one
this.ctx = document.getElementById("myChart1");
this.myChart = new Chart(this.ctx, {
type: this.type,
data: this.data,
options: this.options,
});
this.myChart.render();
},
},
destroyed() {},
mounted() {
this.createChart();
},
watch: {},
};
</script>
<style scoped>
</style>
For using it, you should:
Import it in the section
Declare it in the component section
Call it by <NameOfComponetGiven/> tag
Any help would be quite aprecciated.
Thank you very much.
To customize the legend box color you will need to use a custom HTML legend, there you can specify it with CSS, for the pink grid lines you can use the scriptable options. For both see example:
const getOrCreateLegendList = (chart, id) => {
const legendContainer = document.getElementById(id);
let listContainer = legendContainer.querySelector('ul');
if (!listContainer) {
listContainer = document.createElement('ul');
listContainer.style.display = 'flex';
listContainer.style.flexDirection = 'row';
listContainer.style.margin = 0;
listContainer.style.padding = 0;
legendContainer.appendChild(listContainer);
}
return listContainer;
};
const htmlLegendPlugin = {
id: 'htmlLegend',
afterUpdate(chart, args, options) {
const ul = getOrCreateLegendList(chart, options.containerID);
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = chart.options.plugins.legend.labels.generateLabels(chart);
items.forEach(item => {
const li = document.createElement('li');
li.style.alignItems = 'center';
li.style.cursor = 'pointer';
li.style.display = 'flex';
li.style.flexDirection = 'row';
li.style.marginLeft = '10px';
li.onclick = () => {
const {
type
} = chart.config;
if (type === 'pie' || type === 'doughnut') {
// Pie and doughnut charts only have a single dataset and visibility is per item
chart.toggleDataVisibility(item.index);
} else {
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
}
chart.update();
};
// Color box
const boxSpan = document.createElement('span');
boxSpan.style.background = options.legendBoxColor || item.fillStyle;
boxSpan.style.borderColor = item.strokeStyle;
boxSpan.style.borderWidth = item.lineWidth + 'px';
boxSpan.style.display = 'inline-block';
boxSpan.style.height = '20px';
boxSpan.style.marginRight = '10px';
boxSpan.style.width = '20px';
// Text
const textContainer = document.createElement('p');
textContainer.style.color = options.legendTextColor || item.fontColor;
textContainer.style.margin = 0;
textContainer.style.padding = 0;
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
const text = document.createTextNode(item.text);
textContainer.appendChild(text);
li.appendChild(boxSpan);
li.appendChild(textContainer);
ul.appendChild(li);
});
}
};
const options = {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1,
backgroundColor: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"]
}]
},
options: {
scales: {
x: {
grid: {
color: (line) => ((line.index === 2 || line.index === 3) ? 'pink' : 'rgba(0,0,0,0.1)'),
lineWidth: (line) => ((line.index === 2 || line.index === 3) ? 6 : 1)
}
}
},
plugins: {
htmlLegend: {
// ID of the container to put the legend in
containerID: 'legendContainer',
legendBoxColor: 'blue',
legendTextColor: 'blue'
},
legend: {
display: false,
}
}
},
plugins: [htmlLegendPlugin]
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<div id="legendContainer"></div>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.0.2/chart.js"></script>
</body>
I am having 3 different charts on my web page for which I am using Chartjs. The problem occurs when I hover over one of the charts it starts showing old data. I am creating chart on a HTML button click. I checked few answers on stackoverflow (for eg. destroy()) but that is not working for me. Below is the function for chart. please guide me regarding this.
<script>
function dailyPrd1() {
var pl_type1 = "";
var pl_sl1 = "";
var date1="";
pl_type1 = plant_type1.options[plant_type1.selectedIndex].innerHTML;
//alert(pl_type1);
pl_sl1 = plant_select1.options[plant_select1.selectedIndex].innerHTML;
//alert(pl_sl1);
date1 = document.getElementById('date2').value;
//alert(date1);
var pl2 = "";
pl2 = pl_type1 + '-' + pl_sl1;
var obj2 = "";
var hrs1 = [];
var prod1 = [];
var colr1 = [];
var req2 = new XMLHttpRequest();
var config_string2 = '<%=ConfigurationManager.AppSettings["serverip11"].ToString() %>' + pl_sl1 + "/" + pl_type1 + "/" + date1;
req2.open("GET", config_string2, true);
req2.send();
req2.overrideMimeType("application/json");
req2.onload = function () {
obj2 = JSON.parse(this.response);
obj2 = JSON.parse(obj2);
var len12 = 0;
len12 = obj2.day_list.length;
for (i = 0; i < len12; i++) {
hrs1.push(obj2.day_list[i].day);
}
var speedCanvas2 = document.getElementById("myChart3");
Chart.defaults.global.defaultFontFamily = "Lato";
Chart.defaults.global.defaultFontSize = 16;
var chartOptions2 = {
responsive: true,
scales: {
xAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Days'
}
}],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Value in cu.m'
}
}]
},
legend: {
display: true,
position: 'top',
labels: {
boxWidth: 80,
fontColor: 'black'
}
}
};
var speedData2 = {
labels: hrs1,
// datasets: [dataFirst, dataSecond]
};
var lineChart2 = new Chart(speedCanvas2, {
type: 'bar',
data: speedData2,
options: chartOptions2
});
var iti1 = 0;
iti1 = obj2.prod_qty.length;
var aaa = 'Pl 1'
for (j = 0; j < iti1; j++) {
prod1.push(obj2.prod_qty[j].tot_prod);
}
addData(lineChart2, pl2, getRandomColor(), prod1);
}
}
</script>
After you change your data you should update your chart with chartVariable.update().
I made a JSBin which explains you how to use it.
The important function for you is the last in the code, addDataButton() which gets triggered by a button click. In this function I add new data and update my chart after that.
Instead of chartVariable and chart you should use lineChart2 in your case.
Complete code:
let numberOfDataCounter = 0 // Current data counter
const numberOfDataAtBeginning = 4 // data number to start with
const weekdays = ["Su", "Mo", "Du", "We", "Th", "Fr", "Sa"]
function randomNumber(){
let randomNumber = Math.floor(Math.random()*100)
return randomNumber
}
let chartData = {
label: [],
data: []
}
function addData (){
chartData.label.push(weekdays[numberOfDataCounter % 7])
chartData.data.push(randomNumber())
numberOfDataCounter++
}
// Fill chart with data at beginning
while (numberOfDataAtBeginning>numberOfDataCounter) {
addData()
}
let data = {
labels: chartData.label,
datasets: [{
label: "Label",
data: chartData.data
}]
}
let chart = new Chart(document.getElementById("chart"), {
type: 'line',
data: data,
options: {
scales: {
yAxes: [{
ticks: {
min: 0,
max: 100
}
}]
}
}
});
function addDataButton(){
addData()
chart.update()
}
Herewith I used JavaScript line chart within Laravel framework. Mouse hover tool tip to display point values working perfectly with label:function(...). I need to display only y-axis value on every point at chart on create (Without mouse hovering). To perform this I used drawDatasetPointsLabels() method from How to display Line Chart dataset point labels with Chart.js? and call it on chart options. But unfortunately it is not working. Below is the code.
chart.blade
monthlabels={.......};
salesqty = {......};
stockqty = {......};
document.getElementById("history_canvas_holder").innerHTML = ' ';
document.getElementById("history_canvas_holder").innerHTML = '<canvas id="historyChart" width="500" height="350"></canvas>';
var ctx = document.getElementById("historyChart").getContext('2d');
var myLineChart = new Chart(ctx,
{
type: 'line',
data:
{
labels: monthlabels,
datasets: [{
label: 'Sales',
data: salesqty,
borderColor: [
'rgba(255,99,132,1)'
],
borderWidth: 2
},
{
label: 'Stock',
data: stockqty,
borderColor: [
'rgba(0,161,232)'
],
borderWidth: 2
}
]
},
options: {
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var label = data.datasets[tooltipItem.datasetIndex].label || '';
if (label) {
label += ': ';
}
label += Math.round(tooltipItem.yLabel * 100) / 100;
return label;
},
onAnimationProgress: function() { drawDatasetPointsLabels(ctx) },
onAnimationComplete: function() { drawDatasetPointsLabels(ctx) }
}
}
}
});
function drawDatasetPointsLabels(ctx) {
ctx.font = '.9rem "ABCD",sans-serif';
ctx.fillStyle = '#AAA';
ctx.textAlign="center";
$(historyChart.datasets).each(function(idx,dataset){
$(dataset.points).each(function(pdx,pointinfo){
if ( pointinfo.value !== null ) {
ctx.fillText(pointinfo.value,pointinfo.x,pointinfo.y - 15);
}
});
});
}
In the type of v2.5.0
`options: {
tooltips: {
enabled: false
}
}`
or
`tooltips :{
custom : function(tooltipModel) {
tooltipModel.opacity = 0;
}
}`
type of v2.1.4
`Chart.defaults.global.tooltips.enabled = false;`
people said those ways are work, but not in all charts type.
I am trying to pass in my_series to series in highcharts to graph stacked bars. From my php, I get hash, which is like this:
{"Oat":{"10":"AA","11":"H"},"Brown Rice":{"10":"AA","11":"BB"}}
I actually dont know what is wrong with my code. I think it should work, but when I run it, I do not see anything on the screen. I checked that sample_name has "Oat" and "Brown Rice", which is what I want.
What I eventually want is a color-coded stacked bar graph that has samples on the yAxis and position on the xAxis. I think I almost got to what I want, it's just a problem with making an actual graph.
Thank you!
$(function() {
var allele_str = [];
var sample_name = [];
var hash = {};
var my_series = {
data: []
};
var position = [];
var options = {
chart: {
renderTo: 'container',
defaultSeriesType: 'bar',
zoomType: 'xy',
inverted: true
},
title: {
text: 'Gene'
},
xAxis: {
categories: [],
},
legend: {
enabled: false
},
yAxis: {
title: {
text: 'Position'
},
labels: {
enabled: false
}
}
plotOptions: {
series: {
stacking: 'normal'
}
}
series: []
};
$.getJSON('6K_load.php', function(data) {
sample_name = Object.keys(data);
options.xAxis.categories.push(sample_name);
for (var x in sample_name) { // sample Oat, BR
for (var k in data[sample_name[x]]) { // pos
series.name = k;
var z = data[sample_name[x]][k];
hash[z] = 1;
allele_str.y = hash[z];
if (z === 'AA') {
allele_str.color = 'grey';
}
if (z === 'BB') {
allele_str.color = 'blue';
}
if (z === '--') {
allele_str.color = 'white';
}
if (z === 'H') {
allele_str.color = 'red';
}
my_series.data.push(allele_str);
}
options.series.push(my_series);
}
var chart = new Highcharts.Chart(options);
});
});
The problem is that you not parse your data to number value, you need to use parseFloat() in this line
my_series.data.push(parseFloat(allele_str));