This will fit and scale to the canvas. Note that there is no padding, but thats easy to add
Firstly, you need to setup something to track transforms.
this.trackTransforms = function (ctx) {
var svg = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
var xform = svg.createSVGMatrix();
ctx.getTransform = function () { return xform; };
var savedTransforms = [];
var save = ctx.save;
ctx.save = function () {
savedTransforms.push(xform.translate(0, 0));
return save.call(ctx);
};
var restore = ctx.restore;
ctx.restore = function () {
xform = savedTransforms.pop();
return restore.call(ctx);
};
var scale = ctx.scale;
ctx.scale = function (sx, sy) {
xform = xform.scaleNonUniform(sx, sy);
return scale.call(ctx, sx, sy);
};
var rotate = ctx.rotate;
ctx.rotate = function (radians) {
xform = xform.rotate(radians * 180 / Math.PI);
return rotate.call(ctx, radians);
};
var translate = ctx.translate;
ctx.translate = function (dx, dy) {
xform = xform.translate(dx, dy);
return translate.call(ctx, dx, dy);
};
var transform = ctx.transform;
ctx.transform = function (a, b, c, d, e, f) {
var m2 = svg.createSVGMatrix();
m2.a = a; m2.b = b; m2.c = c; m2.d = d; m2.e = e; m2.f = f;
xform = xform.multiply(m2);
return transform.call(ctx, a, b, c, d, e, f);
};
var setTransform = ctx.setTransform;
ctx.setTransform = function (a, b, c, d, e, f) {
xform.a = a;
xform.b = b;
xform.c = c;
xform.d = d;
xform.e = e;
xform.f = f;
return setTransform.call(ctx, a, b, c, d, e, f);
};
var pt = svg.createSVGPoint();
ctx.transformedPoint = function (x, y) {
pt.x = x; pt.y = y;
return pt.matrixTransform(xform.inverse());
}
}
Next you need to be able to actually fit the contents. My function assumes you know the absolute width and height of the contents, you'll have to figure this out yourself but should be fairly easy. Note that if something starts at -150 and goes to +150, its width is 300.
this.fitToContents = function (widthOfContents, heightOfContents) {
var p1 = this.context.transformedPoint(0, 0);
var p2 = this.context.transformedPoint(this.canvas.width, this.canvas.height);
var centerX = (p2.x - p1.x) / 2;
var centerY = (p2.y - p1.y) / 2;
centerX -= widthOfContents / 2;
centerY -= heightOfContents / 2;
this.context.translate(centerX, centerY);
var lastX = this.canvas.width / 2,
lastY = this.canvas.height / 2;
var scaleFactorX = this.canvas.width / widthOfContents;
var scaleFactorY = this.canvas.height / heightOfContents;
var scaleFactorToUse = Math.abs(scaleFactorX) < Math.abs(scaleFactorY) ? scaleFactorX : scaleFactorY;
var pt = this.context.transformedPoint(lastX, lastY);
this.context.translate(pt.x, pt.y);
this.context.scale(scaleFactorToUse, scaleFactorToUse);
this.context.translate(-pt.x, -pt.y);
}
Putting it together itll look something like this:
this.init = function (elementId) {
this.canvas = document.getElementById(elementId);
this.context = this.canvas.getContext('2d');
this.trackTransforms(this.context);
this.context.canvas.width = this.canvas.offsetWidth;
this.context.canvas.height = this.canvas.offsetHeight;
this.draw();
}
this.draw = function () {
this.fitToContents(300,300);
// you can now draw other stuff
// you can probably only fit to contents once,
// in the init function, if their positions don't change
this.drawStuff();
}