3

所以我有一个 <canvas> 元素,我在其中绘制东西。矢量的东西,而不是像素推动。我希望它在最大范围的观看条件下以最少的资源以最高质量显示。具体来说,我想确保画布像素和设备像素之间存在 1:1 映射,以消除模糊、耗时的像素缩放操作。

我 见过一些关于“Retina”显示器 事情(结论:devicePixelRatio 是你的朋友),但它们都假设以 100% 缩放观看。同样,我也看到过有关检测缩放级别的事情,但它看起来很混乱,很笨拙且不精确(更不用说容易损坏)。特别是,不精确是一个问题,因为整个要点是要获得精确的 1:1 比率。(另外,我实际上并不关心缩放级别本身,只关心处理原生像素。)

所以我的问题,从务实到哲学:

  1. 在现代(== 过去 6 个月内发布)浏览器中获得 1:1 画布的首选方法是什么?我真的必须检测设备像素比缩放(包括检查缩放变化)并从那里计算吗?

  2. 如果我不关心资源消耗,是否值得考虑使用 2:1 画布(“Retina”显示器上的原生分辨率,其他地方最多 200% 缩放)?我假设缩小规模不如扩大规模。

  3. 浏览器供应商对此有何看法?是否有更好的解决方案正在研究中?还是缩放是他们认为 Web 开发人员无需担心的细节?[这里部分回答:许多人认为这很重要。关于 devicePixelRatio 是否应该随缩放级别而变化,或者允许用户指定的缩放检测以及设备特定的 DPI 检测的新属性是否是前进的方向,似乎有些犹豫不决。]

  4. 有多少人经常以 !100% 缩放浏览网页?他们中有多少人使用高 DPI 显示器?有统计吗?[在这里回答:大约 10% 的 gmail 用户。]

4

2 回答 2

3

我发现(对于 Chrome 和 Internet Explorer)是非常棘手、微妙的区别

  • canvas.width, 和
  • canvas.style.width

它们不可互换。如果你想操纵它们,你必须非常小心地这样做才能达到你想要的。有些值是以物理像素为单位测量的。其他的以逻辑像素(如果屏幕仍缩放到 96 dpi 时的像素)来衡量。

就我而言,我希望Canvas占据整个窗口。

首先获取以逻辑像素为单位的窗口大小(我称之为“CSS 像素”):

//get size in CSS pixels (i.e. 96 dpi)
var styleWidth = window.innerWidth;   //e.g. 420. Not document.body.clientWidth
var styleHeight = window.innerHeight; //e.g. 224. Not document.body.clientHeight

这些值以“逻辑像素”为单位。当我的浏览器放大时,“逻辑像素”的数量会减少:

Zoom  window.innerWidth x windows.innerHeight
====  ======================================
100%  1680 x  859
200%   840 x  448
400%   420 x  224
 75%  2240 x 1193

然后你要做的是找出窗口的“真实”大小;应用缩放校正因子。目前,我们将抽象出可以计算当前缩放级别的函数(使用 TypeScript 语法):

function GetZoomLevel(): number
{
}

使用该GetZoomLevel()函数,我们可以计算窗口的实际大小,以“物理像素”为单位。当我们需要将画布的widthand设置为以物理像素为单位height的窗口大小时:

//set canvas resolution to that of real pixels
var newZoomLevel = GetZoomLevel();
myCanvas.width = styleWidth * newZoomLevel;   //e.g. 1680. Not myCanvas.clientWidth
myCanvas.height = styleHeight * newZoomLevel; //e.g. 859.  Not myCanvas.clientHeight

关键的技巧是在内部,画布将渲染到一个像素大小width的表面。height渲染完成后,CSS 可以出现并缩小或拉伸您的画布:

  • 如果 CSS 使它变大,则模糊它
  • 如果 CSS 使其更小,则丢弃像素数据。

最后一步是调整画布的大小,使其占据整个客户区窗口,使用CSS长度String

myCanvas.style.width = styleWidth + "px";   //e.g. "420px"
myCanvas.style.height = styleHeight + "px"; //e.g. "224px"

因此画布将在您的浏览器窗口中正确定位和调整大小,但在内部使用完整的“真实”像素分辨率。

获取缩放级别()

是的,您需要缺少的部分,GetZoomLevel. 不幸的是,只有 IE 提供信息。同样,使用 TypeScript 表示法:

function GetZoomLevel(): number {
    /*
        Windows 7, running at 131 dpi (136% zoom = 131 / 96)
            Internet Explorer's "default" zoom (pressing Ctrl+0) is 136%; matching the system DPI
                screen.deviceYDPI: 130
                screen.systemYDPI: 131
                screen.logicalYDPI: 96

            Set IE Zoom to 150%
                screen.deviceYDPI: 144
                screen.systemYDPI: 131
                screen.logicalYDPI: 96

        So  a user running their system at 131 dpi, with IE at "normal" zoom,
        and a user running their system at  96 dpi, with IE at 136% zoom,
        both need to see the same thing; everything zoomed in from our default, assumed, 96dpi.

        http://msdn.microsoft.com/en-us/library/cc849094(v=vs.85).aspx

    Also note that the onresize event is triggered upon change of zoom factor, therefore you should make sure 
    that any code that takes into account DPI is executed when the onresize event is triggered, as well.

    http://htmldoodads.appspot.com/dimensions.html
    */


    var zoomLevel: number;

    //If the browser supports the corrent API, then use that
    if (screen && screen.deviceXDPI && screen.logicalXDPI)
    { 
        //IE6 and above
        zoomLevel = (screen.deviceYDPI / screen.logicalYDPI);
    else
    {
        //Chrome (see http://htmldoodads.appspot.com/dimensions.html)
        zoomLevel = window.outerWidth / window.innerWidth; //e.g. 1680 / 420
    }

    return zoomLevel;
}

不幸的是,除了 IE 之外没有其他浏览器支持告诉你:

  • 设备 dpi
  • 逻辑dpi

我的完整 TS 代码是:

function OnWindowResize() {
    if (drawingTool == null)
        return;

    //Zoom changes trigger window resize event

    //get size in CSS pixels (i.e. 96 dpi)
    var styleWidth = window.innerWidth; //myCanvas.clientWidth;
    var styleHeight = window.innerHeight; //myCanvas.clientHeight;

    var newZoomLevel = GetZoomLevel();

    //    myCanvas.width = document.body.clientWidth;
    //    myCanvas.height = document.body.clientHeight;

    //set canvas resolution to that of real pixels
    myCanvas.width = styleWidth * newZoomLevel;
    myCanvas.height = styleHeight * newZoomLevel;
    //myCanvas.clientWidth = styleWidth;
    //myCanvas.clientHeight = styleHeight;

    myCanvas.style.width = styleWidth + "px"; //styleWidth.toString() + "px";
    myCanvas.style.height = styleHeight + "px"; //styleHeight.toString() + "px";

    drawingTool.Width = myCanvas.width;
    drawingTool.Height = myCanvas.height;

    // Only take action if zoom factor has changed (won't be triggered by actual resize of window)
    if (newZoomLevel != lastZoomLevel) {
        lastZoomLevel = newZoomLevel;
        drawingTool.CurrentScaleFactor = newZoomLevel;
    }
}

告诉绘图代码用户当前的缩放级别也很重要,以防您尝试对任何长度进行硬编码。例如尝试画画

(50,50) 处的 32px 框

是错的。你需要画一个

128 像素框在 (200,200)

当缩放系数为 4 时。

注意:任何代码都会发布到公共领域。无需归属。

于 2013-10-31T19:36:59.210 回答
0

您基本上无法在现代浏览器版本的 Firefox 和 Chrome 上检测缩放级别:

在 Firefox 18+ 上,Mozilla 在手动缩放 (cmd/ctrl +/-) 上更改了 devicePixelRatio 值,从而无法知道浏览器是处于缩放模式还是视网膜设备,而忽略了 DEVICE 一词所代表的含义。

在 Chrome 27(意味着 WebKit 和 Blink)上,桌面版本的浏览器已弃用 webkitTextSizeAdjust。这是我所知道的检测桌面 chrome 放大的唯一防弹方法。还有其他几种方法,但它们并没有涵盖所有基础 - 一种使用 SVG 但在 iFrames 中不起作用,另一种使用 window.inner/outerWidth 并且在有侧边栏或打开 DevTools 时不起作用旁边。

资源

于 2013-10-08T10:07:40.720 回答