css - <canvas> 尽管缩放,Retina 仍以原始分辨率显示

标签 css html canvas zooming retina-display

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

a few things 我见过“Retina”显示器(结论:devicePixelRatio 是你的 friend ),但它们都假设以 100% 缩放观看。同样,我也看到了关于 detecting zoom level 的事情, 但它看起来凌乱、笨拙且不精确 (更不用说 prone to breakage 了)。特别是,不精确是一个问题,因为关键是要获得精确的 1:1 比率。 (另外,我实际上并不关心缩放级别本身,只关心处理原生像素。)

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

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

  2. 如果我不关心资源消耗,是否值得考虑 2:1 Canvas (“Retina”显示器上的原生分辨率,其他地方高达 200% 的缩放比例)?我假设缩小规模没有扩大规模那么糟糕。

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

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

最佳答案

我发现(对于 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() 函数,我们可以计算窗口的实际 大小,以“物理像素” 为单位。当我们需要将 Canvas 的宽度高度设置为以物理像素为单位的窗口大小时:

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

关键的技巧是在内部 Canvas 将渲染到一个大小为 width x height 像素的表面。渲染完成后,CSS 会出现并缩小或拉伸(stretch)您的 Canvas :

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

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

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

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

获取缩放级别()

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

  • 设备分辨率
  • 逻辑分辨率

我的完整 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;
    }
}

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

a 32px box at (50,50)

错了。你需要画一个

128 px box at (200,200)

当缩放系数为 4 时。

Note: Any code is released into the public domain. No attribution required.

关于css - <canvas> 尽管缩放,Retina 仍以原始分辨率显示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19235866/

相关文章:

javascript - 根据您的变量定位一个 div

firefox - *|* 这在 CSS 中意味着什么?

javascript - 使用 JS 生成 HTML 元素列表。 addEventListener 仅适用于最后一个元素

html - CSS 下拉菜单在 IE 和 Firefox 中不起作用

javascript - 在 Facebook 上分享数据 uri 图片

javascript - 为 Canvas 库创建 mousemove 事件

javascript - 如果下拉菜单位于浏览器窗口的底部,则使下拉菜单向上移动,根据位置自动在向下和向上之间切换

html - 嵌套 Flexbox 扩展问题

html - td 中的多行

javascript - 生成并下载网页截图而不丢失样式