深入了解canvas在移动端绘制模糊的问题解决


Posted in HTML / CSS onApril 30, 2019

由于一些移动端的兼容性原因,我们某个项目需要前端将pdf转换成在移动端页面可直接观看的界面。为了方便解决,我们采用了pdf.js这个插件,该插件可以将pdf转换成canvas绘制在页面上。不过,在测试过程中却发现,在移动端的浏览器上, 绘制的内容展示十分模糊(如下图),经过分析之后发现是由于移动端高清屏幕引起的。 在解决问题之后以文字方式记述原因和探究结果

深入了解canvas在移动端绘制模糊的问题解决

在解释问题之前,首先需要了解一些移动端显示和cavans的小知识,方便后面探究。如果想直接看结果的话看可以拉到最后。

关于屏幕的一些基础知识

物理像素(DP)

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

设备独立像素(DIP)

也称为逻辑像素,比如Iphone4和Iphone3GS的尺寸都是3.5寸,iphone4的物理分辨率是640 * 980,而3gs只有320 * 480,假如我们按照真实布局取绘制一个320px宽度的图像时,在iphone4上只有一半有内容,剩下的一半则是一片空白,为了避免这种问题,我们引入了逻辑像素,将两种手机的逻辑像素都设置为320px,方便绘制

设备像素比(DPR)

上面的设备独立像素说到底是为了方便计算,我们统一了设备的逻辑像素,但是每个逻辑像素所代表的物理像素却不是确定的,为了确定在未缩放情况下,物理像素和逻辑像素的关系,我们引入了设备像素比(DPR)这个概念

设备像素比 = 设备像素 / 逻辑像素
DPR = DP / DIP

上面说了很多理论,下面附个图解释一下

深入了解canvas在移动端绘制模糊的问题解决

从上面的图可以看出,在同样大小的逻辑像素下,高清屏所具有的物理像素更多。普通屏幕下,1个逻辑像素对应1个物理像素,而在dpr = 2的高清屏幕下,1个逻辑像素由4个物理像素组成。这也是为什么高清屏更加细腻的原因。

关于canvas的一些基础知识

 canvas绘制的是位图

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

关于位图的解释我们放在后面,现在我们只要知道canvas绘制的图像是位图。

canvas的width和height属性

canvas的width和height属性是初学者非常容易搞错的内容。这两个属性经常会与css中的width和height属性混淆。

比如我们有如下代码(1):

<canvas width="600" height="300" style="width: 300px; height: 150px"></canvas>
  • style中的width和height分别代表canvas这个元素在界面上所占据的宽高, 即样式上的宽高
  • attribute中的width和height 则代表canvas实际像素的宽高

如果还无法理解的话,可以想象成以下的代码(2):

<!-- logo.png的像素为600 * 300  -->
<img  style="width: 300px; height: 150px" src="logo.png" />

canvas默认的width和height是300 * 150,对其设置了css之后, canvas会根据设置css宽高进行缩放(注意不是裁剪) ,这一点和img标签一样

上述代码(1)其实还可以再换一种更通俗的解释方式,就是1个逻辑像素实际上由2个canvas像素填充。

模糊原因的初步探讨

上面是对所需基础知识的一些简介,下面开始正式进行探究。

首先我们提到使用canvas绘制图像的是位图,而我们平常用的jpg,png也是位图。那么什么是位图?

位图又叫像素图或栅格图,它是通过记录图像中每一个点的颜色、深度、透明度等信息来存储和显示图像。具象一点讲,可以将位图想象成一个巨大的拼图,这个拼图有无数的拼块,每个拼块代表了一个纯色的像素点。 理论上,1个位图像素对应着1个物理像素 。但假如说你使用了高清屏,比如苹果的retina屏去查看一幅图画,又会是什么样子呢?

假设我们有如下代码,该代码将展示在iphone4的retina屏上:

<canvas width="320" height="150" style="width: 320px; height: 150px"></canvas>

iphone4本身的物理像素为640 * 980,而设备独立像素为320 * 480,这代表着1个css像素实际由4个物理像素构成,canvas的像素为320 * 150,其css像素为320 * 150,则代表1个css像素将会由1个canvas元素构成,这样进行换算, 在retina屏幕下,1个canvas像素(或者说是1个位图像素)将会填充4个物理像素,由于单个位图像素不可以再进一步分割,所以只能就近取色,从而导致图片模糊 。

如果还有疑惑的话,以下的图片可以说明位图在retina屏幕下是如何填充的:

深入了解canvas在移动端绘制模糊的问题解决

上图中左侧的是在普通屏幕下的显示规则,可以看出有4个位图像素点,而右侧的高清屏幕下则有16个像素点。由于像素点不可切割的原因,颜色产生了改变。

但是还有一点没有解释清楚,就是为什么图片会就近取色而不是直接取原值,这也是导致模糊的幕后黑手。

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

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

深入了解canvas在移动端绘制模糊的问题解决

可以看出在dpr比较小的情况下,我们的“0”这个图案还比较明显,现在假如我们css大小不变,但是改成在retina屏幕下绘制图像,效果又会变成什么样呢?

深入了解canvas在移动端绘制模糊的问题解决

我们已知在retina屏幕下,一个css像素代表4个物理像素,假如我们不做任何处理,直接按照上面矩阵排列,将矩阵扩大的话,会发现在retina屏幕下,我们的图案锯齿感非常明显,图像明显缺乏了一丝顺化。

假如我们对图像稍作改变,改成下图这样

深入了解canvas在移动端绘制模糊的问题解决

图像感觉瞬间柔和了, 但是原本应该4个0充斥的地方变成了3个1加上1个0。这其实就是所谓的图像平滑处理,为了解决锯齿感觉,将原本的颜色改变,在充斥着较多颜色的图片上,为了更自然,图片的连接处变成了近似的颜色,这也解释了为什么上面填充颜色的时候不是使用本色而是使用近似色。

原因总结

通过了上述的解释,现在我们来总结以下结论,在移动端盛行,高清屏基本上已经普及的现在,1px的css像素实际上代表了4个甚至更多的物理像素。但是由于我们的代码问题,我们1px的css像素和1个canvas像素画上了等号,也就导致了1个canvas像素实际需要填充4个甚至更多物理像素,为了保证图像平滑处理,在填充剩余的物理像素时采用了原先颜色的近似值,导致了图像的模糊。

解决思路

了解了问题出现的原因,解决问题就很容易,解决该问题最重要的一点是让1个canvas像素和一个物理像素挂等号

高版本的浏览器的window对象下都挂着一个devicePixelRatio属性,该属性就是上面所说的dpr,

在canvas元素css宽高确定的情况下,我们可以这么做

let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let dpr = window.devicePixelRatio; // 假设dpr为2
// 获取css的宽高
let { width: cssWidth, height: cssHeight } = canvas.getBoundingClientRect();
// 根据dpr,扩大canvas画布的像素,使1个canvas像素和1个物理像素相等
canvas.width = dpr * cssWidth;
canvas.height = dpr * cssHeight;
// 由于画布扩大,canvas的坐标系也跟着扩大,如果按照原先的坐标系绘图内容会缩小
// 所以需要将绘制比例放大
ctx.scale(dpr,dpr);

经验总结

很多时候,我们发现了问题,不能之集中于问题的解决,而是应该深入去了解问题发生的原因,这样才能更好的在这行走下去。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

HTML / CSS 相关文章推荐
css3新增颜色表示方式分享
Apr 15 HTML / CSS
基于CSS3制作立体效果导航菜单
Jan 12 HTML / CSS
css3 盒模型以及box-sizing属性全面了解
Sep 20 HTML / CSS
HTML5 Canvas 破碎重组的视频特效的示例代码
Sep 24 HTML / CSS
html5指南-2.如何操作document metadata
Jan 07 HTML / CSS
HTML5 canvas基本绘图之图形组合
Jun 27 HTML / CSS
HTML5响应式(自适应)网页设计的实现
Nov 17 HTML / CSS
高清屏下canvas重置尺寸引发的问题的解决
Oct 14 HTML / CSS
5分钟实现Canvas鼠标跟随动画背景
Nov 18 HTML / CSS
CSS3实现三角形不断放大效果
Apr 13 HTML / CSS
在CSS中映射鼠标位置并实现通过鼠标移动控制页面元素效果(实例代码)
Apr 22 HTML / CSS
HTML基础详解(下)
Oct 16 HTML / CSS
详解使用双缓存解决Canvas clearRect引起的闪屏问题
Apr 29 #HTML / CSS
浅谈HTML5新增和废弃的标签
Apr 28 #HTML / CSS
Canvas实现贝赛尔曲线轨迹动画的示例代码
Apr 25 #HTML / CSS
可能这些是你想要的H5软键盘兼容方案(小结)
Apr 23 #HTML / CSS
详解三种方式实现平滑滚动页面到顶部的功能
Apr 23 #HTML / CSS
小程序canvas中文字设置居中锚点
Apr 16 #HTML / CSS
用canvas做一个DVD待机动画的实现代码
Apr 12 #HTML / CSS
You might like
不用mod_rewrite直接用php实现伪静态化页面代码
2008/10/04 PHP
解析关于wamp启动是80端口被占用的问题
2013/06/21 PHP
PHP图片处理之使用imagecopy函数添加图片水印实例
2014/11/19 PHP
smarty模板引擎之内建函数用法
2015/03/30 PHP
PHP中模拟链表和链表的基本操作示例
2016/02/27 PHP
JScript中使用ADODB.Stream判断文件编码的代码
2008/06/09 Javascript
基于JQuery的Select选择框的华丽变身
2011/08/23 Javascript
网页编辑器ckeditor和ckfinder配置步骤分享
2012/05/24 Javascript
根据经纬度计算地球上两点之间的距离js实现代码
2013/03/05 Javascript
JQuery 图片滚动轮播示例代码
2014/03/24 Javascript
js动态移动滚动条至底部示例代码
2014/04/24 Javascript
Javascript中Array.prototype.map()详解
2014/10/22 Javascript
JQuery工具函数汇总
2015/06/15 Javascript
尝试动手制作javascript放大镜效果
2015/12/25 Javascript
ECHO.js 纯javascript轻量级延迟加载的实例代码
2016/05/24 Javascript
JavaScript组合模式学习要点
2016/08/26 Javascript
字太多用...代替的方法(两种)
2017/03/15 Javascript
Vue单页式应用(Hash模式下)实现微信分享的实例
2017/07/21 Javascript
利用jqgrid实现上移下移单元格功能
2018/11/07 Javascript
深入分析jQuery.one() 函数
2020/06/03 jQuery
[02:23]2018DOTA2亚洲邀请赛趣味视频——反应测试
2018/04/04 DOTA
[54:17]DOTA2-DPC中国联赛定级赛 RNG vs iG BO3第二场 1月10日
2021/03/11 DOTA
Python+OpenCV图片局部区域像素值处理详解
2019/01/23 Python
python批量修改文件夹及其子文件夹下的文件内容
2019/03/15 Python
python多线程抽象编程模型详解
2019/03/20 Python
Pytorch根据layers的name冻结训练方式
2020/01/06 Python
pycharm设置默认的UTF-8编码模式的方法详解
2020/06/01 Python
使用Html5实现异步上传文件,支持跨域,带有上传进度条
2016/09/17 HTML / CSS
Skyscanner阿联酋:全球领先的旅游搜索平台
2017/11/25 全球购物
英国户外玩具儿童游乐设备网站:TP Toys(蹦床、攀爬框架、秋千、滑梯和游戏屋)
2018/04/09 全球购物
西班牙在线宠物食品和配件商店:bitiba
2019/10/11 全球购物
个人自荐材料
2014/05/23 职场文书
酒店总经理岗位职责范本
2014/08/08 职场文书
毕业生捐书活动倡议书
2015/04/27 职场文书
Python基础之教你怎么在M1系统上使用pandas
2021/05/08 Python
MySql按时,天,周,月进行数据统计
2022/08/14 MySQL