jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
Testing two different approaches to exporting multiple AmCharts charts as a single image.
<div id="omnidiv" style="width:100%;">
<div id="piediv" style="width:450px; height:400px;"></div>
<div id="linediv" style="width:450px; height:400px;"></div>
<div id="columndiv" style="width:450px; height:400px;"></div>
</div>
<script src="http://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="http://www.amcharts.com/lib/3/pie.js"></script>
<script src="http://www.amcharts.com/lib/3/serial.js"></script>
<script src="http://www.amcharts.com/lib/3/exporting/amexport.js"></script>
<script src="http://www.amcharts.com/lib/3/exporting/canvg.js"></script>
<script src="http://www.amcharts.com/lib/3/exporting/rgbcolor.js"></script>
<script src="http://www.amcharts.com/lib/3/exporting/jspdf.js"></script>
<script src="http://www.amcharts.com/lib/3/exporting/jspdf.plugin.addimage.js"></script>
window.saveAs = function() {};
var pieChart, lineChart, columnChart;
var exportConfig = {
menuTop: "21px",
menuBottom: "auto",
menuRight: "21px",
backgroundColor: "#efefef",
menuItemStyle: {
backgroundColor: '#EFEFEF',
rollOverBackgroundColor: '#DDDDDD'
},
menuItems: [{
textAlign: 'center',
icon: 'http://www.amcharts.com/lib/3/images/export.png',
title: 'export',
items: [{
title: 'JPG',
format: 'jpg'
}, {
title: 'PNG',
format: 'png'
}, {
title: 'SVG',
format: 'svg'
}, {
title: 'PDF',
format: 'pdf'
}]
}]
};
pieChart = AmCharts.makeChart("piediv", {
dataProvider: [{
category: 'A Slice',
value: 60
}, {
category: 'Another Slice',
value: 20
}, {
category: 'Not A Slice',
value: 20
}],
exportConfig: exportConfig,
titleField: "category",
type: "pie",
valueField: "value"
});
lineChart = AmCharts.makeChart("linediv", {
categoryField: "category",
dataProvider: [{
category: "x = 1",
value: 1
}, {
category: "x = 2",
value: 4
}, {
category: "x = 3",
value: 8
}, {
category: "x = 4",
value: 16
}],
exportConfig: exportConfig,
graphs: [{
title: "y = x^2",
type: "line",
valueField: "value"
}],
type: "serial"
});
columnChart = AmCharts.makeChart("columndiv", {
categoryField: "category",
dataProvider: [{
category: "2013-12-02",
value: 2
}, {
category: "2013-12-03",
value: 4
}, {
category: "2013-12-04",
value: 8
}, {
category: "2013-12-05",
value: 16
}],
exportConfig: exportConfig,
graphs: [{
title: "Things",
type: "column",
valueField: "value"
}],
type: "serial"
});
function exportMultiple(charts, format, fileName) {
var extension;
switch (format) {
case 'svg':
format = 'image/svg+xml';
case 'image/svg+xml':
extension = 'svg';
break;
case 'pdf':
format = 'application/pdf';
case 'application/pdf':
extension = 'pdf';
break;
case 'png':
format = 'image/png';
case 'image/png':
extension = 'png';
break;
case 'jpeg':
case 'jpg':
format = 'image/jpeg';
case 'image/jpeg':
extension = 'jpeg';
break;
}
var images = [];
var width = 0,
height = 0;
for (var i = 0; i < charts.length; i++) {
var exporter = charts[i].AmExport;
if (!exporter) {
continue;
}
exporter.output({
format: 'png',
output: 'datastring'
}, function(blob) {
var image = document.createElement("img");
image.src = blob;
images.push(image);
});
width += charts[i].divRealWidth;
height = charts[i].divRealHeight > height ? charts[i].divRealHeight : height;
}
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var context = canvas.getContext("2d");
var currentX = 0,
currentY = 0;
for (i = 0; i < images.length; i++) {
context.drawImage(images[i], currentX, currentY);
currentX += images[i].naturalWidth;
}
var data = canvas.toDataURL(format);
var tempExporter = new AmCharts.AmExport({
addListener: function() {}
});
var blob = tempExporter.generateBlob(data, format);
saveAs(blob, fileName + '.' + extension);
}
function generateOutput(cfg, callback) {
var _this = this,
charts = _this.charts,
svgs = [],
canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
offset = {
x: 0,
y: 0
},
tmp = {};
_this.processing.buffer = [];
_this.processing.drawn = 0;
_this.canvas = canvas;
var currentX = 0,
width = 0,
height = 0;
for (var i = 0; i < charts.length; i++) {
width += charts[i].divRealWidth;
height = charts[i].divRealHeight > height ? charts[i].divRealHeight : height;
var chartSvgs = charts[i].div.getElementsByTagName('svg');
for (var j = 0; j < chartSvgs.length; j++) {
var parentNode = chartSvgs[j].parentNode,
svgX = Number(parentNode.style.left.slice(0, -2)),
svgY = Number(parentNode.style.top.slice(0, -2));
tmp = AmCharts.extend({}, offset);
offset.x = svgX ? svgX : offset.x;
offset.y = svgY ? svgY : offset.y;
_this.processing.buffer.push([chartSvgs[j], AmCharts.extend({}, offset)]);
if (svgY && svgX) {
offset = tmp;
} else {
offset.y += svgY ? 0 : parentNode.offsetHeight;
}
svgs.push(chartSvgs[j]);
}
currentX += charts[i].divRealWidth;
offset = {
x: currentX,
y: 0
};
}
canvas.id = AmCharts.getUniqueId();
canvas.width = width;
canvas.height = height;
if (cfg.backgroundColor || format == 'image/jpeg') {
context.fillStyle = cfg.backgroundColor || '#FFFFFF';
context.fillRect(0, 0, canvas.width, canvas.height);
}
function drawItWhenItsLoaded() {
var img, buffer, offset, source;
if (_this.processing.buffer.length == _this.processing.drawn) {
return callback();
} else {
buffer = _this.processing.buffer[_this.processing.drawn];
source = new XMLSerializer().serializeToString(buffer[0]); //source = 'data:image/svg+xml;base64,' + btoa();
offset = buffer[1];
if (cfg.render == 'browser') {
img = new Image();
img.id = AmCharts.getUniqueId();
source = 'data:image/svg+xml;base64,' + btoa(source);
img.onload = function() {
context.drawImage(this, buffer[1].x, buffer[1].y);
_this.processing.drawn++;
drawItWhenItsLoaded();
};
img.onerror = function() {
context.drawImage(this, buffer[1].x, buffer[1].y);
_this.processing.drawn++;
drawItWhenItsLoaded();
};
img.src = source;
if (img.complete || typeof(img.complete) == 'undefined' || img.complete === undefined) {
img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
img.src = source;
}
} else if (cfg.render == 'canvg') {
canvg(canvas, source, {
offsetX: offset.x,
offsetY: offset.y,
ignoreMouse: true,
ignoreAnimation: true,
ignoreDimensions: true,
ignoreClear: true,
renderCallback: function() {
_this.processing.drawn++;
drawItWhenItsLoaded();
}
});
}
}
}
return drawItWhenItsLoaded();
}
function hopefullyFasterExportMultiple(charts, format) {
var exporter = new AmCharts.AmExport({
addListener: function() {}
});
var temp = exporter.generateOutput;
exporter.charts = charts;
exporter.generateOutput = generateOutput;
exporter.output(format);
}
Ready to run.
Test | Ops/sec | |
---|---|---|
Constructing a new canvas from the three charts' individual outputs |
| ready |
Rewriting the generateOutput function to take multiple charts |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.