html5 canvas scaling without specifying dimensions - html

I've just started working with the html5 canvas element.
I'm using the latest firefox and chromium browsers. And so far, they're
responding alike.
What I'm trying to achieve is scaling of an image without having to
specify the canvas or image drawing sizes. I'd like the canvas to fill
the browser window, and for the image to fill the canvas without
specifying any sizes. And to readjust canvas and its image on the
fly when the user adjusts the browser's frame.
The mansion pic that I'm testing with is 4284x2844.
I've managed to achieve dynamic scaling, but there's a problem...
if I don't specify sizes the image becomes blurry.
This is my first stackoverflow question and I haven't conquered the
formatting. So, please take a look at the small amount of code over
at pastebin:
http://pastebin.com/88faqJUx
Thank you for your help.
I found the solution...
Adding two lines, with no other changes, did the trick, though at this point I'm not exactly sure
why it was originally failing, but thoroughly happy to move on...
<canvas id="taba_main_canvas">
Your browser does not support the canvas element.
</ canvas>
<script type="text/javascript">
var main_canvas=document.getElementById("taba_main_canvas");
var cxt=main_canvas.getContext("2d");
// adding these next two lines solved the blurriness issues
//Set the canvas width to the same as the browser
main_canvas.width = window.innerWidth;
main_canvas.height = window.innerHeight;
var img=new Image();
<!-- mansion pic 4284x2844 -->
img.src="images/mansion_3344.png";
img.onload = function()
{
<!-- use the graphics full size and scale the canvas in css -->
cxt.drawImage(img,0,0,main_canvas.width,main_canvas.height);
}
</script>
Just one tiny little problem, the vertical size of the image is apparently just a few lines taller
than the canvas and so I get a vertival scrollbar. Dragging the browser window taller, which normally
would eliminate the vertical scrollbar has no effect. I've tried manipulating the canvas or image height
in the code, but that didn't change anything.
Still, having the image look clean is a big win. I'm moving on for the moment and will revisit this
later.

The other way to do this is to latch on to the document onresize event and resize the canvas by using window.innerWidth and window.innerHeight or some such thing. I've used it that way myself, but that was for something which I didn't care about IE support - see W3C DOM Compatibility - CSS Object Model View at quirksmode for info about browser support. Note also that the scrollbar width is included in innerWidth and innerHeight; if your page may need scrolling, you may wish to do something like subtract 20 and pad the containing element with a suitable background colour.
I presume that you're not just trying to draw an image - if you were just doing that, <img> would be a much better match.
Edit: jQuery has $(document).width(); and $(document).height(); which seem to get the right figures. Another edit: actually they're wrong; they're the document width and height, not viewport width and height, so I think innerWidth and innerHeight may be all there is.

Related

HTML canvas best practices for resizing

Doing a simple 2d physics engine with HTML5 Canvas (collisions,graphing). I want a full screen canvas with a header navbar. How can I create this layout and handle resizing correctly?
I have tried several solutions:
One involved programatically resizing the canvas to fill its container onload() and onresize(). Canvas contents stay the same.
Another involved a responsive canvas with percents whose contents shrunk as the canvas shrunk.
Can anyone help lead us to the holy grail? The most important question is your opinion about canvas resizing best practices (should we do it?). If so, what about the debate between resizing the canvas pixels and media queries vs flex/percents vs javascript container measuring, etc.
Example/Attempts:
Example-1:
Here is the Javascript code which I used in v1 of my mock up. The corresponding HTML was just a basic document with a header and a 100% container with the canvas inside the container and being set to fill the container.
window.onload = function(){
init();
};
window.addEventListener("resize", init);
function init(){
console.log("init");
initCanvas();
drawCircle();
}
function initCanvas() {
var content = document.querySelector(".content");
var canvas = document.querySelector(".myCanvas");
canvas.width = content.clientWidth;
canvas.height = content.clientHeight;
}
Example-2:
This CodePen is an example of the resizing canvas that I made. It still retreats up under the navbar during extreme resizing though.
Resizing
It will depend on how you are rendering.
requestAnimationFrame is best practice.
Generally best practice to make any changes to the DOM is to use requestAnimationFrame to ensure that changes are presented in sync with the display hardware's refresh. requestAnimationFrame also ensure that only when the page is visible will the changes be made. ie (if the client switches tabs to another tab, your tab will not fire any requestAnimationFrame events)
It is also best to keep the canvas resolution as low as possible. Keeping the canvas at a resolution higher than the display means you will be doing a lot of needless rendering on portions that are off screen, or if you are scaling the canvas via CSS the down or upsampling can result in a variety of unwanted artifacts.
The problem with the resize event.
The resize event is triggered by a variety of sources, OS events that change the window size, mouse events, or from Javascript. None of these events are synced to the display, and some resize events can fire at very high rates (mouse driven resize can fire 100+ times a second)
Because resizing the canvas also clears the image data and resets the context state, each resize requires a re-rendering of the content. The rapid firing rate of the resize event can overwork the thread and you will start to lose events , the page will feel laggy and you can get parts of the page that are not updated in time for the next display frame.
When resizing you should try to avoid resizing when not needed. Thus the best time to resize is via a requestAnimationFrame callback.
Realtime rendering
If you are rendering in realtime then the best way to resize is to compare the canvas size to the container's or window size at the start of every render frame. If the sizes do not match then resize the canvas.
// no need for a resize event listener.
function renderLoop(){
// innerWidth / height or containor size
if(canvas.width !== innerWidth || canvas.height !== innerHeight){
canvas.width = innerWidth;
canvas.height = innerHeight;
}
// your code
requestAnimationFrame(renderLoop);
}
requestAnimationFrame(renderLoop);
Static renders
If you are rendering infrequently or as needed. Then you may be best off to keep a canvas at a standard resolution offscreen and use a resizable canvas on screen to render a view of the offscreen canvas.
In that case you keep a main loop alive that will check a semaphore that indicates that there is a need to update the view. Anything that changes the canvas will then just set the flag to true.
const mainCanvas = document.createElement("canvas");
const mCtx ...
const canvas = document.getElementById("displayCanvas");
const ctx ...
// when updating content
function renderContent(){
mCtx.drawStuff...
...
updateView = true; // flag the change
}
// the resize event only need flag that there is a change
window.addEventListener("resize",()=> updateView = true );
var updateView = true;
function renderLoop(){
if(updateView){
updateView = false; // clear the flag
// is there a need to change display canvas resolution.
if(canvas.width !== innerWidth || canvas.height !== innerHeight){
canvas.width = innerWidth;
canvas.height = innerHeight;
}
// draw the mainCanvas onto the display canvas.
ctx.drawImage(mainCanvas, viewOrigin.x, viewOrigin.y);
}
requestAnimationFrame(renderLoop);
}
requestAnimationFrame(renderLoop);
In your case using the above method (even for realtime) gives you better control over what part of the canvas content is seen.
CSS
Some poeple consider that CSS is the only place any type of visual content should be changed. The problem with the canvas is that CSS can not set the canvas resolution so if you use CSS you still have to set the canvas resolution.
For the canvas I do not set any CSS sizes and let the canvas resolution properties set the display size canvas.width=1920; canvas.height=1080; I can not see the point of having having to set the CSS width and height when there is no need to
Note: If you do not use requestAnimationFrame you will need to set the CSS size as you can not guarantee the canvas resolution will be set in time for the next refresh, while auto CSS updates (eg canvas.style.width="100%") will change in sync with the display device.
It works, you could check it with element inspect.
And I was thinking you wanted change the style width/height of canvas, not the width or height of canvas, they are quite different.
The width or height of canvas would just affect the ratio of things you draw on the canvas. And the CSS style width or height could change the display size of the canvas.

How to have a scrolling renderer in ThreeJS?

I'm using the OrtographicCamera.
A canvas is declared in HTML and I pass it to the renderer as shown below:
var canvas = document.getElementById("canvas")
var width = canvas.clientWidth;
var height = canvas.clientHeight;
renderer = new THREE.WebGLRenderer({
antialias: true,
canvas: canvas
});
renderer.setClearColor(0xf0f0f0);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
The problem is that the cubes I'm adding to the scene are being cut.
The scene is growing and has a height bigger than the available viewport's height.
I'd like to have scrollbars so that I can move to the bottom of the canvas and see all the cubes without having to zoom.
See the screenshot:
HTML
<div id="container">
<canvas id="canvas"></canvas>
</div>
CSS
#canvas
{
width: 100%;
height: 100%;
}
Any advice is really appreciated.
You have two things going on. The first is that the canvas needs to be larger than the client window to get scrollbars to show up. The exact size depends on your needs, but try doubling it from what you have now.
The second issue is going to be that the camera itself has left, right, top, and bottom (as well as near and far) planes that define the area within the camera's view. Increase those values when setting up the camera to get more of the scene visible in the canvas. If your width is fine, you may be able to only increase the top/bottom instead.
https://threejs.org/docs/api/cameras/OrthographicCamera.html
I used orbitcontrols and managed to get what I wanted, that is, being able to see the hidden objects in the scene. The user just have to press arrow down and arrow up to move the camera down\up.
No need to fiddle with the canvas element at all. That's good because controlling the canvas size in CSS is a bit hard and much more when there's scrolling involved and having it attached to ThreeJS renderer.
https://github.com/mattdesl/three-orbit-controls

HTML5 - Detecting image size, resizing canvas

I recently made and hosted Catifier.com.
It's working pretty good, except I have a bug with saving I have to work out, and it stretches images when you set them as the background.
Portrait images look horrible.
Would it be possible to detect the width and height of the image a user pastes in the box, then resize the canvas accordingly?
You have to load your picture in a DOM element to know its size.
So basicly when you want to do it, here are the steps :
Add your picture in an invisible DOM element.
You will be able to get picture width and heignt when onload event is lauched.
Then create your canvas depending those two variables.
All can be done in very few javascript lines.
Just gonna write up a very generic and likely not fully functional way to do this real quick just to give more of an idea.
var img = document.getElementByTagName('img');
for (var i = 0; i < img.length; i++) {
var width = img[i].width,
height = img[i].height;
// do some stuff with the img[i] width and height here if you like. for each image on the page...
}
Not how you'd end up actually doing it but it's just as simple as doing, img.width or height. If that helps more at all.

Scaling canvas element with static resolution

I have canvas element and I want to scale it down, but without changing it's js logic. Drawing space in js should always be 600x300px, even if it is displayed in HTML as 300x150px. I know, I can resize image with static resolution, but can I do the same with canvas?
Changing the size using CSS scales it
Live Demo
So basically you set its size for drawing objects, etc, via the width and height properties like so
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
canvas.width = 600;
canvas.height = 300;
and then change its displayed size using css
#canvas{
width: 300px;
height: 150px;
}​
Loktar has one way, using CSS, but that might cause some things to look funny. For instance paths scaled using CSS and scaled using the canvas' own transform may look very different (with the CSS ones looking bad and the canvas ones looking smooth). This depends on the browser though and might be perfectly fine. On chrome at least, text scaled this way looks very bad.
Instead I'd recommend looking at what I wrote here about the concept of "model" coordinates: Working with canvas in different screen sizes
Write everything as if the drawing space is 600x300, but keep a canvas that is 300x150.
Before drawing anything use ctx.scale(0.5, 0.5); and everything will look great!
It's quite possible after all to write one canvas app and the have it scale to all sorts of screens, even if you're just targeting one screen size.

scaling logo in html5 <canvas>?

Having trouble scaling with . It seems to make sense to code up a drawing in canvas to a fixed size (ie 800x600) then scale it for specific locations - but sizing occurs in 4 places: 1) in the context definition (ie ctx.width = 800 2) with ctx.scale; 3) in html with
I can scale it with ctx.scale(0.25,0.25) and use but this doesn't appear right - it seems to want the scale to be proportional.
css sizing simply makes it fuzzy so not a good way to go. Any ideas?
Actually, you can resize a canvas using stylesheets. The results may vary across browsers as HTML5 is still in the process of being finalized.
There is no width or height property for a drawing context, only for canvas. A context's scale is used to resize the unit step size in x or y dimensions and it doesn't have to be proportional. For example,
context.scale(5, 1);
changes the x unit size to 5, and y's to 1. If we draw a 30x30 square now, it will actually come out to be 150x30 as x has been scaled 5 times while y remains the same. If you want the logo to be larger, increase the context scale before drawing your logo.
Mozilla has a good tutorial on scaling and transformations in general.
Edit: In response to your comment, the logo's size and canvas dimensions will determine what should be the scaling factor for enlarging the image. If the logo is 100x100 px in size and the canvas is 800x600, then you are limited by canvas height (600) as its smaller. So the maximum scaling that you can do without clipping part of the logo outside canvas will be 600/100 = 6
context.scale(6, 6)
These numbers will vary and you can do your own calculations to find the optimal size.
You could convert the logo to svg and let the browser do the scaling for you, with or without adding css mediaqueries.
Check out Andreas Bovens' presentation and examples.
You can resize the image when you draw it
imageobject=new Image();
imageobject.src="imagefile";
imageobject.onload=function(){
context.drawImage(imageobject,0,0,imageobject.width,imageobject.height,0,0,800,600);
}
The last 2 arguments are the width an height to resize the image
http://www.w3.org/TR/html5/the-canvas-element.html#dom-context-2d-drawimage
If you set the element.style.width and element.style.height attributes (assuming element is a canvas element) you are stretching the contents of the canvas. If you set the element.width and element.height you are resizing the canvas itself not the content. The ctx.scale is for dynamic resizing whenever you drawing something with javascript and gives you the same stretching effect as element.style.