iPhone 5 and full canvas width - javascript

Okay, so my iPhone 5 arrived (I live in Australia and am lucky to have it before the US) and the first thing I wanted to do was take full advantage of the 88 pixels of extra width for my 2D platformer using the Impact JS engine.
I cannot get my head around how to actually modify the canvas width.
This is what I was using previously:
ig.main('#canvas', MyGame, 60, 240, 160, 2);
Now, I thought it would've been as easy to just increase the 240 by 44 pixels (considering there's 44 pixels on either side in landscape):
ig.main('#canvas', MyGame, 60, 284, 160, 2);
However, I'm still seeing 44 pixels black bars on either side after the above code change.
Not sure whether I have to modify any other Impact engine files in order to get this working. I haven't played around with it enough as I'm at work at the moment and shouldn't really be working on my game. I'm sure there's a few other areas I'll have to modify to get this working.
Btw, I'm using iOSImpact and deploying my game directly to my iPhone 5, so there may be files within there that need to be updated as well.
Also I noticed the following properties for detection between mobile devices. I'd need to throw in an ig.ua.iPhone5 property in here as well.
ig.ua.pixelRatio = window.devicePixelRatio || 1;
ig.ua.viewport = {
width: window.innerWidth,
height: window.innerHeight
};
ig.ua.screen = {
width: window.screen.availWidth * pixelRatio,
height: window.screen.availHeight * pixelRatio
};
ig.ua.iPhone = /iPhone/i.test(navigator.userAgent);
ig.ua.iPhone4 = (iPhone && pixelRatio == 2);
ig.ua.iPhone5 = ??
ig.ua.iPad = /iPad/i.test(navigator.userAgent);
ig.ua.android = /android/i.test(navigator.userAgent);
ig.ua.webos = /hpwos/i.test(navigator.userAgent);
ig.ua.iOS = iPhone || iPad;
ig.ua.mobile = iOS || android || webos;
I did a console.log on the following and this is what it returns:
ig.ua.viewport.width = 640
ig.ua.viewport.height = 960
ig.ua.screen.width = 1280
ig.ua.screen.height = 1920
I'm a little confused with the above results as well, specifically the ig.ua.viewport.width returning 960. I would've thought it should be returning 1136.

Your app has to tell the OS that it supports the taller/wider screen. I think this is done by specifying a startup image of that size. Otherwise, you'll be run in compatibility mode and not get to use the full screen

Related

Why there's white line appears in HTML canvas between two shape?

Why there's white line appears in JS canvas between two shape?
I'm making a game with JS / TS (I'm using MacBook Pro), with HTML5 canvas, and there's a unexpected white line appear between two shapes in safari browser:
but I run exactly same code in chrome, everything is fine:
So why this is happened? And how can I fix it?
code I'm using to render
CONTEXT.drawImage(
CACHES.get(this.materialURL),
(this.rect.x - camera.location.x) * GRID_W,
(this.rect.y - camera.location.y) * GRID_H,
GRID_W,
GRID_H,
);
Render artifacts
More info?
There are many reasons this can happen. Most are the result of rounding errors. Sometimes the error is in JavaScript, other times it occurs in the rendering.
There are subtle differences in the JS engines (resulting from hardware, OS, driver and or engine implementations) that can result in rendering artifact that differ across devices.
There are major differences in rendering implementations even on the same browser, same OS, and using the same hardware, depending on setup (flags).
Where your artifacts are coming from I can only guess at without a lot more information. Even how you captured the example images can change the solution.
Things to try
Try using nearest pixel lookup by setting 2D context smoothing off
ctx.imageSmoothingEnabled = false;
To turn back on use
ctx.imageSmoothingEnabled = true;
Use software rendering (CPU) by setting the willReadFrequently flag when getting the context.
const ctx = canvas.getContext("2d", {willReadFrequently: true});
Note this can slow things down a lot
Turn off canvas alpha (to stop BG appearing at seams) using context option alpha
const ctx = canvas.getContext("2d", {alpha: false});
Ensure that the source image resolution matches the render size.
In other words does
const img = CACHES.get(this.materialURL);
const isSameRes = img.width === GRID_W && img.height === GRID_H;
isSameRes should equal true?
Note use naturalWidth and naturalHeight if img is an instance of Image
Extend the source image by 1 px on each edge copying the edge pixels as shown in next image. This will prevent transparent edge pixels bleeding into rendering result.
Then render the inner original image as shown below
const img = CACHES.get(this.materialURL);
ctx.drawImage(
img,
1, 1, img.width - 2, img.height - 2,
(this.rect.x - camera.location.x) * GRID_W,
(this.rect.y - camera.location.y) * GRID_H,
GRID_W,
GRID_H,
);
Note this will add in tiny bit of overhead.
Ensure integer coordinates by flooring coordinates and forcing constants to be integers.
// When defining GRID_W and GRID_H (assuming positive integer values)
// Force internal type to int32 by using bitwise operation on values
// Note this may not do anything
const GRID_W = 32 | 0;
const GRID_H = 32 | 0;
// Render using floored coordinates.
ctx.drawImage(
CACHES.get(this.materialURL),
Math.floor((this.rect.x - camera.location.x) * GRID_W),
Math.floor((this.rect.y - camera.location.y) * GRID_H),
GRID_W,
GRID_H,
);
More
There are many more options but without the needed information I would be wasting your time.

Wrong display resolution in browsers on mobiles [duplicate]

Is there a way that works for all browsers?
original answer
Yes.
window.screen.availHeight
window.screen.availWidth
update 2017-11-10
From Tsunamis in the comments:
To get the native resolution of i.e. a mobile device you have to multiply with the device pixel ratio: window.screen.width * window.devicePixelRatio and window.screen.height * window.devicePixelRatio. This will also work on desktops, which will have a ratio of 1.
And from Ben in another answer:
In vanilla JavaScript, this will give you the AVAILABLE width/height:
window.screen.availHeight
window.screen.availWidth
For the absolute width/height, use:
window.screen.height
window.screen.width
var width = screen.width;
var height = screen.height;
In vanilla JavaScript, this will give you the AVAILABLE width/height:
window.screen.availHeight
window.screen.availWidth
For the absolute width/height, use:
window.screen.height
window.screen.width
Both of the above can be written without the window prefix.
Like jQuery? This works in all browsers, but each browser gives different values.
$(window).width()
$(window).height()
You can also get the WINDOW width and height, avoiding browser toolbars and... (not just screen size).
To do this, use:
window.innerWidth and window.innerHeight properties. See it at w3schools.
In most cases it will be the best way, in example, to display a perfectly centred floating modal dialog. It allows you to calculate positions on window, no matter which resolution orientation or window size is using the browser.
Do you mean display resolution (eg 72 dots per inch) or pixel dimensions (browser window is currently 1000 x 800 pixels)?
Screen resolution enables you to know how thick a 10 pixel line will be in inches. Pixel dimensions tell you what percentage of the available screen height will be taken up by a 10 pixel wide horizontal line.
There's no way to know the display resolution just from Javascript since the computer itself usually doesn't know the actual dimensions of the screen, just the number of pixels. 72 dpi is the usual guess....
Note that there's a lot of confusion about display resolution, often people use the term instead of pixel resolution, but the two are quite different. See Wikipedia
Of course, you can also measure resolution in dots per cm. There is also the obscure subject of non-square dots. But I digress.
Using jQuery you can do:
$(window).width()
$(window).height()
Trying to get this on a mobile device requires a few more steps. screen.availWidth stays the same regardless of the orientation of the device.
Here is my solution for mobile:
function getOrientation(){
return Math.abs(window.orientation) - 90 == 0 ? "landscape" : "portrait";
};
function getMobileWidth(){
return getOrientation() == "landscape" ? screen.availHeight : screen.availWidth;
};
function getMobileHeight(){
return getOrientation() == "landscape" ? screen.availWidth : screen.availHeight;
};
See Get Monitor Screen Resolution with Javascript and the window.screen object
function getScreenWidth()
{
var de = document.body.parentNode;
var db = document.body;
if(window.opera)return db.clientWidth;
if (document.compatMode=='CSS1Compat') return de.clientWidth;
else return db.clientWidth;
}
just for future reference:
function getscreenresolution()
{
window.alert("Your screen resolution is: " + screen.height + 'x' + screen.width);
}
If you want to detect screen resolution, you might want to checkout the plugin res. It allows you to do the following:
var res = require('res')
res.dppx() // 1
res.dpi() // 96
res.dpcm() // 37.79527559055118
Here are some great resolution takeaways from Ryan Van Etten, the plugin's author:
2 unit sets exist and differ at a fixed scale: device units and CSS units.
Resolution is calculated as the number of dots that can fit along a particular CSS length.
Unit conversion: 1⁢in = 2.54⁢cm = 96⁢px = 72⁢pt
CSS has relative and absolute lengths. In normal zoom: 1⁢em = 16⁢px
dppx is equivalent to device-pixel-ratio.
devicePixelRatio definition differs by platform.
Media queries can target min-resolution. Use with care for speed.
Here's the source code for res, as of today:
!function(root, name, make) {
if (typeof module != 'undefined' && module.exports) module.exports = make()
else root[name] = make()
}(this, 'res', function() {
var one = {dpi: 96, dpcm: 96 / 2.54}
function ie() {
return Math.sqrt(screen.deviceXDPI * screen.deviceYDPI) / one.dpi
}
function dppx() {
// devicePixelRatio: Webkit (Chrome/Android/Safari), Opera (Presto 2.8+), FF 18+
return typeof window == 'undefined' ? 0 : +window.devicePixelRatio || ie() || 0
}
function dpcm() {
return dppx() * one.dpcm
}
function dpi() {
return dppx() * one.dpi
}
return {'dppx': dppx, 'dpi': dpi, 'dpcm': dpcm}
});
if you mean browser resolution then
window.innerWidth gives you the browser resolution
you can test with http://howbigismybrowser.com/
try changing your screen resolution by zoom in / out browser and check resolution size with http://howbigismybrowser.com/
Window.innerWidth should be same as screen resolution width
Easy steps to find screen resolution is:
Copy
`My screen resolution is: ${window.screen.width} * ${window.screen.height}`
paste in browser console
hit enter

get device PPI in javascript

How to get real PPI (pixels per inch) device resolution with javascript?
some examples of device and value expected:
iMac 27-inch: 109ppi
iPad: 132ppi
19inch 1440x900 screen: 89ppi
...
Running a native application directly on top of the operating system is the only surefire way to acquire the physical characteristics of the client monitor stored in the EDID. No mainstream browser engine currently offers this information through any web API and will likely not in the foreseeable future.
However there are several ways to approximate the density to varying levels of accuracy.
All modern browsers give hints to the pixel density via attributes like devicePixelRatio, deviceXDPI which basically tell you how much zoom the client has going on (versus 1.0x Operating System default zoom). If you're targeting only a few devices like the Apple line then you might be able to tell which one is which, although Apple doesn't leave a scrap of a clue to discern a iPad mini from a regular iPad.
Another alternative is using device databases or proprietary software that analyze the client's "user agent string" to achieve a hit-or-miss guess of the device and then looking up the DPI associated with that device if it exists in their big database. Expensive proprietary systems might have higher accuracy by leveraging complex data mining algorithms but regardless any system like this would need constant manual updating and will still remain open to client manipulation since they can just change their user agent string ("view this website in desktop mode")
It's really an unfortunate situation not having this information available. I've spent countless hours researching ANY POSSIBLE WAY to make a PPI aware Web Application.
Maybe one day someone will be able to convince the folks at WebKit or Mozilla or Microsoft to allow people to create PPI aware Web apps for augmented reality and such... Sigh
In pure JS:
function calcScreenDPI() {
const el = document.createElement('div');
el.style = 'width: 1in;'
document.body.appendChild(el);
const dpi = el.offsetWidth;
document.body.removeChild(el);
return dpi;
}
console.log(calcScreenDPI());
You can create an element of 1in of fixed height, then look at its height in pixels
createDpiTestElements()
var dpi = getDpi()
Because
[dpi] = [px/in]
So let
p be an object's length in pixels [px]
i be that object's length in inches [in]
and d be the searched DPI/PPI for that object, in pixels per inches [dpi]
We get
d = p/i
So if we choose
i = 1
we get
d = p
JS Code
/**
* #function
* #inner
*/
function createDpiTestElements () {
var getDpiHtmlStyle = 'data-dpi-test { height: 1in; left: -100%; position: absolute; top: -100%; width: 1in; }'
var head = document.getElementsByTagName('head')[0]
var getDPIElement = document.createElement('style')
getDPIElement.setAttribute('type', 'text/css')
getDPIElement.setAttribute('rel', 'stylesheet')
getDPIElement.innerHTML = getDpiHtmlStyle
head.appendChild(getDPIElement)
var body = document.getElementsByTagName('body')[0]
var dpiTestElement = document.createElement('data-dpi-test')
dpiTestElement.setAttribute('id', 'dpi-test')
body.appendChild(dpiTestElement)
}
/**
* Evaluate the DPI of the device's screen (pixels per inche).
* It creates and inpect a dedicated and hidden `data-dpi-test` DOM element to
* deduct the screen DPI.
* #method
* #static
* #returns {number} - The current screen DPI, so in pixels per inch.
*/
function getDpi () {
return document.getElementById('dpi-test').offsetHeight
}
In a Chrome Extension
Related, but not an exact answer to the poster's question:
You can get the DPI of your display (and a lot of other information), if you're developing a Chrome Extension with the proper permissions using chrome.system.display.getInfo:
const info = await chrome.system.display.getInfo()
console.log(info.dpiX, info.dpiY)
It returns a DisplayUnitInfo object that tells you everything you might need to know.
Addendum to answers above for plain JS DOM
Also, it's worth noting that you can get an approximate pixel density combining pieces of information provided above. However, it's unlikely this will be useful for anything but logging purposes, IMO. I say this because any pixel values you use in your DOM/JS code aren't going to be "device pixel values". They'll likely have a multiplier applied.
The trick is to alter #nikksan's answer above and multiply by window.devicePixelRatio
Note that it is going to be the same answer every single time on each device, so it's probably only worth doing once.
function calcScreenDPI() {
// Create a "1 inch" element to measure
const el = document.createElement('div');
el.style.width = '1in';
// It has to be appended to measure it
document.body.appendChild(el);
// Get it's (DOM-relative) pixel width, multiplied by
// the device pixel ratio
const dpi = el.offsetWidth * devicePixelRatio;
// remove the measurement element
el.remove();
return dpi;
}
Here is what works for me (but didn't test it on mobile phones):
Then I put in the .js: screenPPI = document.getElementById('ppitest').offsetWidth;
This got me 96, which corresponds to my system's ppi.

How to map deviceorientation to MozOrientation values

I have previously created a labyrinth game which works with Firefox's MozOrientation. I am now looking into getting it working with WebKit also...
But Webkit uses the w3c's DeviceOrientation. The values appear to be totally different, but someone must have some algorithm to get it from one to the other? Or am I missing something simple?
The labyrinth game with github link
http://playground.marmaladeontoast.co.uk/labyrinth/
MozOrientation
https://developer.mozilla.org/en/Detecting_device_orientation
DeviceOrientation
http://dev.w3.org/geo/api/spec-source-orientation.html
Some sample values I have obtained:
Chrome
alpha = null
beta = -178
gamma = 4.57
Firefox
x = 0.035999998450279236
y = -0.02800000086426735
z = 1
Any help would be greatly appreciated :)
I've been playing around with this a bit. The source below 'maps' both alternatives to an understandable degrees range for x/gamma and y/beta.
The MozOrientation.z value is completely different from the deviceorientation.alpha value. The first returns the vertical orientation, the latter returns a sort of compass value. These values are therefor not interchangable / convertible.
function displayOrientation(orientData){
var x = Math.round(orientData.x);
var y = Math.round(orientData.y);
var z = orientData.z;
document.getElementById('x').value = x;
document.getElementById('y').value = y;
document.getElementById('z').value = z;
}
window.addEventListener("MozOrientation", function(orientData){
var obj = {};
obj.x = orientData.x * 90;
obj.y = orientData.y * 90;
obj.z = orientData.z;
displayOrientation(obj);
}, true);
window.addEventListener("deviceorientation", function(orientData) {
var obj = {};
obj.x = orientData.gamma;
obj.y = orientData.beta;
obj.z = orientData.alpha;
displayOrientation(obj);
}, true);
It seems the current desktop Safari (5.0.3) doesn't support this event at all. The beta value in desktop Chrome 9 is exactly 180 less than it is in mobile Safari.
The x & y values in Firefox and mobile Safari should be approximately the same.
I hope that this codes clarify everything. Although the values this code returns can't be matched directly with the values of beta and gamma:
Mozilla returns the sin of the angle so, to get the angle...
X: Math.asin(eventInfo.x)*180/Math.PI
Y: Math.asin(eventInfo.y)*180/Math.PI
Z: Math.asin(eventInfo.z)*180/Math.PI
Webkit returns the acceleration in each axe. Then, to get the angle... (The sing changes is just to unify returned values)
X: Math.asin(clean(eventInfo.accelerationIncludingGravity.x/GRAVITY))*180/Math.PI
Y: Math.asin(clean(-eventInfo.accelerationIncludingGravity.y/GRAVITY))*180/Math.PI
Z: Math.asin(clean(-eventInfo.accelerationIncludingGravity.z/GRAVITY))*180/Math.PI
Being clean: (Because sometimes, the data returned, even without accelerating the phone, it's more than 9.8)
function clean(data)
{
if(data<-1.0)
{
return -1.0;
}
else
{if(data>1.0)
{
return 1.0;
}else
{
return data;
}}
}
The meaning of each axe is:
X-> inclination of the mobile to its right or left. Will be + if you tilt the mobile to its right (clockwise), keeping the buttons of volume up.
Y-> Tells if the mobile is up -if the lock/on-off button is up- (angle +) or down (angle -). It tells the inclination of the mobile relative to the floor. (angle of Y vector with plane X-Z)
Z-> Tells if the mobile screen is facing up (angle +) or upside down (angle -). It's the angle of the Z vector on the plane Y-X
Hope this is worthy for you!
Anyway, I'm working on some GWT classes to make working with this easier :) It's not difficult but I do not have so much time.
I'll tell you
You should be able to calculate one according to the other.
The problem I see is that Mozilla doesn't tell what means the data it returns.
I'm working now with three mobile devices: ipad, iphone4 and nokia n900.
When I work with iOS and safari and I get the accelerationIncludingGravity, I get values from -9.8 to 9.8. Those are values of the gravity that affect each axe. Then using trigonometry, I can calculate the angle in each axe.
If I get orientation in the iphone4, I can get directly the angle in each axe.
The problem comes with nokia n900, whose browser is based in Mozilla. That browser only returns data from -1 to 1. Documentation says that it's the tilt of each axe. In what unities? Cosine? Or just angle/180?
I guess that it's the cosine as when I put it in horizontal, it returns 0, and when I put it in vertical, it gets 1. And upside down, returns -1. Moreover, If you tilt it in aprox 60degrees, it returns 1/2.
As far I've seen, when using a Macbook laptop with Firefox, the data x,y and z is get as the cosine.
Mozilla Firefox now fully supports the W3C DeviceOrientation Events API. You should not use MozOrientation in your web applications any more.

Detecting the system DPI/PPI from JS/CSS?

I'm working on a kind of unique app which needs to generate images at specific resolutions according to the device they are displayed on. So the output is different on a regular Windows browser (96ppi), iPhone (163ppi), Android G1 (180ppi), and other devices. I'm wondering if there's a way to detect this automatically.
My initial research seems to say no. The only suggestion I've seen is to make an element whose width is specified as "1in" in CSS, then check its offsetWidth (see also How to access screen display’s DPI settings via javascript?). Makes sense, but iPhone is lying to me with that technique, saying it's 96ppi.
Another approach might be to get the dimensions of the display in inches and then divide by the width in pixels, but I'm not sure how to do that either.
<div id='testdiv' style='height: 1in; left: -100%; position: absolute; top: -100%; width: 1in;'></div>
<script type='text/javascript'>
var devicePixelRatio = window.devicePixelRatio || 1;
dpi_x = document.getElementById('testdiv').offsetWidth * devicePixelRatio;
dpi_y = document.getElementById('testdiv').offsetHeight * devicePixelRatio;
console.log(dpi_x, dpi_y);
</script>
grabbed from here http://www.infobyip.com/detectmonitordpi.php. Works on mobile devices! (android 4.2.2 tested)
I came up with a way that doesn't require the DOM... at all
The DOM can be messy, requiring you to append stuff to the body without knowing what stuff is going on with width: x !important in your stylesheet. You would also have to wait for the DOM to be ready to use...
/**
* Binary search for a max value without knowing the exact value, only that it can be under or over
* It dose not test every number but instead looks for 1,2,4,8,16,32,64,128,96,95 to figure out that
* you thought about #96 from 0-infinity
*
* #example findFirstPositive(x => matchMedia(`(max-resolution: ${x}dpi)`).matches)
* #author Jimmy Wärting
* #see {#link https://stackoverflow.com/a/35941703/1008999}
* #param {function} fn The function to run the test on (should return truthy or falsy values)
* #param {number} start=1 Where to start looking from
* #param {function} _ (private)
* #returns {number} Intenger
*/
function findFirstPositive (f,b=1,d=(e,g,c)=>g<e?-1:0<f(c=e+g>>>1)?c==e||0>=f(c-1)?c:d(e,c-1):d(c+1,g)) {
for (;0>=f(b);b<<=1);return d(b>>>1,b)|0
}
var dpi = findFirstPositive(x => matchMedia(`(max-resolution: ${x}dpi)`).matches)
console.log(dpi)
There is the resolution CSS media query — it allows you to limit CSS styles to specific resolutions:
http://www.w3.org/TR/css3-mediaqueries/#resolution
However, it’s only supported by Firefox 3.5 and above, Opera 9 and above, and IE 9. Other browsers won’t apply your resolution-specific styles at all (although I haven’t checked non-desktop browsers).
Here is what works for me (but didn't test it on mobile phones):
<body><div id="ppitest" style="width:1in;visible:hidden;padding:0px"></div></body>
Then I put in the .js: screenPPI = document.getElementById('ppitest').offsetWidth;
This got me 96, which corresponds to my system's ppi.
DPI is by definition tied to the physical size of the display. So you won't be able to have the real DPI without knowing exactly the hardware behind.
Modern OSes agreed on a common value in order to have compatible displays: 96 dpi. That's a shame but that's a fact.
You will have to rely on sniffing in order to be able to guess the real screen size needed to compute the resolution (DPI = PixelSize / ScreenSize).
I also needed to display the same image at the same size at different screen dpi but only for Windows IE. I used:
<img src="image.jpg" style="
height:expression(scale(438, 192));
width:expression(scale(270, 192))" />
function scale(x, dpi) {
// dpi is for orignal dimensions of the image
return x * screen.deviceXDPI/dpi;
}
In this case the original image width/height are 270 and 438 and the image was developed on 192dpi screen. screen.deviceXDPI is not defined in Chrome and the scale function would need to be updated to support browsers other than IE
The reply from #Endless is pretty good, but not readable at all,
this is a similar approche with fixed min/max (it should be good ones)
var dpi = (function () {
for (var i = 56; i < 2000; i++) {
if (matchMedia("(max-resolution: " + i + "dpi)").matches === true) {
return i;
}
}
return i;
})();
matchMedia is now well supported and should give good result, see http://caniuse.com/#feat=matchmedia
Be careful the browser won't give you the exact screen dpi but only an approximation
function getPPI(){
// create an empty element
var div = document.createElement("div");
// give it an absolute size of one inch
div.style.width="1in";
// append it to the body
var body = document.getElementsByTagName("body")[0];
body.appendChild(div);
// read the computed width
var ppi = document.defaultView.getComputedStyle(div, null).getPropertyValue('width');
// remove it again
body.removeChild(div);
// and return the value
return parseFloat(ppi);
}
(From VodaFone)
Reading through all these responses was quite frustrating, when the only correct answer is: No, it is not possible to detect the DPI from JavaScript/CSS. Often, the operating system itself does not even know the DPI of the connected screens (and reports it as 96 dpi, which I suspect might be the reason why many people seem to believe that their method of detecting DPI in JavaScript is accurate). Also, when multiple screens are connected to a device forming a unified display, the viewport and even a single DOM element can span multiple screens with different DPIs, which would make these calculations quite challenging.
Most of the methods described in the other answers will almost always result in an output of 96 dpi, even though most screens nowadays have a higher DPI. For example, the screen of my ThinkPad T14 has 157 dpi, according to this calculator, but all the methods described here and my operating system tell me that it has 96 dpi.
Your idea of assigning a CSS width of 1in to a DOM element does not work. It seems that a CSS inch is defined as 96 CSS pixels. By my understanding, a CSS pixel is defined as a pixel multiplied by the devicePixelRatio, which traditionally is 1, but can be higher or lower depending on the zoom level configured in the graphical interface of the operating system and in the browser.
It seems that the approach of using resolution media queries produces at least some results on a few devices, but they are often still off by a factor of more than 2. Still, on most devices this approach also results in a value of 96 dpi.
I think your best approach is to combine the suggestion of the "sniffer" image with a matrix of known DPIs for devices (via user agent and other methods). It won't be exact and will be a pain to maintain, but without knowing more about the app you're trying to make that's the best suggestion I can offer.
Can't you do anything else? For instance, if you are generating an image to be recognized by a camera (i.e. you run your program, swipe your cellphone across a camera, magic happens), can't you use something size-independent?
If this is an application to be deployed in controlled environments, can you provide a calibration utility? (you could make something simple like print business cards with a small ruler in it, use it during the calibration process).
I just found this link: http://dpi.lv/. Basically it is a webtool to discover the client device resolution, dpi, and screen size.
I visited on my computer and mobile phone and it provides the correct resolution and DPI for me. There is a github repo for it, so you can see how it works.
Generate a list of known DPI:
https://stackoverflow.com/a/6793227
Detect the exact device. Using something like:
navigator.userAgent.toLowerCase();
For example, when detecting mobile:
window.isMobile=/iphone|ipod|ipad|android|blackberry|opera mini|opera mobi|skyfire|maemo|windows phone|palm|iemobile|symbian|symbianos|fennec/i.test(navigator.userAgent.toLowerCase());
And profit!
Readable code from #Endless reply:
const dpi = (function () {
let i = 1;
while ( !hasMatch(i) ) i *= 2;
function getValue(start, end) {
if (start > end) return -1;
let average = (start + end) / 2;
if ( hasMatch(average) ) {
if ( start == average || !hasMatch(average - 1) ) {
return average;
} else {
return getValue(start, average - 1);
}
} else {
return getValue(average + 1, end);
}
}
function hasMatch(x) {
return matchMedia(`(max-resolution: ${x}dpi)`).matches;
}
return getValue(i / 2, i) | 0;
})();
Maybe I'm a little bit steering off this topic...
I was working on a html canvas project, which was intended to provide a drawing canvas for people to draw lines on. I wanted to set canvas's size to 198x280mm which is fit for A4 printing.
So I started to search for a resolution to convert 'mm' to 'px' and to display the canvas suitably on both PC and mobile.
I tried solution from #Endless ,code as:
const canvas = document.getElementById("canvas");
function findFirstPositive(b, a, i, c) {
c=(d,e)=>e>=d?(a=d+(e-d)/2,0<b(a)&&(a==d||0>=b(a-1))?a:0>=b(a)?c(a+1,e):c(d,a-1)):-1
for (i = 1; 0 >= b(i);) i *= 2
return c(i / 2, i)|0
}
const dpi = findFirstPositive(x => matchMedia(`(max-resolution: ${x}dpi)`).matches)
let w = 198 * dpi / 25.4;
let h = 280 * dpi / 25.4;
canvas.width = w;
canvas.height = h;
It worked well on PC browser, showing dpi=96 and size was 748x1058 px;work well on PC
However turned to mobile devices, it was much larger than I expected: size: 1902x2689 px.can't work on mobile
After searching for keywords like devicePixelRatio, I suddenly realize that, I don't actually need to show real A4 size on mobile screen (under which situation it's actually hard to use), I just need the canvas's size fit for printing, so I simply set the size to:
let [w,h] = [748,1058];
canvas.width = w;
canvas.height = h;
...and it is well printed:well printed

Categories

Resources