【问题标题】:<canvas> at native resolution despite zooming, Retina<canvas> 尽管缩放,但仍保持原始分辨率,Retina
【发布时间】:2013-10-14 16:15:24
【问题描述】:

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

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

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

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

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

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

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

【问题讨论】:

    标签: css html canvas zooming retina-display


    【解决方案1】:

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

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

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

    Source

    【讨论】:

    • 我知道这一点。它在link in my question 中。也就是说,我不理解对 devicePixelRatio 缩放的仇恨。当然,它使确定缩放变得困难,但由于缩放实际上不是这里的问题,并且这种行为使得现有的 Retina 感知画布代码在缩放下工作(模必须检查调整大小事件上的 devicePixelRatio 变化),我认为它是好东西。
    • 显然 Chrome 31 也将实现这一点,将这种行为带到所有 Firefox、Chrome、Opera 和 IE。不过,Apple 是 against it,或者至少是一年前。
    • 这一切都可能归结为一个简单的原因 - 可用性。在大多数情况下,浏览器中实现放大/缩小的方式会不可预测地影响所有 HTML 元素和结构,而不仅仅是画布。如果所有 Web 开发人员都必须考虑他们的网站在不同浏览器缩放条件下的外观,加上跨浏览器、跨平台以及所有可能的响应式布局,那么开发满足所有这些条件的网站将非常困难。
    • 我建议实现某种滑块或按钮,允许最终用户操纵其&lt;canvas&gt; 元素的大小,这将使用户可以选择是否要保留完整的画布大小,更大或更小。
    【解决方案2】:

    我发现(对于 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() 函数,我们可以计算窗口的真实大小,以“物理像素”为单位。当我们需要将画布的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
    

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

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

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

    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 之外没有其他浏览器支持告诉你:

    • 设备 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) 处的 32 像素框

    错了。你需要画一个

    在 (200,200) 处的 128 像素框

    当缩放系数为 4 时。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多