SVG渲染到画布上的视网膜显示模糊

Gna*_*ato 5 javascript svg canvas blurry retina

我在将SVG渲染到画布中时遇到问题。在视网膜显示器上,渲染为base64 url​​并设置为SRC的画布是模糊的。

在此处输入图片说明

我尝试了下面列表中描述的各种方法,但没有走运:

现在我不知道该怎么做才能使它更好。请调查我的结果:jsfiddle.net/a8bj5fgj/2/

编辑:

更新了修正的小提琴:jsfiddle.net/a8bj5fgj/7/

Bli*_*n67 9

Retina显示屏

视网膜和超高分辨率显示器的像素尺寸小于人眼平均可以分辨的像素尺寸。渲染一条线最终看起来像一条较亮的线。为了修复涉及检测高分辨率显示的页面的问题,会将默认 CSS 像素大小更改为 2。

DOM 知道这一点并调整其渲染来进行补偿。但 Canvas 并不知道,它的渲染只是按比例放大了。画布的默认显示渲染类型是双线性插值。这可以平滑从一个像素到下一个像素的过渡,这对于照片来说非常有用,但对于线条、文本、SVG 等来说就不太好了。

一些解决方案

  • 首先是在画布上关闭双线性过滤。这可以通过 CSS 规则来完成,image-rendering: pixelated;尽管这不会创建在 DOM 上渲染的 SVG 的质量,但会减少某些用户体验到的模糊外观。

  • 将 SVG 渲染到画布时,您应该关闭图像平滑,因为这会降低 svg 图像的质量。SVG 在内部渲染,当内部副本渲染到画布上时不需要额外的平滑。

    去做这个ctx.imageSmoothingEnabled = false;

  • 检测 CSS 像素大小。window 变量devicePixelRatio返回 CSS 像素的大小与实际屏幕物理像素大小的比较。Retina 和高分辨率设备的值通常为 2。然后您可以使用它来设置画布分辨率以匹配物理像素分辨率。

    但有一个问题,因为devicePixelRatio并非所有浏览器都支持,并且devicePixelRatio受页面缩放设置的影响。

    因此,在最基本的使用devicePixelRatio和假设下,很少有人放大到 200% 以上。

代码 假设 和canvas.style.widthcanvas.style.height正确设置。

if(devicePixelRatio >= 2){        
    canvas.width *= 2;
    canvas.height *= 2;
}
Run Code Online (Sandbox Code Playgroud)

现在您已经增加了分辨率,您还必须增加渲染尺寸。这可以通过画布变换来完成,最好将其创建为函数。

function setCanvasForRetina(canvas){
    canvas.width *= 2;
    canvas.height *= 2;
    canvas.setTransform(2,0,0,2,0,0);
}
Run Code Online (Sandbox Code Playgroud)

注意,我不会将像素大小增加“devicePixelRatio”的值,这是因为视网膜设备的分辨率仅为 2 倍,如果宽高比大于 2,则因为客户端已放大。为了满足预期如果可以的话,我不会调整画布的行为缩放设置。尽管这不是规则,只是建议。

更好的猜测

以上两种方法要么是权宜之计,要么是简单的猜测。您可以通过检查某些系统来提高胜算。

目前,视网膜显示屏针对一组固定的设备(手机、平板电脑、笔记本电脑)提供一组固定的分辨率。

您可以查询window.screen.widthwindow.screen.height来确定绝对物理像素分辨率,并将其与已知的视网膜显示分辨率进行匹配。您还可以查询 userAgent 以确定设备类型和品牌。

将所有这些放在一起,您可以改进猜测。下一个函数会猜测显示器是否是视网膜显示器。您可以使用类似的方法来确定设备是否是视网膜设备,然后相应地增加画布分辨率。

在wiki Retina Display Models中找到以下代码的信息如果您想保持最新,可以使用 Wiki 的 SPARQL 界面机器查询此信息。

演示猜猜视网膜。

if(devicePixelRatio >= 2){        
    canvas.width *= 2;
    canvas.height *= 2;
}
Run Code Online (Sandbox Code Playgroud)
function setCanvasForRetina(canvas){
    canvas.width *= 2;
    canvas.height *= 2;
    canvas.setTransform(2,0,0,2,0,0);
}
Run Code Online (Sandbox Code Playgroud)
rWidth.textContent = screen.width
rHeight.textContent = screen.height

aWidth.textContent = screen.availWidth
aHeight.textContent = screen.availHeight

pWidth.textContent = innerWidth
pHeight.textContent = innerHeight

dWidth.textContent = document.body.clientWidth
dHeight.textContent = document.body.clientHeight

//doWidth.textContent = document.body.offsetWidth
//doHeight.textContent = document.body.offsetHeight

//sWidth.textContent = document.body.scrollWidth
//sHeight.textContent = document.body.scrollHeight

pAspect.textContent = devicePixelRatio

userA.textContent = navigator.userAgent



function isRetina(){
  // source https://en.wikipedia.org/wiki/Retina_Display#Models
  var knownRetinaResolutions = [[272,340], [312,390], [960,640], [1136,640 ], [1334,750 ], [1920,1080], [2048,1536], [2732,2048], [2304,1440], [2560,1600], [2880,1800], [4096,2304], [5120,2880]];
  var knownPhones =  [[960,640], [1136,640 ], [1334,750 ], [1920,1080]];
  var knownPads =  [[2048,1536], [2732,2048]];
  var knownBooks = [[2304,1440], [2560,1600], [2880,1800], [4096,2304], [5120,2880]];

  var hasRetinaRes = knownRetinaResolutions.some(known => known[0] === screen.width && known[1] === screen.height);
  var isACrapple = /(iPhone|iPad|iPod|Mac OS X|MacPPC|MacIntel|Mac_PowerPC|Macintosh)/.test(navigator.userAgent);
  var hasPhoneRes =  knownPhones.some(known => known[0] === screen.width && known[1] === screen.height);
  var isPhone = /iPhone/.test(navigator.userAgent);
  var hasPadRes =  knownPads.some(known => known[0] === screen.width && known[1] === screen.height);
  var isPad = /iPad/.test(navigator.userAgent);
  var hasBookRes =  knownBooks.some(known => known[0] === screen.width && known[1] === screen.height);
  var isBook = /Mac OS X|MacPPC|MacIntel|Mac_PowerPC|Macintosh/.test(navigator.userAgent);

  var isAgentMatchingRes = (isBook && hasBookRes && !isPad && !isPhone) ||
      (isPad && hasPadRes && !isBook && !isPhone) ||
      (isPhone && hasPhoneRes && !isBook && !isPad)
  return devicePixelRatio >= 2 && 
         isACrapple && 
         hasRetinaRes && 
          isAgentMatchingRes;
}

guess.textContent = isRetina() ? "Yes" : "No";
    
Run Code Online (Sandbox Code Playgroud)

从你的片段

这可能会做你想做的事。由于我没有任何苹果产品,除了在 isRetina 上强制 true 之外,我无法测试它。

div, h1, span {
  font-family : arial;
}
span {
  font-weight : bold
}
Run Code Online (Sandbox Code Playgroud)
<div class="r-display" id="info">
  <h1>System info</h1>
  <div>Device resolution : 
    <span id = "rWidth"></span> by <span id = "rHeight"></span> pixels
  </div>
  <div>Availabe resolution : 
    <span id = "aWidth"></span> by <span id = "aHeight"></span> pixels
  </div>
  <div>Page resolution : 
    <span id = "pWidth"></span> by <span id = "pHeight">  </span> CSS pixels
  </div>
  <div>Document client res : 
    <span id = "dWidth"></span> by <span id = "dHeight">  </span> CSS pixels
  </div>
  <div>Pixel aspect : 
    <span id = "pAspect"></span>
  </div>
  <div>User agent :
    <span id="userA"></span>
  </div>
  <h3>Best guess is retina "<span id = "guess"></span>!"</h3>
</div>
  
Run Code Online (Sandbox Code Playgroud)

请注意。

大多数视力为 6/6(帝国国家为 20/20)的人将很难看出画布稍微模糊的显示和清晰的 DOM 之间的差异。你应该问自己,你需要仔细观察才能确定吗?在正常观看距离下你能看到模糊吗?

此外,有些人将显示缩放至 200% 是出于充分的理由(视力受损),并且不会欣赏您规避他们的设置。