【Canvas】:High DPI 下的 Canvas 模糊问题

2021-01-18 14:45:06 浏览数 (1)

代码语言:javascript复制
1. 起因?
2. 基础?
  2.1. window.devicePixelRatio
  2.2. Sizing the canvas using CSS versus HTML
3. 解决方案?

1. 起因?

  • 用 Canvas 绘制了一个标尺组件;
  • 4K 屏做的测试;
    • 处于 1.5 倍缩放模式下
  • 发现文字显示效果很模糊;

2. 基础?

2.1. window.devicePixelRatio

The devicePixelRatio of Window interface returns the ratio of the resolution in physical pixels to the resolution in CSS pixels for the current display device. This value could also be interpreted as the ratio of pixel sizes: the size of one CSS pixel to the size of one physical pixel. In simpler terms, this tells the browser how many of the screen's actual pixels should be used to draw a single CSS pixel.

  • This is useful when dealing with the difference between rendering on a standard display versus a HiDPI or Retina display, which use more screen pixels to draw the same objects, resulting in a sharper image.
  • A double-precision floating-point value indicating the ratio of the display's resolution in physical pixels to the resolution in CSS pixels. A value of 1 indicates a classic 96 DPI (76 DPI on some platforms) display, while a value of 2 is expected for HiDPI/Retina displays. Other values may be returned as well in the case of unusually low resolution displays or, more often, when a screen has a higher pixel depth than double the standard resolution of 96 or 76 DPI.

2.2. Sizing the canvas using CSS versus HTML

The displayed size of the canvas can be changed using CSS, but if you do this the image is scaled during rendering to fit the styled size, which can make the final graphics rendering end up being distorted.

It is better to specify your canvas dimensions by setting the width and height attributes directly on the <canvas> elements, either directly in the HTML or by using JavaScript.

示例:

代码语言:javascript复制
<!DOCTYPE html>
<html>
<head>
  <title></title>
</head>
<body>
  <canvas id="c1" width="500" height="500" style="border:1px solid blue;"></canvas>
  <canvas id="c2" style="width:500px;height:500px; border: 1px solid green;"></canvas>
  <script type="text/javascript">
    function drawTriangle(ctx){
      ctx.beginPath();
      ctx.moveTo(75, 50);
      ctx.lineTo(200, 120);
      ctx.lineTo(230, 16);
      ctx.fill();
    }

    drawTriangle(document.querySelector("#c1").getContext("2d"));
    drawTriangle(document.querySelector("#c2").getContext("2d"));
</script>
</body>
</html>

3. 解决方案?

Enabling your canvas to appear crisp on Retina as well as standard-definition displays is as simple as multiplying your canvas instructions by a ratio determined by the screen’s pixel density. First, you need to understand how pixel values are stored in a canvas.

The backing store is where the canvas stores data for each pixel’s color value. The goal is to provide a pixel in the backing store for each display pixel rendered on the canvas. Before the pixels are pushed to the screen, their values are computed here. However, the number of pixels represented in the backing store might not be equal to the number of pixels pushed to the screen. On Retina devices, the canvas width and height is doubled to maintain consistent size and position relative to other HTML elements, and as a result, it stretches and blurs its contents. To counteract this stretching, you need to double the width and height of the backing store when appropriate.

  • Retina devices have a pixel ratio of 2 because there is a 2:1 ratio of display pixels to backing store pixels in both the x and y direction.Standard-resolution displays, on the other hand, map one backing store pixel to one display pixel, so their device pixel ratio will always be 1.
代码语言:javascript复制
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

// Set display size (css pixels).
var size = 200;
canvas.style.width = size   "px";
canvas.style.height = size   "px";

// Set actual size in memory (scaled to account for extra pixel density).
var scale = window.devicePixelRatio; // Change to 1 on retina screens to see blurry canvas.
canvas.width = Math.floor(size * scale);
canvas.height = Math.floor(size * scale);

// Normalize coordinate system to use css pixels.
ctx.scale(scale, scale);

ctx.fillStyle = "#bada55";
ctx.fillRect(10, 10, 300, 300);
ctx.fillStyle = "#ffffff";
ctx.font = '18px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';

var x = size / 2;
var y = size / 2;

var textString = "I love MDN";
ctx.fillText(textString, x, y);

因此,要使 canvas 适配高倍屏:

  • 将 canvas 放大到设备像素比来绘制;
  • 然后将 canvas 压缩成一倍的物理大小来展示;
  • 并且将 canvas中的线条大小、文字大小等都需要乘以设备像素比来进行绘制,否则高倍屏下的线条会变细几倍;

参考:

Window.devicePixelRatio: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio Support Retina Displays From the Start: https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/HTML-canvas-guide/SettingUptheCanvas/SettingUptheCanvas.html High DPI Canvas: https://www.html5rocks.com/en/tutorials/canvas/hidpi/

0 人点赞