我在开发中使用canvas的机会不是很多,但是第一次实际使用中就遇到了问题,“很久很久以前,我自己画了一个雷达图,线宽都是1像素,但是显示效果不如期望,这才发现canvas中的画线还是有坑的”,对比一下两个图,可以发现下图比较清晰。

我们先画一个线宽为1像素的线,代码和显示效果如下:

  const ctx = document.getElementById(canvas).getContext("2d");
ctx.strokeStyle = "red";
ctx.lineWidth =1;
ctx.moveTo(50, 10);
ctx.lineTo(200, 10);
ctx.stroke();

不对呀,这条线咋这么模糊,而且宽度貌似也不是1px

为了画这条线,浏览器首先到达初始起点(50,10)。这条线宽1px,所以两边各留0.5px。所以基本上初始起点是从(50,9.5)延伸到(50,10.5)。现在浏览器不能在屏幕上显示0.5像素——最小阈值是1像素。浏览器别无选择,只能将起点的边界延伸到屏幕上的实际像素边界。它会在两边再加0.5倍的“垃圾”。所以现在,最初的起点是从(50,9)扩展到(50,11),所以看起来有2px宽。情况如下:

下面是真实的2px宽的线,作为对照,可以发现,上面的线宽确实占据了2px,只是边缘显示非常模糊

那如何绘制1像素的线呢?本文总结了三种方法。

  • 将划线起始点移动到x.5的位置

既然1px宽的线是向两边各延伸0.5px,那如果我们的起点直接放在x.5的位置,向左右扩展则刚好占据1px,如下图所示:

修改代码

  ctx.moveTo(50, 10.5);
ctx.lineTo(200, 10.5);


上面的方法有个坏处是我们要修改现有的计算逻辑,时刻留意要多出0.5px

  • 绘制上下文整体移动x.5
    这个方法的好处是不扰乱我们的设计尺寸和计算逻辑,操作也很简单,只需要正常画好线后,上下文整体移动。
ctx.translate(0, 0.5);
  • 控制canvas元素大小与绘制上下文大小的比率

canvas有两个尺寸,一个是元素本身的css尺寸,一个是绘上下文的尺寸(画布的尺寸),默认情况下,canvas的画布尺寸和元素尺寸都是300*150.比率为1.画布尺寸是通过直接在元素上添加widthheight属性设置的,css尺寸则是通过css样式属性设置的,需要注意的是:

1.如果仅在元素上添加widthheight属性来设置画布尺寸,实际表现则同时设置了画布尺寸和css尺寸为相同的大小;
2.如果仅设置了css尺寸,则画布尺寸还是默认的300*150,并不会改变;
3.如果同时设置了画布尺寸和css尺寸,浏览器会缩放画布尺寸来适应css尺寸;

上面的第三条规则给我们画1px线宽提供了另一种方法,具体方法是设置canvas的画布尺寸是css尺寸的两倍,这样我们只需要按照两倍的设计尺寸(2px)来画线,这样就不会存在占用半个像素导致模糊的问题,浏览器会自动压缩画布到它的一半大小,原来2px的线宽会压缩显示成1px。

<canvas id="canvas5" width="600" height="300"> </canvas>
<canvas id="canvas6" width="600" height="300"> </canvas>
#canvas5 {
width: 300px;
height: 150px;
}
#canvas6 {
transform-origin: top left;
transform: scale(0.5);
}

如果想画出真实的物理1px的线,可以将画布尺寸继续放大widow.devicePixelRatio

参考
How to Draw 1px Crisp Lines in HTML5 Canvas

Canvas中如何画一条清晰的线宽为奇数(如1px逻辑像素)的线?的更多相关文章

  1. 有趣html5(两)----使用canvas结合剧本画在画布上的简单图(html5另一个强大)

    请珍惜劳动小编成果,这篇文章是原来小编,转载请注明出处. 于html5中能够使用canvas标签在画布上绘图,先直接上代码,这篇文章先简介一下canvas的用法.简单画几个圆,矩形,三角形,写字. 在 ...

  2. 根据多个点使用canvas贝赛尔曲线画一条平滑的曲线

    众所周知想用canvas画一条曲线我们可以使用这些函数: 二次曲线:quadraticCurveTo(cp1x, cp1y, x, y) 贝塞尔曲线:bezierCurveTo(cp1x, cp1y, ...

  3. HTML5在canvas中绘制复杂形状附效果截图

    HTML5在canvas中绘制复杂形状附效果截图 一.绘制复杂形状或路径 在简单的矩形不能满足需求的情况下,绘图环境提供了如下方法来绘制复杂的形状或路径. beginPath() : 开始绘制一个新路 ...

  4. Html5新特性 &lt;canvas&gt;画板画直线

     以下样例为用canvas标签画多条直线 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" & ...

  5. <canvas>中isPointInPath()方法在不同绘制内容中的效果

    <canvas>是HTML5中新增加的一个元素,我们可以使用脚本(通常使用JavaScript)在上面绘制图形,就像个画布一样.我们可以用它来绘制图表.制作一些动画.默认大小为300px ...

  6. HTML5 在canvas中绘制复杂形状

    作者:卿笃军 原文地址:http://blog.csdn.net/qingdujun/article/details/32942667 一.绘制复杂形状或路径 在简单的矩形不能满足需求的情况下,画图环 ...

  7. Canvas中的save方法和restore方法

    初学者也许会误认为canvas中save方法是用来保存绘图状态的图形,而restore方法是用来还原之前保存的绘图状态的图形,其实不然. save():保存当前的绘图状态. restore():恢复之 ...

  8. 浅谈HTML5中canvas中的beginPath()和closePath()的重要性

    beginPath的作用很简单,就是开始一段新的路径,但在使用canvas绘图的过程中却非常重要 先来看一小段代码: var ctx=document.getElementById("can ...

  9. Canvas中的非零围绕规则原理

    非零围绕规则:对于路径中指定范围区域,从该区域内部画一条足够长的线段.使此线段的全然落在路径范围之外. 非零围绕规则计数器:然后,将计数器初始化为0,每当这个线段与路径上的直线或曲线相交时,就改变计数 ...

随机推荐

  1. [openssh-server]install and enable ssh in debian9 / 在debian9上安装并启用ssh

    新安装的debian9.8 with XFCE 发现没有ssh,下载debian-9.8.0-amd64-DVD-1.iso并挂在到ESXi虚拟机/media/cdrom0. 清空或保存/etc/ap ...

  2. You Are the One HDU - 4283 (区间DP)

    Problem Description The TV shows such as You Are the One has been very popular. In order to meet the ...

  3. logstash配置多入多出并互相隔离

    需求:需要利用同一logstash进程采集不同日志,输出到es的不同index,各输入输出隔离: 主要需要解决如下两个问题: 1.如何加载多个配置文件? 普通启动方式:nohup bin/logsta ...

  4. zeppelin中连接hive和impala

    连接Hive 新建interpreter default.driver = org.apache.hive.jdbc.HiveDriver default.url = jdbc:hive2://hos ...

  5. Angular记录(6)

    文档资料 箭头函数--MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_fun ...

  6. Node.js目录

    [相关学习] npm入门教程 [基础] (1) 初识Node.js (2) 开发环境和调试工具 (3) commonJs 规范 (4) node 概念(global.process进程.调试) (5) ...

  7. [算法竞赛入门经典]Message Decoding,ACM/ICPC World Finals 1991,UVa213

    Description Some message encoding schemes require that an encoded message be sent in two parts. The ...

  8. 用SQL表达连接与外连接

    关系代数运算中,有连接运算,又分为θ连接和外连接 标准SQL语言中连接运算通常是采用 SELECT 列名[[,列名]...] FROM 表名1,表名2,... WHERE 检索条件; SQL的高级语法 ...

  9. 【4】学习C++之内存管理

    单个内存申请及释放: int *p = new int; if(p == NULL) { //内存分配失败 //异常处理 } delete p; p = NULL; 内存块的申请及释放: ]; if( ...

  10. mysql字符集设置注意事项

    mysql字符集设置必须是在具体的某一个数据库情况下才能进行设置 否则会报错.