画布网格在不同缩放级别下变得模糊

4
我是一个有用的助手,可以为您翻译文本。
我正在尝试创建一个简单的画布网格,它将根据玩家当前的缩放级别和一定的画布高度/宽度比例屏幕限制来自适应。以下是我目前的进展:
JS:
var bw = window.innerWidth / 2; //canvas size before padding
var bh = window.innerHeight / 1.3; //canvas size before padding
//padding around grid, h and w
var pW = 30;
var pH = 2;
var lLimit = 0; //9 line limit for both height and width to create 8x8

//size of canvas - it will consist the padding around the grid from all sides + the grid itself. it's a total sum
var cw = bw + pW;
var ch = bh + pH;

var canvas = $('<canvas/>').attr({width: cw, height: ch}).appendTo('body');

var context = canvas.get(0).getContext("2d");

function drawBoard(){ 

     for (var x = 0; lLimit <= 8; x += bw / 8) { //handling the height grid 
        context.moveTo(x, 0);
        context.lineTo(x, bh);
        lLimit++;
    }

    for (var x = 0; lLimit <= 17; x += bh / 8) { //handling the width grid
        context.moveTo(0, x); //begin the line at this cord
        context.lineTo(bw, x); //end the line at this cord
        lLimit++;
    }
    //context.lineWidth = 0.5; what should I put here?
    context.strokeStyle = "black";
    context.stroke();
}

drawBoard(); 

现在,我成功地让画布在每个屏幕分辨率缩放级别上保持相同的比例水平。这是我试图实现的一部分。我还试图实现细线,在所有不同的缩放级别下看起来都相同,并且当然要消除模糊。目前,线条的粗细会根据缩放级别而改变,并且有时会模糊。

这里是jsFiddle(虽然jsFiddle窗口本身很小,所以您几乎看不到差异):

https://jsfiddle.net/wL60jo5n/

非常感谢您的帮助。


当你说缩放级别时,你是指大多数浏览器内置的缩放功能(使用Ctrl + 鼠标滚轮上/下)吗? - Kevin L
是的。window.innerWidth会在页面加载时考虑用户当前的缩放级别。 - RunningFromShia
我尝试实现的网格系统的一个实时示例:http://slither.io/ 无论您以何种缩放级别进入网页,甚至在游戏过程中缩放时,网格系统看起来都是相同的。 - RunningFromShia
js2dx.com是一个库,旨在解决“canvas”标签中模糊问题。 - Gonki
3个回答

5
为了防止模糊,您在设置canvas元素的尺寸时应考虑window.devicePixelRatio(当然,在之后的绘图过程中也应该考虑这些尺寸)。
您的canvas元素的widthheight属性值应该比同名CSS属性中的值高一定比例。可以通过以下函数表达:
function setCanvasSize(canvas, width, height) {
    var ratio = window.devicePixelRatio,
        style = canvas.style;

    style.width  = '' + (width  / ratio) + 'px';
    style.height = '' + (height / ratio) + 'px';

    canvas.width  = width;
    canvas.height = height;
}

5
为了消除画布缩放时的模糊效果,我使用了CSS中的image-rendering: pixelated

0
问题在于您正在使用十进制值进行绘制。您的`drawBoard()`循环中,画布宽度和位置增量都使用分数。画布是位图表面,而不是矢量图形。当您设置画布的宽度和高度时,实际上设置了存储在内存中的像素数量。该值不能为小数(浏览器可能会将小数部分截断)。当您尝试在小数位置绘制时,画布将使用像素插值来避免锯齿,因此偶尔会出现模糊。
请参见一个在绘制之前对`x`进行四舍五入的版本: https://jsfiddle.net/hts7yybm/ 尝试在绘制之前将值四舍五入,但不要在实际逻辑中这样做。这样,不精确性就不会随着算法不断添加到值中。
function drawBoard(){
  for (var x = 0; lLimit <= 8; x += bw / 8) {
    var roundedX = Math.round(x);
    context.moveTo(roundedX, 0);
    context.lineTo(roundedX, bh);
    lLimit++;
  }

  for (var x = 0; lLimit <= 17; x += bh / 8) {
    var roundedX = Math.round(x);
    context.moveTo(0, roundedX);
    context.lineTo(bw, roundedX);
    lLimit++;
  }
  context.lineWidth = 1; // never use decimals
  context.strokeStyle = "black";
  context.stroke();
}

编辑:我相当确定所有浏览器都会像 img 元素一样处理 canvas,因此除了使用带前缀的 CSS 之外,没有办法防止用户使用浏览器缩放功能时出现锯齿。即使这样做,我也不确定浏览器的缩放功能是否会考虑到这一点。

canvas {
  image-rendering: -moz-crisp-edges;
  image-rendering: -o-crisp-edges;
  image-rendering: -webkit-optimize-contrast;
  image-rendering: crisp-edges;
  -ms-interpolation-mode: nearest-neighbor;
}

此外,请确保画布没有任何CSS设置的尺寸。这只会在绘制后拉伸图像,而不是增加绘图表面。如果您想通过给它100%的宽度和高度来填充画布,则需要一些JS来计算CSS给定的高度和宽度,并根据此设置画布的宽度和高度属性的值。然后,您可以在画布绘制代码中制作自己的缩放功能实现,但根据您正在做的事情,这可能过于复杂。

我尝试了你的方法,但是没有起作用。将缩放级别设置为200%以上,然后刷新页面。然后将其设置为300%并刷新。您会注意到每个缩放级别都会有变化;它会变得更厚或更薄。测试应在常规HTML窗口上执行,而不是在jsFiddle上执行,因为差异不明显。 - RunningFromShia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接