如何在Chrome上绘制高分辨率画布?以及为什么如果设备PixelRatio === webkitBackingStor
How draw in high resolution to canvas on Chrome? And why if devicePixelRatio === webkitBackingStorePixelRatio does scaling to 2x improve resolution?
我正在尝试将 300dpi 的图像绘制到画布对象上,但在 Chrome 中它以非常差的质量显示。当我使用以下代码时,它没有改进,但那是因为devicePixelRatio
与backingStoreRatio
相同(两者都1
(。
然后,我尝试强制更改一些比率,并发现以下内容:
- 如果我将
ratio
更改为2
并强制缩放代码运行,则它会以更好的分辨率绘制到画布。 - 如果我将
ratio
更改为大于2
的任何值(例如3
、4
、5
、6
等(,那么它的分辨率很差!
这一切都是在台式计算机上完成的。
如何确保画布以高分辨率绘制?
(代码来自: http://www.html5rocks.com/en/tutorials/canvas/hidpi/(
/**
* Writes an image into a canvas taking into
* account the backing store pixel ratio and
* the device pixel ratio.
*
* @author Paul Lewis
* @param {Object} opts The params for drawing an image to the canvas
*/
function drawImage(opts) {
if(!opts.canvas) {
throw("A canvas is required");
}
if(!opts.image) {
throw("Image is required");
}
// get the canvas and context
var canvas = opts.canvas,
context = canvas.getContext('2d'),
image = opts.image,
// now default all the dimension info
srcx = opts.srcx || 0,
srcy = opts.srcy || 0,
srcw = opts.srcw || image.naturalWidth,
srch = opts.srch || image.naturalHeight,
desx = opts.desx || srcx,
desy = opts.desy || srcy,
desw = opts.desw || srcw,
desh = opts.desh || srch,
auto = opts.auto,
// finally query the various pixel ratios
devicePixelRatio = window.devicePixelRatio || 1,
backingStoreRatio = context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1,
ratio = devicePixelRatio / backingStoreRatio;
// ensure we have a value set for auto.
// If auto is set to false then we
// will simply not upscale the canvas
// and the default behaviour will be maintained
if (typeof auto === 'undefined') {
auto = true;
}
// upscale the canvas if the two ratios don't match
if (auto && devicePixelRatio !== backingStoreRatio) {
var oldWidth = canvas.width;
var oldHeight = canvas.height;
canvas.width = oldWidth * ratio;
canvas.height = oldHeight * ratio;
canvas.style.width = oldWidth + 'px';
canvas.style.height = oldHeight + 'px';
// now scale the context to counter
// the fact that we've manually scaled
// our canvas element
context.scale(ratio, ratio);
}
context.drawImage(pic, srcx, srcy, srcw, srch, desx, desy, desw, desh);
}
仅进行以下更改会导致高分辨率画布图像(为什么?
//WE FORCE RATIO TO BE 2
ratio = 2;
//WE FORCE IT TO UPSCALE (event though they're equal)
if (auto && devicePixelRatio === backingStoreRatio) {
如果我们把上面改成3
的比例,就不再是高分辨率了!
编辑:一个额外的观察 - 即使使用2倍比率,虽然它的分辨率明显更好,但它仍然不如在img
标签中显示图像那么清晰(
从这个问题链接的HTML5Rocks文章使事情变得比他们需要的更加困难,但它犯了与我见过的其他资源相同的基本错误(1,2,3,4(。这些参考给出了这个公式的一些变化:
var rect = canvas.getBoundingClientRect();
canvas.width = Math.round (devicePixelRatio * rect.width); // WRONG!
公式是错误的。一个更好的公式是
var rect = canvas.getBoundingClientRect();
canvas.width = Math.round (devicePixelRatio * rect.right)
- Math.round (devicePixelRatio * rect.left);
关键是,通过设备PixelRatio缩放宽度或高度(即两个位置的差异(是没有意义的。你只应该缩放一个绝对位置。我找不到这个确切点的参考,但我认为这是显而易见的,一旦你得到它。
命题。
无法根据矩形的 CSS 宽度和高度(以设备无关的像素为单位(计算矩形的物理宽度和高度(以设备像素为单位(。
证明。
假设您有两个元素,其边界矩形以与设备无关的像素为单位为
{ left: 0, top: 10, right: 8, bottom: 20, width: 8, height: 10 },
{ left: 1, top: 20, right: 9, bottom: 30, width: 8, height: 10 }.
现在假设 devicePixelRatio 为 1.4,元素将覆盖这些设备像素矩形:
{ left: 0, top: 14, right: 11, bottom: 28, width: 11, height: 14 },
{ left: 1, top: 28, right: 13, bottom: 42, width: 12, height: 14 },
其中,左,上,右和下已乘以devicePixelRatio并四舍五入到最接近的整数(使用Math.round(((。
您会注意到,这两个矩形在与设备无关的像素中具有相同的宽度,但在设备像素中具有不同的宽度。
测试。
下面是用于测试的代码示例。在浏览器中加载它,然后用鼠标放大和缩小。最后一张画布应始终具有清晰的线条。其他三个在某些分辨率下会模糊不清。
在桌面Firefox,IE,Edge,Chrome和Android Chrome和Firefox上进行了测试。(注意,这在JSfiddle上不起作用,因为getBoundingClientRect在那里返回不正确的值。
<!DOCTYPE html>
<html>
<head>
<script>
function resize() {
var canvases = document.getElementsByTagName("canvas");
var i, j;
for (i = 0; i != canvases.length; ++ i) {
var canvas = canvases[i];
var method = canvas.getAttribute("method");
var dipRect = canvas.getBoundingClientRect();
var context = canvas.getContext("2d");
switch (method) {
case "0":
// Incorrect:
canvas.width = devicePixelRatio * dipRect.width;
canvas.height = devicePixelRatio * dipRect.height;
break;
case "1":
// Incorrect:
canvas.width = Math.round(devicePixelRatio * dipRect.width);
canvas.height = Math.round(devicePixelRatio * dipRect.height);
break;
case "2":
// Incorrect:
canvas.width = Math.floor(devicePixelRatio * dipRect.width);
canvas.height = Math.floor(devicePixelRatio * dipRect.height);
break;
case "3":
// Correct:
canvas.width = Math.round(devicePixelRatio * dipRect.right)
- Math.round(devicePixelRatio * dipRect.left);
canvas.height = Math.round(devicePixelRatio * dipRect.bottom)
- Math.round(devicePixelRatio * dipRect.top);
break;
}
console.log("method " + method
+ ", devicePixelRatio " + devicePixelRatio
+ ", client rect (DI px) (" + dipRect.left + ", " + dipRect.top + ")"
+ ", " + dipRect.width + " x " + dipRect.height
+ ", canvas width, height (logical px) " + canvas.width + ", " + canvas.height);
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "cyan";
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "black";
for (j = 0; j != Math.floor (canvas.width / 2); ++ j) {
context.fillRect(2 * j, 0, 1, canvas.height);
}
}
};
addEventListener("DOMContentLoaded", resize);
addEventListener("resize", resize);
</script>
</head>
<body>
<canvas method="0" style="position: absolute; left: 1px; top: 10px; width: 8px; height: 10px"></canvas>
<canvas method="1" style="position: absolute; left: 1px; top: 25px; width: 8px; height: 10px"></canvas>
<canvas method="2" style="position: absolute; left: 1px; top: 40px; width: 8px; height: 10px"></canvas>
<canvas method="3" style="position: absolute; left: 1px; top: 55px; width: 8px; height: 10px"></canvas>
</body>
</html>
的一个技巧是实际将画布宽度和高度设置为照片的像素高度和宽度,然后使用 css 调整图像大小。下面是一个 sudo 代码示例。
canvasElement.width = imgWidth;
canvasElement.height = imgHeight;
canvasElement.getContext("2d").drawImage(ARGS);
然后,您可以使用宽度和高度设置或3D变换。
canvasElement.style.transform = "scale3d(0.5,0.5,0)";
或
canvasElement.style.width = newWidth;
canvasElement.style.height = newWidth * imgHeight / imgWidth;
我建议您使用 3d 转换,因为当您使用 3d 转换时,元素的图像数据会被复制到 GPU,因此您不必担心任何质量下降。
我希望这有帮助!
- 我需要帮助弄清楚为什么;如果'声明不起作用
- 为什么 JavaScript 如果一个数字有一个前导零,就将其视为八进制
- 如果总是执行finally,为什么try会返回未更改的值
- JQuery UI可拖动:如果助手设置为克隆,为什么不't可拖动集的已应用类到克隆
- 我的徽章;如果“;为什么没有调用关联的函数
- 为什么如果我的函数中的范围被 javascript 解释器完全忽略了
- 为什么当我使用初始化触发器函数时,我总是找不到脚本函数:发送发票如果新
- 为什么“如果( !Object.property)“,如果 Object 未定义,则中断
- 我可以通过使用函数绑定来保留默认值吗?如果没有,为什么
- 为什么 1.2 == true 返回 false 如果布尔值 (1.2) 实际上是真的
- 如果AJAX是异步的,为什么我们要使用setTimout函数
- 如果我不这样做会发生什么;t有页码吗?继续循环帖子..为什么需要分页,对于页脚
- 如果在transclusion之后添加,为什么指令下的动态元素具有错误的作用域
- 如果我用fadeTogle替换fadeIn/fadeOut,为什么它不起作用
- 如果我对索引进行硬编码,它会按预期工作,为什么这个增量器会返回NAN
- 如果在外部单击,则隐藏弹出窗口.为什么我的脚本没有'不起作用
- 为什么我的JS"如果“;语句的求值结果总是为false
- 为什么Ng Repeat不工作,如果按钮从不同的形式调用
- 如果我刷新几次,为什么我的应用程序会在Chrome中崩溃
- 这个“开关”应该是一个“如果”——为什么