canvas画图模糊,canvas不清晰

  canvas画图模糊,canvas不清晰

  由于部分移动终端的兼容性问题,我们的一个项目需要前端将pdf转换成可以在移动页面直接查看的界面。为了方便解决问题,我们采用了插件pdf.js,可以将pdf转换成画布,在页面上进行绘制。但在测试过程中发现,绘制的内容在手机浏览器上的显示非常模糊(如下图所示)。分析后发现是移动高清屏幕造成的。解决问题后,记下原因和询问结果。

  在说明问题之前,需要了解一些关于移动显示和cavans的小知识,方便后面的探索。如果想直接看到结果,可以看到最后。

  关于屏幕的一些基础知识

  物理像素(DP)

  物理像素也称为设备像素。我们经常听说手机的分辨率是物理像素。比如iPhone 7的物理分辨率是750 * 1334。屏幕是由像素组成的,也就是说水平方向有750个像素,垂直方向有1334个像素。

  设备独立像素(DIP)

  也称逻辑像素,比如Iphone4和Iphone3gs的尺寸都是3.5英寸,而iphone4的物理分辨率是640 * 980,而3gs的物理分辨率只有320 * 480。如果我们按照真实布局画一个320px宽度的图像,只有一半有iphone4上的内容,其余都是空白的。为了避免这个问题,我们引入了逻辑像素,并设置了两款手机的逻辑像素。

  设备像素比(DPR)

  归根结底,以上设备无关像素是为了计算方便。我们统一了设备的逻辑像素,但是每个逻辑像素所代表的物理像素并不确定。为了在不缩放的情况下确定物理像素和逻辑像素之间的关系,我们引入了设备像素比率(DPR)的概念。

  设备像素比率=设备像素/逻辑像素DPR=DP/DIP。上面提到的理论有很多,这里有一张图来解释一下。

  从上图可以看出,在逻辑像素大小相同的情况下,高清屏幕的物理像素更多。在普通屏幕中,一个逻辑像素对应一个物理像素,而在dpr=2的高清屏幕中,一个逻辑像素由四个物理像素组成。这也是高清屏幕更细腻的原因。

  关于canvas的一些基础知识

  canvas绘制的是位图

  这是每个了解过canvas的人都应该知道的知识点,也是我们接下来要分析的问题核心。

  我们会把位图的解释放在后面。现在我们只需要知道画布绘制的图像是位图。

  canvas的width和height属性

  canvas的宽度和高度属性对于初学者来说非常混乱。这两个属性经常与css中的宽度和高度属性相混淆。

  例如,我们有下面的代码(1):

  画布宽度=600 高度=300 样式=宽度:300pxHeight:150 px /canvas样式中的width和height分别代表界面上画布的宽度和高度。即样式上的宽高属性中的宽度和高度则代表canvas实际像素的宽高如果还是看不懂可以想象成下面的代码(2):

  !-logo.png的像素是600 * 300-img style= width:300 px;height:150 px src= logo . png /canvas默认的宽度和高度是300 * 150。css设置后是canvas会根据设置css宽高进行缩放(注意不是裁剪),和img标签一样。

  实际上,上面的代码(1)可以用一种更通俗的方式来解释,即一个逻辑像素实际上由两个画布像素填充。

  模糊原因的初步探讨

  以上是对所需基础知识的简单介绍,下面是正式的询问。

  首先我们提到了用canvas画图像是位图,我们平时用的jpg和png也是位图。那么什么是位图呢?

  位图又称像素图或光栅图,通过记录图像中各点的颜色、深度、透明度等信息来存储和显示图像。形象地说,你可以把位图想象成一个巨大的拼图。这个拼图有无数块,每一块代表一个纯色像素。理论上,1个位图像素对应着1个物理像素。但是,如果你使用高清屏幕,如苹果的视网膜屏幕来观看图片,会怎么样呢?

  假设我们有以下代码,这些代码将显示在iphone4的视网膜屏幕上:

  画布宽度=320 高度=150 样式=宽度:320px高度:150 px /画布iPhone 4本身的物理像素是640 * 980,而设备的独立像素是320 * 480,也就是说一个css像素实际上是由四个物理像素组成的,画布的像素是320 * 150,它的css像素是320 * 150。那么css像素将由canvas元素组成。这样,在retina screen下,一个画布像素(或位图像素)将被四个物理像素填充。由于单个位图像素无法进一步分割,只能就近上色,导致图片模糊。

  如果有任何疑问,下面的图片可以说明如何在视网膜屏幕下填充位图:

  上图左侧,正常屏幕下有显示规则。可以看到,有4个位图像素,而在右侧,高清屏幕下有16个像素。因为像素不能切,所以颜色变了。

  但是有一点没有解释清楚,就是为什么图片是附近着色而不是直接原值,这也是模糊的背后。

  幕后黑手---平滑处理技术

  以下是我一个大同学的解释。刚才我们说每个位图元素其实都是一个纯色像素。现在假设我们需要在一个css大小为4px 4px,dpr为1的普通屏幕上绘制一个数字“0”,那么我们的绘制应该是下图这样的,其中1代表黑色像素,0代表白色像素。

  可以看出,在dpr比较小的时候,我们的“0”格局还是比较明显的。现在,如果我们的css的大小不变,但是图像被绘制在视网膜屏幕上,效果会是什么?

  我们知道,在视网膜屏幕下,一个css像素代表四个物理像素。如果我们不做任何处理,直接按照上面的排列矩阵,展开矩阵,会发现在视网膜屏幕下,我们的图案锯齿感非常明显,图像明显缺少一丝平滑。

  如果我们稍微改变一下图像,就改成下图。

  图像瞬间变得柔和,但本该填四个零的地方变成了三个一加一个零。其实这就是所谓的图像平滑处理。为了解决锯齿感,改变了原来的颜色。为了更加自然,图片的接缝处变成了近似色,这也解释了为什么颜色是用近似色填充而不是原色。

  原因总结

  通过上面的解释,现在我们来总结一下以下结论:现在移动终端上高清屏幕的普及基本已经普及,1px css像素实际上代表的是四个或者更多的物理像素。但是由于我们的代码问题,我们的1px css像素和一个canvas像素是相等的,这就导致了一个canvas像素实际上需要填充四个或者更多的物理像素。为了保证图像的平滑处理,在填充剩余的物理像素时使用原始颜色的近似值,导致图像模糊。

  解决思路

  知道了问题的原因,解决问题就容易了。解决这个问题最重要的一点就是让一个画布像素等于一个物理像素。

  高层浏览器的窗口对象下挂着一个devicePixelRatio属性,就是上面说的dpr。

  当画布元素css的宽度和高度确定后,我们就可以这样做了。

  let canvas=document . getelementbyid( canvas );let CTX=canvas . get context( 2d );设dpr=window.devicePixelRatio//假设dpr为2//获取css let的宽度和高度{width: CSS width,height:CSS height }=canvas . getboundingclient();//根据dpr,扩展画布的像素,使1个画布像素和1个物理像素相等canvas.width=dpr * cssWidthcanvas.height=dpr * cssHeight//随着画布的展开,画布的坐标系也随之展开。如果绘图内容与原始坐标系一致,它将收缩。//所以需要用ctx.scale(dpr,dpr)放大绘图比例;经验总结

  很多时候,我们发现了问题,与其专注于解决问题,不如深入到问题产生的原因,这样才能更好的在这个事业上走下去。

  这就是本文的全部内容。希望对大家的学习和支持有帮助。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: