Chart.js 更改图例切换行为
I have a radar chart from chart.js. Currently it loads in all the data which works great and the supporting legend behaves by clicking on the legend label which toggles off the data associated to the legend able. I want to be able to click on the legend label, it then toggles all the other off and maybe introduce an 'all' option? Is this doable with chart.js?
Here is how my chart looks now:
var chartata = {
labels: [
"Strategic Development and Ownership",
"Driving change through others",
"Exec Disposition",
"Commercial Acumen",
"Develops High Performance Teams",
"Innovation and risk taking",
"Global Leadership",
"Industry Leader"
]};
var ctx = $("#allRadarData");
var config = {
type: 'radar',
data: chartata,
animationEasing: 'linear',
options: {
legend: {
fontSize: 10,
display: true,
itemWidth: 150,
position: 'bottom',
fullWidth: true,
labels: {
fontColor: 'rgb(0,0,0)',
boxWidth: 10,
padding: 20
},
},
tooltips: {
enabled: true
},
scale: {
ticks: {
fontSize: 15,
beginAtZero: true,
stepSize: 1,
max: 5
}
}
},
},
LineGraph = new Chart(ctx, config);
var colorArray = [
["#f44336", false],
["#E91E63", false],
["#9C27B0", false],
["#673AB7", false],
['#3F51B5', false],
["#607D8B", false]
];
for (var i in data) {
tmpscore=[];
tmpscore.push(data[i].score1);
tmpscore.push(data[i].score2);
tmpscore.push(data[i].score3);
tmpscore.push(data[i].score4);
tmpscore.push(data[i].score5);
tmpscore.push(data[i].score6);
tmpscore.push(data[i].score7);
tmpscore.push(data[i].score8);
var color, done = false;
while (!done) {
var test = colorArray[parseInt(Math.random() * 10)];
if (!test[1]) {
color = test[0];
colorArray[colorArray.indexOf(test)][1] = true;
done = !done;
}
}
newDataset = {
label: data[i].firstName+' '+data[i].lastName,
borderColor: color,
backgroundColor: "rgba(0,0,0,0)",
data: tmpscore,
};
config.data.datasets.push(newDataset);
}
LineGraph.update();
},
});
});
解决方案
To reverse how legend label clicking behaves, you can use the legend onClick
option to implement the new click logic. Here is an example below that will give you the desired behavior. Note, in this implementation if you click an already hidden label, it will unhide it, and hide all the others.
function(e, legendItem) {
var index = legendItem.datasetIndex;
var ci = this.chart;
var alreadyHidden = (ci.getDatasetMeta(index).hidden === null) ? false : ci.getDatasetMeta(index).hidden;
ci.data.datasets.forEach(function(e, i) {
var meta = ci.getDatasetMeta(i);
if (i !== index) {
if (!alreadyHidden) {
meta.hidden = meta.hidden === null ? !meta.hidden : null;
} else if (meta.hidden === null) {
meta.hidden = true;
}
} else if (i === index) {
meta.hidden = null;
}
});
ci.update();
};
Here is a working example as well.
If however, you want a more complex logic that will unhide a label that is currently hidden when at least one other label is currently visible, then you can use the below implementation.
function(e, legendItem) {
var index = legendItem.datasetIndex;
var ci = this.chart;
var alreadyHidden = (ci.getDatasetMeta(index).hidden === null) ? false : ci.getDatasetMeta(index).hidden;
var anyOthersAlreadyHidden = false;
var allOthersHidden = true;
// figure out the current state of the labels
ci.data.datasets.forEach(function(e, i) {
var meta = ci.getDatasetMeta(i);
if (i !== index) {
if (meta.hidden) {
anyOthersAlreadyHidden = true;
} else {
allOthersHidden = false;
}
}
});
// if the label we clicked is already hidden
// then we now want to unhide (with any others already unhidden)
if (alreadyHidden) {
ci.getDatasetMeta(index).hidden = null;
} else {
// otherwise, lets figure out how to toggle visibility based upon the current state
ci.data.datasets.forEach(function(e, i) {
var meta = ci.getDatasetMeta(i);
if (i !== index) {
// handles logic when we click on visible hidden label and there is currently at least
// one other label that is visible and at least one other label already hidden
// (we want to keep those already hidden still hidden)
if (anyOthersAlreadyHidden && !allOthersHidden) {
meta.hidden = true;
} else {
// toggle visibility
meta.hidden = meta.hidden === null ? !meta.hidden : null;
}
} else {
meta.hidden = null;
}
});
}
ci.update();
}
Here is a working example for this alternate implementation as well.
To use this in your specific code, just place it in your chart's legend config using the onClick
property.
var config = {
type: 'radar',
data: chartata,
animationEasing: 'linear',
options: {
legend: {
fontSize: 10,
display: true,
itemWidth: 150,
position: 'bottom',
fullWidth: true,
labels: {
fontColor: 'rgb(0,0,0)',
boxWidth: 10,
padding: 20
},
onClick: function(e, legendItem) {
var index = legendItem.datasetIndex;
var ci = this.chart;
var alreadyHidden = (ci.getDatasetMeta(index).hidden === null) ? false : ci.getDatasetMeta(index).hidden;
ci.data.datasets.forEach(function(e, i) {
var meta = ci.getDatasetMeta(i);
if (i !== index) {
if (!alreadyHidden) {
meta.hidden = meta.hidden === null ? !meta.hidden : null;
} else if (meta.hidden === null) {
meta.hidden = true;
}
} else if (i === index) {
meta.hidden = null;
}
});
ci.update();
},
},
tooltips: {
enabled: true
},
scale: {
ticks: {
fontSize: 15,
beginAtZero: true,
stepSize: 1,
max: 5
}
}
},
},
It was not clear what behavior you wanted the 'all' option to have, but you might be able to use the legend.labels.generateLabels
option to trick Chart.js to add an 'all' label (you will have to modify the above onClick
logic to deal with that thought.
However, I think a better solution would be to implement your own link or button outside of the chart.js canvas that would show/hide all datasets. Check out the Chart.js radar sample page to see how they bind buttons with chart actions.
相关文章