<h3>SVG path (red) versus canvas path (black) performance</h3>
<p>This test draws 100 - 5000 bars using both APIs and measures the inter-frame timings.</p>
<p>Results are shown on a linear axis, bar count increases left-to-right.</p>
<p>Solid triangle indicates testing in progress.</p>
<svg width="640" height="480">
<path/>
</svg>
<canvas width="640" height="480"></canvas>
body {
font-family: 'Open Sans', sans-serif;
font-size: 14px;
}
svg, canvas {
position: absolute;
top: 25px;
left: 25px;
opacity: 0.5;
}
canvas {
left: 30px;
}
path {
stroke: red;
}
const select = d3_selection.select;
const range = d3_array.range;
const extent = d3_array.extent;
const linear = d3_scale.scaleLinear;
const barGenerator = fc_shape.bar;
const render = (svgData, canvasData) =>
new Promise((resolve, reject) => {
const timings = {
count: svgData.length
};
const x = linear()
.range([10, 630])
.domain(extent(svgData, (d, i) => i));
const y = linear()
.range([470, 10])
.domain(extent(svgData, (d, i) => d));
const path = select('svg')
.selectAll('path')
.data([svgData]);
const svgBar = barGenerator()
.x((d, i) => x(i))
.y((d) => y(d))
.height((d) => y(0) - y(d))
.width(1)
.verticalAlign('top');
timings.startSvg = performance.now();
path.attr('d', svgBar);
requestAnimationFrame(() => {
timings.endSvg = performance.now();
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const canvasBar = barGenerator()
.context(ctx)
.x((d, i) => x(i))
.y((d) => y(d))
.height((d) => y(0) - y(d))
.width(1)
.verticalAlign('top');
timings.startCanvas = performance.now();
canvas.width = canvas.width;
canvasBar(canvasData);
ctx.stroke();
requestAnimationFrame(() => {
timings.endCanvas = performance.now();
resolve(timings);
});
});
});
let promise = Promise.resolve();
let results = [];
const recordResults = (results, timings) =>
[...results, {
count: timings.count,
canvas: timings.endCanvas - timings.startCanvas,
svg: timings.endSvg - timings.startSvg
}];
for (let i = 100; i <= 5000; i+=100) {
promise = promise.then((timings) => {
if (timings) {
results = recordResults(results, timings);
}
const data = range(0, i)
return render(data, data);
});
}
promise.then((timings) => {
results = recordResults(results, timings);
render(results.map((d) => d.svg), results.map((d) => d.canvas));
});
View Compiled