Pergunta

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
  • ...
Foi útil?

Solução

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

Outras dicas

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());

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;
}

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
}

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top