I am drawing a scatter chart with NVD3 library in React when a component is first mounted. I hide and show the component depending on the buttons clicked. Each time the component appears, it is mounted, so it is re-drawn but I want to avoid the redrawing, doing some kind of caching of the chart since it takes quite a bit of time to draw the chart with many datapoints. I am calling createScatterChart in componentDidMount:
createScatterChart() {
const node = this.node
nv.utils.symbolMap.set('thin-x', function(size) {
size = Math.sqrt(size);
return 'M' + (-size/2) + ',' + (-size/2) +
'l' + size + ',' + size +
'm0,' + -(size) +
'l' + (-size) + ',' + size;
});
// create the chart
var chart;
nv.addGraph(function() {
chart = nv.models.scatterChart()
.showDistX(true)
.showDistY(true)
.useVoronoi(true)
.color(d3.scale.category10().range())
.duration(300)
;
var data_func = () => this.props.datum;
var data_obj = data_func();
var that = this;
chart.tooltip.contentGenerator(function (d) {
//var html = "<div>";
var html = "";
d.series.forEach(function(elem){
Object.keys(data_obj).forEach(function(key_1) {
var outer_obj = data_obj[key_1];
if (outer_obj["key"] === elem.key) {
that.showBarChart(elem.key);
html += "<p>cluster " + elem.key + "</p>";
/*var expr = outer_obj["values"][0]["expr"];
html += "<p>" + elem.key + "</p>";
html += "<p>x = " + d.value + ", y = " + elem.value + "</p>";*/
}
});
})
//html += "</div>";
return html;
});
chart.dispatch.on('renderEnd', function(){
console.log('render complete');
});
chart.xAxis.tickFormat(d3.format('.02f'));
chart.yAxis.tickFormat(d3.format('.02f'));
d3.select(node)
.datum(data_func)
.call(chart);
nv.utils.windowResize(chart.update);
chart.dispatch.on('stateChange', function(e) { ('New State:', JSON.stringify(e)); });
return chart;
}.bind(this));
}
The function ultimately returns chart, so could I somehow save it in a variable and then draw it much faster? Or what would you recommend for such caching of the chart?
The problem can be resolved by changing visible attribute of the html element instead of rendering it.
<div visibility={this.state.showButton ? "visible": "hidden"} ></div>
Related
The tooltip in a Highcharts Gantt show the start and end date of the hovered task, but I'm not able to translate the prefix used ("Start" and "End"):
There is no options in Highcharts.lang for these text.
That is caused by the default pointFormatter function:
if (!milestone) {
retVal += 'Start: ' + start + '<br/>';
retVal += 'End: ' + end + '<br/>';
} else {
retVal += start + '<br/>';
}
Source code: https://github.com/highcharts/highcharts/blob/master/ts/Series/Gantt/GanttSeries.ts#L116
As a solution you can implement your own pointFormatter, for example:
tooltip: {
pointFormatter: function() {
var point = this,
H = Highcharts,
series = point.series,
xAxis = series.xAxis,
startOfWeek = xAxis.options.startOfWeek,
formats = series.tooltipOptions.dateTimeLabelFormats,
tooltip = series.chart.tooltip,
ttOptions = series.tooltipOptions,
format = ttOptions.xDateFormat,
returnVal = '<b>' + (point.name || point.yCategory) + '</b>',
start,
end;
if (!format) {
format = H.splat(tooltip.getDateFormat(xAxis.closestPointRange, point.start, startOfWeek, formats))[0];
}
start = series.chart.time.dateFormat(format, point.start);
end = series.chart.time.dateFormat(format, point.end);
returnVal += '<br/>';
returnVal += 'A1: ' + start + '<br/>';
returnVal += 'A2: ' + end + '<br/>';
return returnVal;
}
}
Live demo: https://jsfiddle.net/BlackLabel/a839yLsd/
API Reference: https://api.highcharts.com/gantt/tooltip.pointFormatter
Imagine that we have a pie chart like the code bellow:
am4core.ready(function() {
// Create chart instance
chartReg[id] = am4core.create(id, am4charts.PieChart);
// Add data
chartReg[id].data = data;
chartReg[id].innerRadius = 60;
// Add and configure Series
var pieSeries = chartReg[id].series.push(new am4charts.PieSeries());
pieSeries.dataFields.value = "value";
pieSeries.dataFields.category = "key";
pieSeries.ticks.template.disabled = true;
pieSeries.alignLabels = false;
// Create custom legend
chartReg[id].events.on("ready", function(event) {
// populate our custom legend when chart renders
chartReg[id].customLegend = $('#legend');
pieSeries.dataItems.each(function(row, i) {
var color = pieSeries.colors.getIndex(i);
var percent = Math.round(row.values.value.percent * 100) / 100;
var value = row.value;
var title = row.category
legend.innerHTML += '<div class="legend-item" id="legend-item-' + i + '" onclick="toggleSlice(' + i + ');" onmouseover="hoverSlice(' + i + ');" onmouseout="blurSlice(' + i + ');"><div class="legend-marker" style="background: ' + color + '"></div><div class="legend-title">' + title + '</div><div class="legend-value">' + value + ' | ' + percent + '%</div></div>';
});
});
});
The custom legends work fine like bellow:
But if we have multiple pie charts that get rendered in the DOM at the same time, the legends don't show up!
❤❤ Thank you for reading my question. ❤❤
I found the answer. Insted of:
legend.innerHTML += '<div>...</div>';
We should use:
$('#legend_'+id).append('<div>...</div>');
that dynamically adds the legends to the related div ;)
I am trying use below code (found from a forum) as JavaScript initialization code in Oracle APEX Donut chart to display total value in middle. But the result showing up only the Text "Total" in middle of the chart and does not show any numerical value. Can anyone help me out in spotting the error from the below code ? I am new to Javascript and have very less knowledge about the same.
function( options ){
var total;
this.pieSliceLabel = function(dataContext){
var series_name;
percent = Math.round(dataContext.value/dataContext.totalValue*100);
total = Math.round(dataContext.totalValue);
if ( dataContext.seriesData ) {
series_name = dataContext.seriesData.name;
} else {
series_name = 'Other';
}
document.getElementById("myDiv").innerHTML = Math.round(dataContext.totalValue);
return series_name + " " + percent + "% ( " + dataContext.value + " )";
}
// Set chart initialization options
options.dataLabel = pieSliceLabel;
this.centerCallback = function(dataContext){
var pieElem = document.createElement('div');
pieElem.innerHTML =
'<div id="myDiv" style="position:absolute;text-align:center;font-size:16px;">' +
'Total' +' '+ total +
'</div>';
var outerDiv = pieElem.children[0];
var innerBounds = dataContext.innerBounds;
// Outer bounds
outerDiv.style.top = innerBounds.y + "px";
outerDiv.style.left = innerBounds.x + "px";
outerDiv.style.height = innerBounds.height + "px";
outerDiv.style.width = innerBounds.width + "px";
outerDiv.style.lineHeight = innerBounds.height + "px";
return pieElem;
}
options.pieCenter = {
renderer : centerCallback
}
return options;
}
if I correct understood, try to fix it, add to centerCallback,
var value = dataContext.totalValue;
pieElem.innerHTML =
'<div id="myDiv" style="position:absolute;text-align:center;font-size:16px;">' +
'Total ' + value +
'</div>';
Could you try this
function( options ){
// will hold the total value, must be global variable
var total;
Add below statement in this.pieSliceLabel = function(dataContext){}
percent = Math.round(dataContext.value/dataContext.totalValue*100);
total = dataContext.totalValue; //assign to global variable
Update the below innerHTML code in this.centerCallback = function(dataContext){}
//updated element
pieElem.innerHTML =
'<div id="myDiv" style="position:absolute;text-align:center;font-size:16px;">' +
'Total' + total +
'</div>';
The problem I have is that all the json we pass over come from python, and we can't send over javascript in such things. For instance', I need to include the tech from this question but obviously in the plotOptions. I'm just not sure how to do things like reference series, etc. So an example with the above series would be great. I tried the following but it didn't work since this is not what I expected it to be.
options.plotOptions = options.plotOptions || {};
options.plotOptions.series = options.plotOptions.series || {};
options.plotOptions.series.point =
options.plotOptions.series.point || {};
options.plotOptions.series.point.events =
options.plotOptions.series.point.events || {};
options.plotOptions.tooltip = {formatter: function() {
var text = '';
if(this.series.name == 'MSFT') {
text = this.x + ': ' + this.series.name +
'<br> $' + Highcharts.numberFormat(this.y, 0);
} else {
text = 'In ' + this.x + ' the median value was' + this.median +
'and the total $' + Highcharts.numberFormat(this.y, 0);
}
return text;
}
options.plotOptions.series.point.events.click = function() {
if (this.options.url){
window.open(this.options.url);
}
}
I can create a HeatMap using d3.js, dc.js and crossfilter, using data in a CSV file.
code:
var chart = dc.heatMap("#test");
d3.csv("morley.csv", function(error, experiments) {
var ndx = crossfilter(experiments),
runDim = ndx.dimension(function(d) { return [+d.Run, +d.Expt]; }),
runGroup = runDim.group().reduceSum(function(d) { return +d.Speed; });
chart
.width(45 * 20 + 80)
.height(45 * 5 + 40)
.dimension(runDim)
.group(runGroup)
.keyAccessor(function(d) { return +d.key[0]; })
.valueAccessor(function(d) { return +d.key[1]; })
.colorAccessor(function(d) { return +d.value; })
.title(function(d) {
return "Run: " + d.key[0] + "\n" +
"Expt: " + d.key[1] + "\n" +
"Speed: " + (299000 + d.value) + " km/s";})
.colors(["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"])
.calculateColorDomain();
chart.render();
});
but i want to do this same thing using JSON data, perhaps an array with some json data. This seems like a noob question but I was unable to find any example which uses JSON data for heatmap.
If you're using a JSON file then the code is going to be largely the same just replace d3.csv with d3.json.
If the data is already loaded in the browser then you can remove this function and just run:
var experiments = JSON.parse(json_object) //if json_object is still a string
var ndx = crossfilter(experiments)
and the rest of the code will be the same.