文章:李蕊(成都中心)
排版:陈倩


背景:
某天我收到来自公司首席设计师的一则钉钉消息:轻设计编辑器导出的图片为什么会模糊?当我看到这个问题时,脑海中不禁产生了下面的疑问:怎么会模糊呢?是不是添加到编辑器的图片本身就是模糊的?
之后根据转赠给我的“作品”在编辑器里导出后发现,问题还真存在!换作较为准确的描述应该是:导出jpeg格式的自定义小尺寸图片会导致图片上的某些组件边缘虚化(特别是文字)。
一 基础概念什么是图片?
在计算机上看到的图片可以称为“数字图像”,按存储方式可分为两种:点阵图(Bitmap)和矢量图(Vector)。这里讨论的是点阵图,点阵图又称为位图,在Photoshop里放大一张图片可以看到一个一个的“方格”,即是“像素”,位图就是由像素阵列的排列来呈现图像的。每个像素可以表达色彩的位数是不同的,色彩位数越多,可用的颜色就越多,图像越逼真。
如何界定模糊?
一种是图片本身被放大后,单位面积内精细度降低,就会失真变模糊,因为图片的像素信息是固定的(点阵图)。还有一种情况是超高分辨率电脑屏幕下查看一张图片会比在低分辨率电脑屏幕上感觉模糊,例如Retina屏幕。所以若是要在超高分辨率下保持清晰,需要把图片精细度提高才能让肉眼看上去达到舒适感。
生成图片的原理是什么(dom-to-image源码分析)?
1. 整体思路
step1:利用XMLSerializer的serializeToString()构造一个字符串,以XML形式表示指定的DOM树
step2:利用SVG的将来自不同XML名称空间的元素包含进去,这里指上面的(X)HTML
step3:将SVG转为CANVAS
step4:利用HTMLCanvasElement.toDataURL() 生成不同格式的图片文件
2. 关键函数
3. 特殊处理
将DOM转换成XMLString之前,还需要处理字体文件和图片,通过ajax下载成blob数据再转换成base64以解决跨域问题。首先通过getComputedStyle(node)可以获取Node的样式,找出fontFamily和图片src,再查找字体的文件地址。
通过document.styleSheets获取样式表数组,注意根据每个CSSOM样式表接口的ownerNode属性区分其可读性,见下图:
ownerNode.tagName === 'STYLE' 则cssRules 可读取 内部样式表
ownerNode.tagName === 'LINK' 则cssRules 不可读取 外部样式表
获取cssRules后,根据type === CSSRule.FONT_FACE_RULE 找到字体样式,从而获取字体文件路径,匹配对应的fontFamily对应的字体即是需要下载的。见下图:
最后一步替换htmlStr里的字体和图片的url为base64格式,大体流程及完成。一些浏览器兼容性问题一并考虑,篇幅关系这里就不一一讲解了。
生成图片的关键在于:将 htmlStr塞到svg里,转换成svg,再将 svg 转换成 canvas
SVG的元素允许包含不同的XML命名空间。在浏览器的上下文中,就是我们的Xhtml, 极具包容性的接收了它
最后通过blob2base64把svg转换到canvas里,生成图片
生成图片的格式
canvas生成后,可以通过toDataURL() 转为不同格式的图片。下面是常见的两种图片格式:
PNG格式: 便携式网络图形,无损压缩的位图图形格式,带有alpha通道。
JPEG格式:JPEG是常见的一种格式,有损压缩,以去除冗余的图像和彩色数据获取极高的压缩率,用以减小图片大小。
清楚了上面的基础知识,我对前端生成图片有了大致的概念,接下来是定位“模糊”原因。分别导出 png格式和jpeg格式图片,发现只有jpeg格式才会出现边缘模糊,如下图:
因为相同的canvas数据导出的图片却因为格式不同效果不同,怀疑 jpeg格式边缘模糊有可能是因为图片质量被压缩了。那么canvas转成不同格式的图片质量是如何控制的呢?在MDN上可以找到答案:
官方解释:即使在清贫的岁月,也不能失去对幸福美好的向往,那些摆脱平庸的梦总能编制我们简单的生活,为我们简单的时光点缀希望。不能说我们总要多热爱生活,但总要有一颗懂得欣赏和珍惜的心。
由此可见,导出png格式的图片是没有质量参数的,而导出jpeg格式的图片是有质量参数可设置的,而且默认是0.92。经过测试,当质量参数设置为1.0时,质量最好,导出效果与png一致。
下图是质量为0.1和1.0的效果对比:
前端canvas生成图片的整体流程和思路就是上述所讲,最后通过优化jpeg格式导出的质量参数解决了模糊的问题,如有不同观点和方案可以留言进行讨论。

想要了解更多技术知识
快来关注我们吧
