canvas 中做到无损缩放

6,006 阅读2分钟

前言

通常来说,大家对svgcanvas的首要区别与印象,是svg具有与设备、分辨率无关的特性,转而普遍就认为canvas区别于svg的一大劣势就在于此。

个人愚见,更合理的描述应该是canvas对设备与分辨率“敏感”。

canvas的高清屏适配

svg自适应适配了Retina高清屏的,而如果你不做任何处理,初始化一个canvas,在具有缩放设置的屏幕上显示出来的内容看起来是糊的:

1.jpg

初见之下,很难不让人苦恼。

其实解决这个问题的办法及其简单,首先需要获取一个值:let dpr = window.devicePixelRatio;

在普通屏幕下,dpr1,而高清屏或者人为调整过系统缩放设置的屏幕下,dpr大于1,多数时候大致为2或者3

所以,解决办法就是将canvas的真实宽高canvas.width/height乘以dpr,将canvas.style.width/height设置为正常宽高。

比如目标canvas的宽高为200,那么canvas.style.width/height设置为200px,而canvas.width/height设置为400即可。

如图所示:

2.jpg

这样子,提供足量多的“真实”像素,canvas自然就不糊了。

无损缩放

再说到canvas实现类似svg的无损缩放。

最简单的无损缩放,比如放大2倍,使用绘图上下文context.scale(2, 2);再绘制图形即可。

而通常缩放由事件来触发,这个技巧常用于大数据量的展示和交互,事件有一个坐标x, y,表示用户关注点的位置,图形也应以此坐标点来进行缩放,而不同的缩放点也会使得缩放结果不同,如图:

3.jpg

4.jpg

连接圆心O缩放事件发生点E,使用中学学过的向量的知识,很容易就可得到:

目标坐标=事件坐标+(图形坐标事件坐标)×缩放倍数目标坐标=事件坐标+(图形坐标-事件坐标)\times 缩放倍数

表示得更清楚一点即:

{x=event.x+(shape.xevent.x)×zoomingy=event.y+(shape.yevent.y)×zooming\left\{ \begin{array}{} x = event.x + (shape.x - event.x) \times zooming \\ y = event.y + (shape.y - event.y) \times zooming \end{array} \right.

至此,目标位置就确定好了,同样的,配合使用context.scale(zooming, zooming);也就完成了canvas中无损缩放的整个过程。

CodePen 在线演示,使用鼠标滚轮缩放图形。