0

Im making some thing that is described in the title but I cant make the canvas responsive. All I want to do is make a canvas with ratio of 16:9 and with width set to 100% and height calculated by jQuery to keep the same ratio, so when a person on a 4:3 LCD is drawing with the ONE in 16:9, the lines wont appear stretched. However it is not working properly. If I use this code:

<canvas id="canvas" width="1600" height="900">
</canvas>

$('#canvas').css({
        width: $(window).width(),
        height: $(window).width() * 9 / 16
   });

It does not work as it bugs out while drawing.

The second thing I tried is:

$('#canvas').attr('width', $(window).width()).attr('height', $(window).width() * 9 / 16);

It just crops the drawing on smaller screens.

How can I make this work?

mrf1freak
  • 71
  • 2
  • 8
  • 1
    Possible duplicate of [Canvas width and height in HTML5](http://stackoverflow.com/questions/4938346/canvas-width-and-height-in-html5) – Fionna Nov 17 '15 at 13:18
  • @FionnaChan My question is completely different. He is asking about the difference between the attributes: width &height and the CSS width & height. I'm talking about making the canvas responsive. – mrf1freak Nov 17 '15 at 13:33
  • @mrf1freak , this sounds like a duplicate for me too. Your issue is while drawing on a canvas stretched by CSS. The solution is to not use CSS to set canvas's width and height. Or, what you could do is to wrap a larger canvas in a responsive container, this way, your users on smaller screens will have scrollbars to navigate the whole canvas. and everyone will be drawing on the same size. – Kaiido Nov 17 '15 at 13:49
  • @Kaiido I don't know why you think that his question and mine are even remotely similar. I suppose we both mention canvas in our questions so my question obviously and automatically a duplicate. And I don't need a container for canvas. I can just make it 1600x9000 and the person with 1280x1024 monitor will automatically get scrollbars, but that's not what I want as I mentioned in the question witch you didn't bother to read. – mrf1freak Nov 17 '15 at 15:29
  • @mrf1freak is this something you are after https://jsfiddle.net/g4n0u5to/1/ ? – Canvas Nov 17 '15 at 17:31
  • @Kaiido. When the questioner says something rude like *"... witch[sic] you didn't bother to read"* then I don't even want to try to help them. – markE Nov 17 '15 at 21:08
  • @mrf1freak. You're ignoring some good advice from both Fionna Chan and Kaiido regarding your misuse of CSS to resize html5 canvases. – markE Nov 17 '15 at 21:15
  • @markE I flew away from this question on my Nimbus 2000 when I read this comment :P But now you're getting my attention back on it and you seem to have though the last comment was mine... (I know how to make a 16 / 9 canvas)... I'm kind of offended. – Kaiido Nov 18 '15 at 01:33
  • @Kaiido. Upgrade to a Nimbus 2015! ;-). I was saying that I thought the OP was a bit rude towards you when you were just trying to be helpful. Yep, I suspect you can make a 16/9 canvas easily. :-) – markE Nov 18 '15 at 01:42

1 Answers1

1

Since you don't seem to accept the duplicate, here is an explanation just for you :

Canvas has its own coordinate matrix
This implies that the x and y values you do use in the drawing methods will be dependent on this matrix, not the one of your view-page.

The following example will show you that for two exact same canvases, the result will look different on screen because of the CSS you applied on them :

var ctx = normal.getContext('2d'),
    ctx2 = CSS_stretched.getContext('2d');
ctx.textAlign = ctx2.textAlign = 'center';
ctx.textBaseline = ctx2.textBaseline ='bottom';
for(var i=0; i<26; i++){
  ctx.fillRect(i*20, 0, 1, 10);
  ctx2.fillRect(i*20, 0, 1, 10);
  ctx.fillText(i*20, i*20, 25);
  ctx2.fillText(i*20, i*20, 25);
  }
#CSS_stretched{width : 50%;}
canvas{border: 1px solid black;}
p,canvas{display: block}
<p>Unstretched canvas:</p>
<canvas id="normal"  width="500" height ="25"></canvas>
<p>Stretched canvas :</p>
<canvas id="CSS_stretched" width="500" height ="25"></canvas>

This becomes a real pain when you try to use some functionnalities such as mouse coordinates to paint on your canvas, since the event coordinates are based on the page ones :

var ctx = canvas.getContext('2d');
canvas.onmousemove = function(e){
    var x = e.clientX - this.offsetLeft;
    var y = e.clientY - this.offsetTop;
    ctx.clearRect(0,0,canvas.width, canvas.height)
    ctx.fillRect(x-1, y-20, 2, 40)
    ctx.fillRect(x-20, y-1, 40, 2)
    }

stretch.onclick = function(){
    canvas.className = canvas.className?'':'stretched';
  }
canvas{border: 1px solid black;}
.stretched{width: 100%;}
<button id="stretch">toggle CSS stretching</button><br>
<canvas id="canvas" width="250" height="135" class="stretched"></canvas>

CSS stretching implies antialiasing algorithm
It also does provide an image-rendering property, but I don't think it will do what you want...

When you tell the browser to render at a smaller scale, you are telling him that the pixels are smaller than a pixel. It then has to create false semi-transparent ones around the original pixel location. This will also create some artifacts and the impression of a crappy rendering.

You can already see this effect in the first snippet :antialiasing effect
Note that even on non-scaled canvas, browsers already apply antialiasing, but crapped crap is just crappier.

So, now that I hope you've got the why "it bugs out while drawing", we can come to the solutions :

Don't use CSS to set your canvas size. Instead, set the canvas .widthand .height properties. That's the easiest and the only solution I can recommend.

Of course, in your own case it does imply other problems that you already encountered :
If you set the canvas size to the page's one, small screens will have cropped canvases.

One solution to this is to use a container, that will allow scroll-bars to appear only for the canvas.

You could also fix everyone's canvas to the smallest one, but if you're also targeting mobiles, then it will looks odd on larger screens.

Or, you will have to make all the calculations to convert the canvases coordinates to relative ones (strokes included) and if you do write it, I'd be pleased to see the code.

But this is a design problem that only you can answer. different solutions mockup

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • 1
    Upvote for a well detailed answer...and, BTW, you can calculate the scaling factor required to make "the smallest win" like this: `var scalingFactor = Math.min( (desiredWidth/originalWidth), (desiredHeight/originalHeight) );` Then after you've resized `canvas.width=desiredWidth` & `canvas.height=desiredHeight` you can tell canvas to automatically scale its drawing with `context.scale( scalingFactor, scalingFactor)`. That way you don't have to change your drawing coordinates at all. As you've mentioned, anytime you do scaling you might (or might not) end up with some visible deterioration. – markE Nov 18 '15 at 05:55
  • Thank You for finally understanding. I think I will work with the fixed size canvas for now on. Seriously, thank you for the detailed explanation. – mrf1freak Nov 19 '15 at 11:52
  • @Kaiido Here is the code if you wanna check it out. Sadly, not responsive: https://agario-clone-mrf1freak.c9users.io/ – mrf1freak Nov 29 '15 at 09:32
  • @mrf1freak you could wrap only your canvas into a container, to avoid all your html to also have scroll-bars. You could even play with it, by drawing a smaller copy (resized with drawImage() options) of the whole canvas in a corner and let a magnifier thing let your user know and move where they are on the whole drawing. – Kaiido Nov 29 '15 at 09:46