I am trying to store a canvas reference in a global object and then apply that reference to an element instead of regenerating the canvas. here is my existing code. I hope that makes sense. thanks in advance!
waveformCache is assumed to be a global
var cL = document.getElementById('track' + trackId + 'WaveformL');
var cR = document.getElementById('track' + trackId + 'WaveformR');
if (waveformCache.hasOwnProperty(track.path))
{
var waveformCacheItem = waveformCache[track.path];
if (waveformCacheItem.hasOwnProperty('left'))
{
// restore canvas data here to cL element
}
}
else
{
waveformCache[track.path] = {};
var left = track.data.getChannelData(0);
var ctx1 = cL.getContext('2d');
ctx1.save();
ctx1.strokeStyle = 'rgb(49,73,11)';
ctx1.translate(0, 55/2); //centers where the line drawing starts horizontally
for(var i = 0; i < left.length; i += 200) {
var x1 = Math.floor(track.waveformLength * i / left.length); //first parameter affects the length of the drawn waveform #ZOOM
var y1 = left[i] * 55/2;
ctx1.beginPath();
ctx1.moveTo(x1, 0);
ctx1.lineTo(x1 + 1, y1);
ctx1.stroke();
}
ctx1.restore();
waveformCache[track.path].left = ctx1;
}
You can use a
Path2D
object to store your paths commands at. Then store the path in your global object. When you need to re-apply the path, simply stroke or fill using the stored path object.For example:
Later when you need to recall the path:
(A bonus is that you can initialize it using SVG paths. This means you can just define your path using SVG commands and store that as a single string. Reapply using the route of Path2D at a slight cost of performance when initializing.)
Path2D
can be polyfilled for browsers which do not yet support it (see notes for special cases).An outline of how to serialize an html5 canvas CanvasRendingContext2D
The canvas context (CanvasRendingContext2D ) holds the canvas' properties (styling, current transformation, etc).
Important! The context does not hold all the executed drawing commands that created the canvas content. Context Properties:
Coloring: strokeStyle, fillStyle(1), globalAlpha,
Line styles: lineWidth, lineCap, lineJoin, miterLimit,
Text Styles: font, textAlign, textBaseline,
Compositing: globalCompositeOperation,
Shadowing: shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY
(1)
fillStyle
is usually a string ('#ff0000'), but it can alternatively hold a reference to a gradient object or pattern object. To save the context's fillStyle, you will have to either ignore gradients / patterns or also serialize the gradient / pattern properties.Here's how to save context properties into an object
Here's how to copy saved context properties into a new context:
Re-executing the drawings
If you want to re-execute all the drawings commands, you must save the commands and their arguments.
From your example code, it looks like your drawings involve line segments(
moveTo
&lineTo
) so you can save each segment as a segment-object in an array of segment-objects.And then you can "replay" the line-segment drawing commands after you've reset all the context properties:
You can also serialize and replay all the common drawing commands (arc, beginPath, bezierCurveTo , clearRect, clip, closePath, fill, fillRect, fillText, lineTo, moveTo, quadraticCurveTo, rect, restore, rotate, save, scale, setTransform, stroke, strokeRect, strokeText, transform, translate). Save each command name & associated arguments in an object and save all those command-objects in an array.
These commands return values so you will need to do more work to handle them: measureText, getImageData (putImageData), toDataURL, isPointInPath, isPointInStroke, createImageData, createLinearGradient, createRadialGradient, createPattern. Luckily, these commands are used are used less often than the more common (simpler) commands.
About portability
If you use this method of saving all properties & drawing commands into object arrays, you can easily serialize them all into JSON strings with
JSON.stringify
and you can easily deserialize them back into object arrays withJSON.parse
.Having your canvas properties & drawing commands serialized to strings means that you can easily transport them to a server for storage and then fetch them for replaying.