尽管缩放,仍然是原始分辨率的<canvas>,Retina </canvas>

时间:2013-10-07 22:27:01

标签: css html5 canvas zoom retina-display

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

a few things 我见过“Retina”显示器(结论:devicePixelRatio是你的朋友),但他们都假设以100%变焦观看。同样我已经看到了关于detecting zoom level的事情,但它看起来很混乱,黑客和不精确 (更不用说prone to breakage)。特别是,不精确是一个问题,因为整个要点是精确地比例为1:1。 (另外,我实际上并不关心缩放级别本身,只关心寻址原生像素。)

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

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

  2. 如果我不关心资源消耗,那么2:1画布(“Retina”显示器上的原生res,其他地方放大200%)值得考虑吗?我假设缩小比升级更糟糕。

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

  4. 有多少人定期浏览网页!100%缩放?有多少人使用高DPI显示器?有统计数据吗? [已回答here:约有10%的Gmail用户。]

2 个答案:

答案 0 :(得分:3)

我发现(对于Chrome和Internet Explorer)是

之间非常棘手,微妙的区别
  • canvas.width
  • canvas.style.width

它们不可互换。如果你想操纵它们,你必须非常小心地去实现你想要的。某些值以物理像素衡量。其他的以逻辑像素(如果屏幕仍然缩放到96 dpi则为像素)进行测量。

在我的情况下,我希望Canvas占据整个窗口。

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

//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()功能,我们可以在“物理像素”中计算窗口的实际大小。当我们需要将画布的widthheight设置为物理像素中窗口的大小时:

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

关键技巧是画布内部将渲染到widthheight像素大小的曲面。渲染完成后,CSS可以出现并收缩或拉伸画布:

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

最后一部分是调整画布的大小,使其占据整个客户区窗口,使用 CSS 长度字符串

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

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

GetZoomLevel()

是的,您需要缺少的部分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以外的其他浏览器都没有告诉你:

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

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

  

一个32px的盒子(50,50)

错了。你需要画一个

  

128 px框(200,200)

当缩放系数为4时。

  

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

答案 1 :(得分:0)

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

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

在Chrome 27上(含义WebKit和Blink)webkitTextSizeAdjust在浏览器的桌面版本上已被弃用。这是我所知道的唯一能够检测桌面镶边放大的防弹方法。 还有其他几种方法,但它们并不涵盖所有基础 - 一个使用SVG但不在iFrame中工作,另一个使用window.inner / outerWidth并且当有侧边栏或DevTools打开时无法工作一边。

Source