fix(extension): ensure dpr>=1 when exporting images(#1222) #1274
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
close #1222
问题发生的原因
svg基础知识
SVG坐标系有两个重要的属性:
viewport
,viewBox
当运行上面的代码时,可以发现,如果没有
viewBox
,那么svg
内部的元素是不会根据svg
的宽高进行自适应的drawImage
导出图片流程分析
回到这个问题中,最终导出图片的步骤是:
svg
拼接然后放入到new Image()
中,即new Image()
绘制到canvas中问题关键点分析
从上面的分析我们可以知道,本质就是绘制一个svg到canvas上面
当我们调试svg,我们可以看到如下的结构
这里我们可以看出,是没有使用viewBox进行缩放,换句话说,由于svg的
viewport
设置的是width="100%" height="100%"
,因此如果绘制时装载svg的容器小于svg内部的元素,也就是canvas(因为我们要进行drawImage)的大小无法保证canvas.width/canvas.height
>svg内部元素宽/高
,那么绘制将会是缺失的svg图形而缩放浏览器比例时,经过调试可以发现,
window.devicePixelRatio
也会随着变小,最终导致canvas.width
<canvas.style.width
从下面代码可以看出,svg内部元素的大小就是
bboxWidth/bboxHeight
因此,当
window.devicePixelRatio
变小到window.devicePixelRatio <1
时,会导致canvas.width
<canvas.style.width
=svg内部元素的大小
,从而导致svg绘制到canvas上的图形缺失问题非关键点分析
按照上面svg基础知识的分析,由于没有设置
viewBox
,因此当canvas.width
远远大于canvas.style.width
=svg内部元素的大小
时,会导致svg绘制到canvas上的图形占的比例非常小,那么为什么放大浏览器比例后(window.devicePixelRatio
变得非常大)也仍然能导出非常合适宽高的图片呢?这是因为当
canvas.width
跟canvas.style.width
不相同时,会进行缩放在导出图片代码中,为了解决绘制模糊问题,进行了
canvas.width = bboxWidth * dpr
配合ctx.scale(dpr, dpr)
的逻辑比如dpr=3时,计算出来的svg内部元素的大小为
那么对应的
当没有使用
ctx.scale(3, 3)
时,svg内部元素
绘制到canvas只占了1/3空间(svg元素全部都能绘制出来),由于canvas.width
跟canvas.style.width
不相同时,会进行缩放,因此整体canvas会缩小为原来的1/3大小,此时绘制上去的svg为:此时触发了ctx.scale(3, 3),会将绘制的svg放大,此时绘制上去的svg为:
刚好适配上目前canvas展示出来的宽和高
那是因为当
dpr<1
时,canvas.width
<svg内部元素的宽
,此时绘制上去的就是缺失的svg,这个时候再按照上面流程,触发canvas.width
跟canvas.style.width
不相同时,会进行等比例的放大(因为dpr<1
)ctx.scale(dpr, dpr)
进行内部元素的缩小(因为dpr<1
)也是把绘制上去的缺失的svg部分
等比例的放大
+scale缩小
,简单点说,就是缺少一半的图形,再怎么放大或者缩小,缺失还是缺失,无法改变解决方法
从上面的分析中,我们已经明白了问题的关键点,就是
canvas.width
不能小于svg内部元素的大小
,那么就有对应的两种解决方法:canvas.width
>canvas.style.width
=svg内部元素的大小
viewBox
进行svg内部元素的缩放,去自适应适配canvas.width
的大小方法2
直接对svg元素设置属性
viewBox
方法1
直接判断是否小于1,强制转化为1
总结
最终我采用了方法1进行代码的提交,主要有两点:
window.devicePixelRatio
如果非常非常非常小的时候,canvas.width
也会变得非常小,为了适配canvas.width
,最终导出的图片有效内容占据的地方也会非常小canvas.width = bboxWidth * dpr配合ctx.scale(dpr, dpr)
是为了解决绘制模糊,本质是为了解决dpr>1高分辨率
导致的绘制模糊问题,当dpr<1
时并不存在这种绘制模糊问题,因此没必要进行这种canvas.width = bboxWidth * dpr配合ctx.scale(dpr, dpr)
逻辑,即当dpr<1
时直接就dpr=1