【问题标题】:Canvas position in grid inaccurate网格中的画布位置不准确
【发布时间】:2021-08-19 22:49:50
【问题描述】:

画布上网格的 x 和 y 位置在某些单元格上被放置在左侧 1 行和上方 1 列。我不知道是什么原因造成的,但它主要发生在接近网格末端的中间。

'use strict';

// the canvas
const canvas = document.querySelector('.canvas');
const context = canvas.getContext('2d');

class Node {
  constructor(row, col) {
    // save current index (row, col) to know where in the canvas it is
    this.row = row;
    this.col = col;

    this.show = color => {
      context.beginPath();
      context.rect(this.row * cellDimension, this.col * cellDimension, cellDimension - 1, cellDimension - 1);
      context.fillStyle = color;
      context.fill();
    }
  }
}

// the counter from input field
const colRowCount = 60;
// will be determined from setup
let cellDimension;

let gridArray = new Array(colRowCount);
//start and finish
let startNode = new Node();
let endNode = new Node();
// array of the path nodes
let totalPath = new Array();


// setup all the neccessities
const setup = () => {
  // subtract the height of menu
  canvas.height = window.innerHeight;
  canvas.width = window.innerWidth;
  cellDimension = canvas.width / colRowCount;

  for (let i = 0; i < colRowCount; i++) {
    gridArray[i] = new Array(colRowCount);
    for (let j = 0; j < colRowCount; j++) {
      gridArray[i][j] = new Node(i, j);
    }
  }

  context.beginPath();
  for (let i = 0; i < canvas.width; i += cellDimension) {
    // horizontal divider
    context.moveTo(0, i);
    context.lineTo(canvas.width, i);

    // vertical divider
    context.moveTo(i, 0);
    context.lineTo(i, canvas.width);

    context.strokeStyle = '#ddd';
    context.stroke();
  }
}

const getMousePosition = event => {
  const rect = canvas.getBoundingClientRect();
  const x = event.clientX - rect.left;
  const y = event.clientY - rect.top;

  // top left
  let cellX = 0;
  let cellY = 0;
  // bounds of the square unit
  const bounds = Math.floor(Math.floor(canvas.width) / colRowCount);

  for (let i = 1; i < colRowCount; i++) {
    if (x > (bounds * i)) {
      cellX = i;
    }

    if (y > (bounds * i)) {
      cellY = i;
    }
  }

  return {
    x: cellX,
    y: cellY
  };
}

const placeStart = position => {
  startNode = gridArray[position.x][position.y];
  startNode.show('green');
}

window.addEventListener('load', () => {
  // setup the canvas
  setup();
  // event listener for canvas
  canvas.addEventListener('mousedown', event => {
    placeStart(getMousePosition(event));
  });
});
.container {
  height: calc(100% - 50px);
  width: 100%;
}


/*for all the grids with canvas*/

.canvas {
  height: 100%;
  width: 100%;
  border: 1px solid green;
}


/*for all the grids with canvas*/
<div class='container'>
  <canvas class='canvas'>
    </canvas>
</div>

jsfiddle 中的那个人的行为更加出人意料。

【问题讨论】:

  • 没什么好说的,cellDimension自然是整数。同样,当您放下东西时,您会失去准确性(向下)。这些是导致问题的原因吗?
  • @AHaworth,我是在注意到问题后添加的,所以不是地板。但我同意这会导致更多的不准确。我会在本地删除它们

标签: javascript html css canvas


【解决方案1】:

这里有多个问题...

首先,画布有两种尺寸,一种是缓冲区,由其widthheight 属性设置,另一种是渲染(由 CSS 规定)。
这里两种尺寸不同,因此实际的画布图像被 CSS 渲染器拉伸,导致抗锯齿和舍入不精确。 See more here.

然后,stroke() 确实在提供的坐标的两侧重叠,因此对于1lineWidth,您需要添加(或删除)0.5px 的偏移量,以便垂直或水平线正确适合在单个像素的边界内,否则您将再次启用抗锯齿功能。See more here.

最后,你的坐标甚至没有四舍五入,所以你会落在两个像素坐标之间,再次添加抗锯齿......

这是一个解决所有问题的尝试:

'use strict';

// the canvas
const canvas = document.querySelector('.canvas');
const context = canvas.getContext('2d');
class Node {
  constructor(row, col) {
    // save current index (row, col) to know where in the canvas it is
    this.row = row;
    this.col = col;

    this.show = color => {
      const border_offset = context.lineWidth;
      context.beginPath();
      context.rect(this.row * cellDimension + border_offset, this.col * cellDimension + border_offset, cellDimension - border_offset, cellDimension - border_offset);
      context.fillStyle = color;
      context.fill();
      // remember we're on, so we can be redrawn on resize
      this.color = color;
    }
  }
}

// The counter from input field
const colRowCount = 60;
// Will be determined from setup
let cellDimension;

let gridArray = new Array(colRowCount);
//start and finish
let startNode = new Node();
let endNode = new Node();
// array of the path nodes
let totalPath = new Array();
// Initialize all the nodes directly, 
// we don't need to know the size of the cells
// or of the canvas to do so.
for (let i = 0; i < colRowCount; i++) {
  gridArray[i] = new Array(colRowCount);
  for (let j = 0; j < colRowCount; j++) {
    gridArray[i][j] = new Node(i, j);
  }
}

// called at init, and at every page resize
const redraw = () => {
  // get the size of the container measured by CSS
  const parent = canvas.parentNode;
  const parent_width = parent.offsetWidth;
  // remove 1 lineWidth for outer strokes
  const max_grid_size = parent_width - context.lineWidth;
  // cellDimension must be an integer
  cellDimension = Math.floor( max_grid_size / colRowCount );
  const grid_size = cellDimension * colRowCount;
  // now we know the size of our grid,
  // our canvas will be one lineWidth bigger to show the outer strokes
  canvas.width = canvas.height = grid_size + context.lineWidth;

  context.beginPath();
  const stroke_offset = context.lineWidth / 2;
  for (let i = 0; i <= grid_size; i += cellDimension) {
    // horizontal divider
    context.moveTo(stroke_offset, i + stroke_offset);
    context.lineTo(grid_size + stroke_offset, i + stroke_offset);

    // vertical divider
    context.moveTo(i + stroke_offset, stroke_offset);
    context.lineTo(i + stroke_offset, grid_size + stroke_offset );
  }
  // stroke only once
  context.strokeStyle = '#ddd';
  context.stroke();
  
  // redraw all the nodes that were already active
  gridArray.flat().filter( (node) => node.color )
    .forEach( (node) => node.show( node.color ) );
}

const getMousePosition = event => {
  const rect = canvas.getBoundingClientRect();
  const x = event.clientX - rect.left;
  const y = event.clientY - rect.top;

  // top left
  let cellX = 0;
  let cellY = 0;
  // bounds of the square unit
  const bounds = Math.floor(Math.floor(canvas.width) / colRowCount);

  for (let i = 1; i < colRowCount; i++) {
    if (x > (bounds * i)) {
      cellX = i;
    }

    if (y > (bounds * i)) {
      cellY = i;
    }
  }

  return {
    x: cellX,
    y: cellY
  };
}

const placeStart = position => {
  startNode = gridArray[position.x][position.y];
  startNode.show('green');
}

window.addEventListener('load', () => {
  redraw();
  // event listener for canvas
  canvas.addEventListener('mousedown', event => {
    placeStart(getMousePosition(event));
  });
  window.onresize = redraw;
});
.container {
  height: calc(100% - 50px);
  width: 100%;
}

.canvas {
  border: 1px solid green;
  /* we let the canvas buffer's size rule its presentation dimensions */
}
<div class='container'>
  <canvas class='canvas'></canvas>
</div>

【讨论】:

  • 您好,感谢您的回答。我将您的功能添加到我的代码中。我做了一个额外的函数来设置一个网格,因为我添加了一个 colCount,它取决于画布的高度。它不会以某种方式在画布上绘制任何东西。你可以看看吗?这是repo
  • 顺便说一句,我将你的 parent_height 添加到常量 here
  • parent_height 应在每次调整大小事件时重新计算。不要在每次调整大小时重新初始化节点,你会松开被点击的节点。 window.onresize = setup(); 将设置setup() (undefined) 的返回值作为调整大小处理程序,我写了window.onresize = setup。不确定这就是全部,抱歉,我没有太多时间查看您的代码。
  • 没关系。我得到它的工作,非常感谢我很感激它
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-03
  • 2016-10-19
  • 1970-01-01
相关资源
最近更新 更多