Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas state lost after changing size

I want to resize a canvas, but when I do, the context gets reset, losing the current fillStyle, transformation matrix, etc. The ctx.save() and ctx.restore() functions didn’t work as I initially expected:

function resizeCanvas(newWidth, newHeight) {
    ctx.save();
    canvas.width = newWidth; // Resets context, including the save state
    canvas.height = newHeight;
    ctx.restore(); // context save stack is empty, this does nothing
}

After researching for a while, I can’t seem to find a good way to save and restore the canvas context after resizing. The only way I can think of is manually saving each property, which seems both messy and slow. ctx.save() doesn’t return the saved state either, and I don’t know of a way to access the context’s stack.

Is there a better way, or am I doomed to use something like this:

function resizeCanvas(newWidth, newHeight) {
    let fillStyle = ctx.fillStyle;
    let strokeStyle = ctx.strokeStyle;
    let globalAlpha= ctx.globalAlpha;
    let lineWidth = ctx.lineWidth;
    // ...

    canvas.width = newWidth;
    canvas.height = newHeight;

    ctx.fillStyle = fillStyle;
    ctx.strokeStyle = strokeStyle;
    ctx.globalAlpha= globalAlpha;
    ctx.lineWidth = lineWidth;
    // ...
}
like image 220
Isaac Chen Avatar asked Dec 31 '17 18:12

Isaac Chen


1 Answers

The accepted answer no longer works because some properties are deprecated and hence give runtime error when trying to set deprecated properties.
Here's a fix for Khauri MacClain's answer.

function save(ctx){
    let props = ['strokeStyle', 'fillStyle', 'globalAlpha', 'lineWidth', 
    'lineCap', 'lineJoin', 'miterLimit', 'lineDashOffset', 'shadowOffsetX',
    'shadowOffsetY', 'shadowBlur', 'shadowColor', 'globalCompositeOperation', 
    'font', 'textAlign', 'textBaseline', 'direction', 'imageSmoothingEnabled'];
    let state = {}
    for(let prop of props){
      state[prop] = ctx[prop];
    }
    return state;
}

function restore(ctx, state){
    for(let prop in state){
      ctx[prop] = state[prop];
    }
}

function resize(ctx, width, height){
    let state = save(ctx);
    ctx.canvas.width = width || canvas.width;
    ctx.canvas.height = height || canvas.height;
    restore(ctx, state);
}
like image 123
sziraqui Avatar answered Sep 28 '22 05:09

sziraqui