I have arbitrary data points with x/y coordinates that I want to draw on an HTML5 canvas. I would like to zoom the canvas context to fit the data points. How can I adjust the canvas scale and translation to fit a specific bounding box?
My searches for this topic show various hits using different libraries like d3.js, or geospatial databases, but I'd like a pure JavaScript/HTML5 solution. I can (have) hand-written combinations of ctx.scale()
and ctx.translate()
that via hand-tweaking can match a particular set of points, but I want a general solution that I can feed a bounding box (e.g. x,y,w,h) to and have the context transformed.
Here is a function that takes an HTML canvas context and a bounding box—specified as minX
, minY
, either maxX
/maxY
or width
/height
, and an optional padding
value—and calculates the scale and translation needed to make the canvas drawing area focus only on that box.
I've assumed that a symmetric scale is desired, with extra content shown on the sides or top/bottom, based on aspect ratio.
function zoomContext(ctx, bbox={minX:0, minY:0, maxX:10, maxY:20, padding:0}) {
let {minX:x, minY:y, maxX, maxY, width:w, height:h, padding} = bbox
w ||= maxX - x
h ||= maxY - y
if (padding) {
x -= padding
y -= padding
w += padding*2
h += padding*2
}
const cx = x + w / 2
cy = y + h / 2,
sx = ctx.canvas.width / w,
sy = ctx.canvas.height / h
const symmetricScale = Math.min(sx, sy)
ctx.resetTransform()
ctx.scale(symmetricScale, symmetricScale)
ctx.translate(-cx, -cy)
}
Before calling this you may want to ensure that the internal height and width of the canvas match its display size (in case you are scaling the canvas using CSS):
myCanvas.width = myCanvas.offsetWidth
myCanvas.height = myCanvas.offsetHeight