I noticed that the <canvas> element can have different scales. For instance, if I set the CSS width and height to 100px, but have the javascript set the element's width and height to 200px, the element is sized down so everything printed on it is 1/2 the size. (Or 2x the resolution)
I have a retina screen Macbookpro, so in development, I set the scaling to 2x so the images and objects look clear and crisp on my screen.
I have heard that other screens can have a 1.2x resolution (CSS pixels vs Actual pixels)
Is there a way to find out what the resolution/scaling is of the device's screen so I can make my canvas as crisp and as clean as possible to the user?
If it helps at all, I'm trying to make a game in javascript using canvas as my graphics output.
These properties will give your dimensions:
window.screen.availHeight
window.screen.availWidth
For pixel depth, use this property:
window.devicePixelRatio
For application in canvas, a helpful script and explanation is given here.
After searching around using different terms, I was able to find the answer that I was looking for.
The window object has a variable called window.devicePixelRatio. This lets us know the ratio of pixels to the device's screen pixels. On my retina screen, this variable gives me a 2. With this, I can set the canvas to the correct scaling so it looks clean and crisp on any screen.
Source: http://www.html5rocks.com/en/tutorials/canvas/hidpi/
Related
This question already has answers here:
Display pixel-perfect canvas on all devices
(3 answers)
Closed last month.
Is it possible to set the size of the bitmap in an HTML5 canvas so that it exactly matches the display size in device pixels, ie the number of physical pixels used to display the canvas, even when window.devicePixelRatio is not an integer?
There is a good description of how to resize the canvas on webglfundamentals but it does not properly handle non-integer devicePixelRatios. My understanding so far:
The display size of the canvas is set in CSS with, eg, canvas {width: 200px;}.
The underlying bitmap size is set with, eg, <canvas width="100"/> in HTML or canvas.width = 100 in JS.
The bitmap will be stretched to fit the CSS size (how is affected by object-fit and object-position).
We can set bitmap size to be equal to some expression involving canvas.clientWidth.
canvas.clientWidth is an integer and its unit are CSS pixels and is the calculated width of the content (plus padding). I don't know if the browsers actually draw the content into a whole number of CSS pixels or a whole number of device pixels.
So webglfundamentals suggests something like
canvas.width = Math.floor(canvas.clientWidth * window.devicePixelRatio);
but if window.devicePixelRatio is a fraction, this sometimes doesn't work (2 pixel wide lines drawn on integer coordinates are fuzzy). My 1920x1080 screen has devicePixelRatio of 1.5 by default, and page zoom can affects this, so lots of reasons why devicePixelRatio is not an integer as a rule. What can we do?
1px is a logical length unit in CSS/DOM realm. Dot.
So I don't think that you will find reliable method for getting physical pixels. Check this.
That's why in Sciter I've made px units to represent always physical pixels of the device. And introduced dip units that are logical pixels - 1/96 of inch measured by the ruler on device surface (so 1dip ~ 1px of W3C CSS).
I simply do not understand how to do serious UI development without the ability to define objects in device pixels.
I know this is an old thread, but I hope I can help a bit any other wanderer coming to this.
There is a way to make a "canvas pixel" match "device pixel" as close to a 1:1 ratio as possible, at least from my experience while making a Canvas WebGL based game engine, even with non-integer devicePixelRatio values.
First, there are 3 things we need to understand:
CSS Size: Refers to the actual Canvas DOM element size in CSS pixel (px) units.
Canvas Size: Logical size of the canvas, as set using the element properties (i.e. canvasElem.width = 100;), this affects your render space.
Device Pixel Ratio: Value obtained from window which effectively describes the ratio between a physical pixel and a CSS pixel.
Brief Example
Let's say you have a devicePixelRatio of 2.625, and your mobile device reports a screen/window size of 411 x 487 -a weird size- you might say, but that is in CSS pixel units, if we multiply those by the devicePixelRatio we get a size of 1078 x 1278 which tells us that most likely the device screen is actually a 1080 x 1280 screen, as originally advertised by your favorite phone manufacturer.
Now, to size stuff...
Say we want a canvas filling the whole screen. Set your CSS Size match the screen size (or any box size you want).
canvas.style.width = '411px';
canvas.style.height = '487px';
Now set the Canvas Size to the same but scaled-up by devicePixelRatio:
canvas.width = 411 * window.devicePixelRatio;
canvas.height = 487 * window.devicePixelRatio;
Now you can render your images.
When the browser calculates the layouts, it will have to scale-down your canvas to make it fit in its DOM rectangle area, the down-scale factor is cssSize / canvasSize yielding a down-scale factor of 1 / devicePixelRatio.
And then when the browser needs to render the layout to physical screen, it will scale it up by devicePixelRatio, thus leaving a total scaling of 1.0.
TL;DR
Supersampling. Scale up your canvas logical size by devicePixelRatio while keeping your CSS size at whatever size you need, and let the browser do the rest.
NOTE: If your rendering expects the canvas to be of certain size, and this "scaling up" messes up your layout and/or calculations, use the canvas setTransform method to scale down your "logical units" by 1.0 / devicePixelRatio, no quality is loss at all.
When using the CSS object-fit: contain option to scale the contents of a canvas while maintaining aspect ratio, how do you get the visible pixel dimensions of the canvas after scaling, not including letterbox?
canvas.height & canvas.width give the native resolution of the canvas, and canvas.clientHeight & canvas.clientWidth give the pixel dimensions of the "whole" canvas, including the letterbox bars above and below the scaled area.
This JSFiddle shows a scaled <canvas> inside a larger <div>, and shows which sizes are being returned. In this instance, I'd like a property that returns a width and height of 300. Does such a property exist?
My application needs to use the other object-fit modes as well, so I need a solution that works for any (specifically fill or none). I'd like to avoid switching on the object-fit mode and writing the logic to calculate the size myself if I can.
I know how to find dimensions in pixels both inner, outer, browser etc in Javascript using document, window and other objects..
Problem is, the returned width and hight are in px not in dp.
Lets say if screen is small enough that something is too big to fit I wanna hide some elements. And I wanna make it in a way that I remove elements in specific order (some are more useful, other are for additional info) until everything fits screen both horizontally and vertically (no scroll).
So I would get the screen size and ng-if remove objects from window until remaining objects sizes in pixels added together would be less then window.innerSize. Works well on desktop where pixel is pixel.
BUT, here comes the problem. On mobiles there is often a ratio of real screen pixels and displayed pixels. Eg. lets say a block is 300px high in css (height: 300px !important) but its 700 pixel high on actual device (measured using developer mode click on his bottom). I realize some is due to extra pixels on screen outside of app, like staus bar on top, but rest is in dp to px ratio of a screen.
How would you approach this problem, if there is no solution to original question of finding the dp dimensions of window?
Found it, when you are using Cordova you have
window.devicePixelRatio
available for you.
And it comes to play just nicely. Use window.innerHeight to get actual height of everything inside window (ALSO works with keyboard overlaying your app!).
For each element its height on screen is his_css_height * window.devicePixelRatio.
Translates exactly to what i wanted.
Sorry to answering my own question here but I spent almost 3 hours searching for an answer, and I decided to post here and then continue my search. Just became aware of this, hopefully it helps someone else :)
you can try this :
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics outMetrics = new DisplayMetrics ();
display.getMetrics(outMetrics);
float density = getResources().getDisplayMetrics().density;
float dpHeight = outMetrics.heightPixels / density;
float dpWidth = outMetrics.widthPixels / density;
I have a HTML5 animation that uses a canvas of size 900x520. However ~60% of the canvas is not utilized in the most general case. If some of the parameters of the animation are varied, then the whole canvas is utilized which is the reason why I pegged the maximum canvas size to W:900px and H:520px.
When a user opens the page with this HTML5 canvas, a general case is considered and only 280x280 at the center of the HTML5 canvas is used. As per my knowledge though I'm not using the other 60% of the canvas is not used but is rendered every time which is causing the animation to slow down, especially on tablets and mobile phones (Most of my intended users are tablets / mobile users). So, I would like to know if there are any techniques to render only a set of pixels everytime (say 300x300 at the center of the canvas) ? Are there any workaround solutions ?
I'm already using RequestAnimationFrame (However, I want some more optimization)
First thing i would try is to use the clip() function. It slows down the drawing, but in your case it might be faster to check first against a clip since 60% of the time the clipping will quit the drawing.
Second thing to try is to handle clipping by yourself : handle a viewport object ( center x, center y, width, height ) and exclude unseen object with simple bounding box testing. Rq : Maybe the way you draw your scene allows for easy optimisation.
3) do not define a canvas bigger than the screen : rather define it at max size, and keep track of an offset, like with a viewPort object. And to avoid re-writing all your draw code, just use save/translate/restore before/after your old methods. For the clipping on this canvas, refer to 1) :-)
4) you can reduce graphic strain on mobile by using css scaling. Even if scaling reduces performances, on mobiles it is faster to draw on smaller canvas with a css scale than to draw full resolution with no scale.
So you have to set canvas.style.width and canvas.style.height to some wise values, and you can set canvas.width and canvas.height to the setting that allows for the right performance.
I'm looking to create a system where users create an image in canvas on their browser (with the help of paper.js or processing.js etc) and be able to print a large (up to A3, 300dpi ideally) size representation of what they have created in canvas.
Obviously exporting images straight from the canvas element in the users browser I'm limited to screen size and resolution etc.
So I've looked into the solution of momentarily scaling up the canvas element when the users saves and capturing the image data at the larger size. This could be a solution but I'm pretty sure file size would get out of hand pretty quickly at larger sizes.
I haven't used Node.js very much but was wondering if anyone with experience would know if Node could achieve this and if it would be a better solution and briefly how I'd go about it?
I see two ways to achieve what you want :
use an oversized canvas, that you scale with css.
For instance, you can have a 1000X1000 canvas, that you show within a 200pxX200px smaller view.
<canvas width=1000 height=1000
style = 'width:200px; height:200px;' id='cv'>
</canvas>
use a small canvas for display on screen, and really draw on a backing canvas, that you reproduce on the view canvas at each change.
Both solution cannot solve the issue that mouse coordinates are integer, so to implement a 'pixel perfect' location of object you'll have to implement some kind of zooming. Second solution might be simpler for this.
To retrieve the mouse coordinates, with css scaling do not forget to multiply them by the scale, and in case 2, by the scale you decided.
// formula to get the css scale :
var cssScaleX = canvas.width / canvas.offsetWidth;
var cssScaleY = canvas.height / canvas.offsetHeight;
// then mouse coords are to be multiplied by the scale :
// mouse.x *= cssScaleX;
I tried quickly both solutions, and i was quite surprised to see that css solution is very slow (in both Ch and FF), and it seems faster to copy a back canvas than to have css doing it.
Maybe it depends on some quality settings, but it seems solution 2 is both more flexible and faster.
first css version is here (move mouse to draw 10X10 rect) :
http://jsbin.com/kegohufu/1/
second back canvas + copy version is here (move mouse to draw 10X10 rect) :
http://jsbin.com/qomiqoqi/1/