从 JS/CSS 检测系统 DPI/PPI?

发布于 2024-07-08 08:42:06 字数 419 浏览 9 评论 0原文

我正在开发一种独特的应用程序,它需要根据显示的设备以特定分辨率生成图像。 因此,在常规 Windows 浏览器 (96ppi)、iPhone (163ppi)、Android G1 (180ppi) 和其他设备上的输出是不同的。 我想知道是否有办法自动检测到这一点。

我的初步研究似乎是否定的。 我见过的唯一建议是制作一个宽度在 CSS 中指定为“1in”的元素,然后检查其 offsetWidth (另请参阅 如何通过 JavaScript 访问屏幕显示的 DPI 设置?)。 有道理,但 iPhone 用这种技术对我撒谎,说它是 96ppi。

另一种方法可能是获取显示器的尺寸(以英寸为单位),然后除以宽度(以像素为单位),但我也不知道如何做到这一点。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

土豪我们做朋友吧 2024-07-15 08:42:06
<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>

从这里获取http://www.infobyip.com/detectmonitordpi.php。 适用于移动设备! (安卓4.2.2测试)

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

月隐月明月朦胧 2024-07-15 08:42:06

我想出了一种根本不需要 DOM 的方法...

DOM 可能很混乱,要求您在不知道 width: x !important 在你的样式表中。 您还必须等待 DOM 准备好使用......

/**
 * 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)

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)

幽蝶幻影 2024-07-15 08:42:06

resolution CSS 媒体查询 - 它允许您将 CSS 样式限制为特定分辨率:

但是,仅 Firefox 3.5 及以上版本、Opera 9 及以上版本以及 IE 9 支持。 其他浏览器根本不会应用您的特定于分辨率的样式(尽管我没有检查非桌面浏览器)。

There is the resolution CSS media query — it allows you to limit CSS styles to specific resolutions:

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).

烟花肆意 2024-07-15 08:42:06

阅读所有这些回复非常令人沮丧,因为唯一正确的答案是:不,不可能从 JavaScript/CSS 检测 DPI。通常,操作系统本身甚至不知道 DPI连接屏幕的数量(并将其报告为 96 dpi,我怀疑这可能是许多人似乎相信他们在 JavaScript 中检测 DPI 的方法是准确的原因)。 此外,当多个屏幕连接到形成统一显示的设备时,视口甚至单个 DOM 元素都可以跨越具有不同 DPI 的多个屏幕,这将使这些计算变得非常具有挑战性。

其他答案中描述的大多数方法几乎总是会产生 96 dpi 的输出,即使现在大多数屏幕都有更高的 DPI。 例如,根据这个计算器,我的ThinkPad T14的屏幕有157 dpi,但是所有方法这里描述的和我的操作系统告诉我它有 96 dpi。

您将 1in 的 CSS 宽度分配给 DOM 元素的想法行不通。 看起来 CSS 英寸被定义为 96 个 CSS 像素。 据我了解,CSS 像素定义为像素乘以 devicePixelRatio,传统上为 1,但可以更高或更低,具体取决于在操作系统和浏览器的图形界面。

似乎使用 分辨率 媒体查询的方法在少数设备上至少会产生一些结果,但它们通常仍然偏差超过 2 倍。尽管如此,在大多数设备上,此方法也会产生 96 dpi 的值。

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.

森罗 2024-07-15 08:42:06

根据定义,DPI 与显示器的物理尺寸相关。 因此,如果不确切了解背后的硬件,您将无法获得真正的 DPI。

为了具有兼容的显示器,现代操作系统商定了一个共同的值:96 dpi。 这很遗憾,但这是事实。

您必须依靠嗅探才能猜测计算分辨率所需的实际屏幕尺寸(DPI = PixelSize / ScreenSize)。

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).

究竟谁懂我的在乎 2024-07-15 08:42:06

这是对我有用的方法(但没有在手机上进行测试):

<body><div id="ppitest" style="width:1in;visible:hidden;padding:0px"></div></body>

然后我放入 .js: screenPPI = document.getElementById('ppitest').offsetWidth;

这得到了我 96 ,它对应于我系统的 ppi。

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.

音盲 2024-07-15 08:42:06

我还需要在不同的屏幕 dpi 下以相同的尺寸显示相同的图像,但仅限于 Windows IE。 我使用:

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

在本例中,原始图像宽度/高度为 270 和 438,图像在 192dpi 屏幕上开发。 screen.deviceXDPI 未在 Chrome 中定义,需要更新缩放功能以支持 IE 以外的浏览器

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

橘和柠 2024-07-15 08:42:06

@Endless 的回复很好,但根本不可读,
这是一个类似的方法,具有固定的最小/最大(应该是好的)

var dpi = (function () {
    for (var i = 56; i < 2000; i++) {
        if (matchMedia("(max-resolution: " + i + "dpi)").matches === true) {
            return i;
        }
    }
    return i;
})();

matchMedia 现在得到了很好的支持,并且应该给出良好的结果,请参阅 http://caniuse.com/#feat=matchmedia

请注意,浏览器不会为您提供准确的屏幕 dpi,而只会提供近似值

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

十二 2024-07-15 08:42:06
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);
} 

(来自沃达丰)

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)

回梦 2024-07-15 08:42:06

我认为最好的方法是将“嗅探器”图像的建议与设备的已知 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.

乄_柒ぐ汐 2024-07-15 08:42:06

你就不能做点别的吗? 例如,如果您正在生成一个由相机识别的图像(即您运行程序,在相机上滑动手机,奇迹就会发生),您不能使用与大小无关的东西吗?

如果这是要部署在受控环境中的应用程序,您能否提供校准实用程序? (您可以做一些简单的事情,例如打印带有小尺子的名片,在校准过程中使用它)。

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).

若言繁花未落 2024-07-15 08:42:06

我刚刚找到这个链接:http://dpi.lv/。 基本上,它是一个用于发现客户端设备分辨率、dpi 和屏幕尺寸的网络工具。

我在电脑和手机上访问过,它为我提供了正确的分辨率和 DPI。 它有一个 github 存储库,因此您可以了解它是如何工作的。

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.

桜花祭 2024-07-15 08:42:06

生成已知 DPI 的列表:
https://stackoverflow.com/a/6793227

检测确切的设备。 使用类似的东西:

navigator.userAgent.toLowerCase();

例如,当检测移动设备时:

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

和利润!

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!

海未深 2024-07-15 08:42:06

也许我有点偏离这个话题......
我当时正在开发一个 html canvas 项目,该项目的目的是提供一个绘图画布供人们在上面画线。 我想将画布尺寸设置为198x280mm,适合A4打印。
因此,我开始寻找一种分辨率,将“mm”转换为“px”,并在 PC 和移动设备上合适地显示画布。
我尝试了@Endless的解决方案,代码为:

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;

它在PC浏览器上运行良好,显示dpi=96,大小为748x1058 px;在 PC 上运行良好
然而,转向移动设备,它比我预期的要大得多:尺寸:1902x2689 px。无法使用移动
在搜索了 devicePixelRatio 这样的关键字后,我突然意识到,我实际上不需要在手机屏幕上显示真实的 A4 尺寸(在这种情况下实际上很难使用),我只需要画布的尺寸适合打印,所以我简单地将尺寸设置为:

let [w,h] = [748,1058];
canvas.width = w;
canvas.height = h;

...并且打印良好:打印良好

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文