How to use a relative coordinate system in HTML canvas? - Stack Overflow

I am used to working with SVGs. When you create a SVG element in a webpage, you can specify its viewbox

I am used to working with SVGs. When you create a SVG element in a webpage, you can specify its viewbox by stating the minimum X,Y coordinate and the maximum X,Y coordinate. Eg: (0 0 100 100)

That way, the SVG container will responsively adapt to the page window size and you can still draw shapes according to that original coordinate system (0-100) without worrying about the relative screen size. So if you draw a rectangle at coordinate (50,50) you can be guaranteed that the rectangle will always appear in the center of the container.

But apparently the HTML canvas does not have an equivalent of a viewbox. It only takes in coordinates in pixels? This makes it very difficult to work in instances where you need a canvas to resize(adapt) to the screen size, because all your drawings will get displaced.

What's the solution to this?

I am used to working with SVGs. When you create a SVG element in a webpage, you can specify its viewbox by stating the minimum X,Y coordinate and the maximum X,Y coordinate. Eg: (0 0 100 100)

That way, the SVG container will responsively adapt to the page window size and you can still draw shapes according to that original coordinate system (0-100) without worrying about the relative screen size. So if you draw a rectangle at coordinate (50,50) you can be guaranteed that the rectangle will always appear in the center of the container.

But apparently the HTML canvas does not have an equivalent of a viewbox. It only takes in coordinates in pixels? This makes it very difficult to work in instances where you need a canvas to resize(adapt) to the screen size, because all your drawings will get displaced.

What's the solution to this?

Share Improve this question asked Mar 22 at 17:10 JackieChilesTheSecondJackieChilesTheSecond 1012 bronze badges 4
  • It's application-specific: you'll need to scale all the values in your rendered canvas to account for the arbitrary sizes and redraw accordingly. Canvas is raster (like a photograph), not vector (like an SVG). – ggorlen Commented Mar 22 at 18:02
  • @ggorlen, but how do I even know what pixel coordinates to use when drawing a shape, if I don't know the dimensions of the screen? – JackieChilesTheSecond Commented Mar 22 at 18:54
  • You can get the dimensions of the screen with innerWidth, innerHeight and other browser/DOM APIs. – ggorlen Commented Mar 22 at 19:44
  • Point [0,0] is bottom left, but using SVG it's top left. – NVRM Commented Mar 24 at 2:30
Add a comment  | 

2 Answers 2

Reset to default 3

Transform matrix

When rendering to the 2D canvas context all coordinates are first transformed by the current transformation matrix.

You can manipulate the matrix with the following calls.

  • ctx.getTransform
  • ctx.setTransform overwrites existing transform.
  • ctx.transform *
  • ctx.scale *
  • ctx.rotate *
  • ctx.translate *
  • ctx.resetTransform same as ctx.setTransform(1,0,0,1,0,0) sets identity matrix.

Note Functions marked with * multiply the existing matrix with a new matrix. IE if you call from default ctx.scale(2, 2); ctx.scale(2, 2). The resulting matrix will scale by 4, 4.

You can set a view box using the following function

function setViewBox(ctx, left, top, right, bottom) {
    const viewXScale = ctx.canvas.width / (right - left);
    const viewYScale = ctx.canvas.height / (bottom - top);
    ctx.setTransform(
        viewXScale, 0,    // X Axis dir & scale in pixels
        0, viewYScale,    // Y Axis dir & scale in pixels
        -left * viewXScale, -top * viewYScale // origin in pixels
    );
}

Use as followed...

setViewBox(ctx, -50, -50, 50, 50);
ctx.arc(0, 0, 49, 0,  Math.PI * 2);  // arc in center of canvas

This ensures that ctx.lineWidth and fonts are correctly scaled as well.

Note that a square aspect may not be maintained.

You can modify the transform to fit the view box within the canvas or fill the canvas with the view box.

In fact canvas and svg have a quite similar concept when it comes to defining the drawing area.

  • you need to specify a "canvas/viewBox" size - otherwise you get the fallback 300x150px
  • the specified "canvas/viewBox" size doesn't necessarily express the actual layout size – affected by CSS rules. But we need to be careful about not changing the aspect ratios
  • we can't derive intrinsic dimensions from the "content" – as we can do for raster images which have a perfectly determined size due to their pixel data

A common practice would be to add width and height attributes to your canvas (or set these in your drawing function). These attributes would serve a similar purpose as a SVG viewBox – you define an "artboard" size and most importantly an aspect-ratio.

let cx=100, cy=50, r=25, fill='green'
let canvas = document.getElementById('canvas')
let canvasAdaptiveRes = document.getElementById('canvasAdaptiveRes')
let canvasHires = document.getElementById('canvasHires')
let svg = document.getElementById('svg')
let circle = document.getElementById('circle')

// draw on canvas
drawCircle(canvas, cx, cy, r, fill);
drawCircleAdaptiveRes(canvasAdaptiveRes, cx, cy, r, fill);
drawCircleHires(canvasHires, cx, cy, r, fill);

// sync SVG
circle.setAttribute('cx', cx)
circle.setAttribute('cy', cy)
circle.setAttribute('r', r)
circle.setAttribute('fill', fill)


function drawCircle(canvas, cx, cy, r, fill){
  const ctx = canvas.getContext("2d");
  ctx.beginPath();
  ctx.arc(cx, cy, r, 0, 2 * Math.PI, false);
  ctx.fillStyle = fill;
  ctx.fill();

}


function drawCircleHires(canvas, cx, cy, r, fill){
  
  // get device resolution
  let maxWidth = window.screen.availWidth
  let maxHeight = window.screen.availHeight
  
  //get scale by maximum device pixel length
  let maxDim = Math.max(maxWidth, maxHeight)
  scale  = maxDim/canvas.width
  
  // adjust canvas size for rendering context
  canvas.width *=scale;
  canvas.height *=scale;
  
  const ctx = canvas.getContext("2d");
  ctx.beginPath();
  ctx.arc(cx*scale, cy*scale, r*scale, 0, 2 * Math.PI, false);
  ctx.fillStyle = fill;
  ctx.fill();

}

function drawCircleAdaptiveRes(canvas, cx, cy, r, fill){
  
  // calculate scaling factor according to rendered bounding box
  let {width, height} = canvas.getBoundingClientRect();
  scale  = width/canvas.width
  
  // adjust canvas size for rendering context
  canvas.width *=scale;
  canvas.height *=scale;
  
  const ctx = canvas.getContext("2d");
  ctx.beginPath();
  ctx.arc(cx*scale, cy*scale, r*scale, 0, 2 * Math.PI, false);
  ctx.fillStyle = fill;
  ctx.fill();

}
canvas,
svg {
  display: block;
  width:100%;
  outline: 1px solid #ccc;
  margin-bottom:1em;
}
<h3>SVG</h3>
<svg id="svg" viewBox="0 0 100 50">
  <circle id="circle" cx="100" cy="50" r="25" />
</svg>

<h3>Canvas - fixed resolution</h3>
<canvas id="canvas" width="100" height="50"></canvas>

<h3>Canvas - resolution based on rendered size</h3>
<canvas id="canvasAdaptiveRes" width="100" height="50"></canvas>

<h3>Canvas - resolution based on max device resolution</h3>
<canvas id="canvasHires" width="100" height="50"></canvas>

  

The above snippet illustrates:

  • "Canvas - fixed resolution" – your canvas attribute size is not tied to the actual rendering size. Albeit, the rendering becomes blurry
  • "Canvas - resolution based on rendered size" calculates a scaling factor based on the current rendering size. This ensures a crisp rendering on load. However you would need to update the rendering on resize to get the best rendering (may also become blurry)
  • "Canvas - resolution based on max device resolution" – sets a scaling factor for a hires rendering based on the maximum device resolution. While the rendering won't get blurry on resize it may also be overly crisp (jagged edges). However this problem may be a good compromise as you don't need to add any resize events or observers to update the rendering. Also, the overly crisp edges are not noticeable on hi-pixel-density screens.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744306568a4567741.html

相关推荐

  • How to use a relative coordinate system in HTML canvas? - Stack Overflow

    I am used to working with SVGs. When you create a SVG element in a webpage, you can specify its viewbox

    7天前
    40

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信