Javascript — рисовать на холсте, используя процент от ширины и высоты

#javascript #canvas

#javascript #холст

Вопрос:

У меня есть фоновое изображение, которое занимает всю ширину и высоту моего холста, и я хочу иметь возможность ссылаться на разные местоположения на изображении, поэтому я размещаю несколько кругов в этих точках изображения, чтобы я мог визуально подтвердить, что у меня есть правильное место.

Я делаю это, щелкая мышью в том месте, где я хочу нарисовать точку, со следующим кодом в моем событии прослушивания щелчка мыши:

 var rectCanvas = canvas.getBoundingClientRect();
var positionX = Math.round(((e.clientX - rectCanvas.left) / canvas.width * 100) * 1000) / 1000;
var positionY = Math.round(((e.clientY - rectCanvas.top) / canvas.height * 100) * 1000) / 1000;

//alert(positionX   ", "   positionY);

alert("WidthnPosition: "   (e.clientX - rectCanvas.left)   "nPercentage Position: "   positionX   "nBack to position: "   percentToPxHor(positionX));
  

У меня объявлены следующие процедуры:

 function percentToPxHor(intPx) {
    return intPx * canvas.width / 100;
}

function percentToPxVert(intPx) {
    return intPx * canvas.height / 100;
}
  

Нажав на заметную особенность фонового изображения, я получаю значение, которое имеет смысл. Например, щелчок примерно посередине изображения подсказывает мне:

Позиция ширины: 532,5
Процентная позиция: 53,091
Вернуться к позиции: 532,50273

Из этого я могу четко сказать, что моя процедура преобразования процента ширины обратно в значение пикселя работает. Однако, когда я пытаюсь применить это на практике, это не работает.

При попытке нарисовать круги, о которых я упоминал ранее, они правильно располагаются по оси Y, но слишком далеко вправо по оси X. Я подозреваю, что это как-то связано с тем, как я учитываю положение холста по отношению к окну, но я не могу точно указать на это. Круги, расположенные в левой части изображения, немного слишком правы, те, что расположены ближе к правой части изображения, сильно правее (почти как если бы в чем бы ни заключалась проблема, применялась экспоненциальная проблема, которая становится хуже, чем больше задействованных чисел.

Когда я добавляю эти круги, я размещаю четыре, по одному в каждом углу прямоугольника, используя следующий фрагмент кода:

 var topLeft = {x:percentToPxHor(17.275),y:percentToPxVert(59.691)};
var bottomLeft = {x:percentToPxHor(17.275),y:percentToPxVert(60.72)};
var topRight = {x:percentToPxHor(71.459),y:percentToPxVert(61.407)};
var bottomRight = {x:percentToPxHor(71.352),y:percentToPxVert(62.436)};
  

Любая помощь была бы высоко оценена.

Ответ №1:

Я создал тестер для положения мыши, который показывает смещение при наличии границы. Могут быть и другие факторы, но это демонстрирует, насколько getBoundingClientRect неточен. Я тестировал в FireFox, и вы можете четко видеть смещение.

 var canvas = document.getElementById('rect-canvas');
var check = document.getElementById('no-border-checkbox');
var ctx = canvas.getContext('2d');
var mouse = {p:{x:0, y:0}, frame:0, frames:30};
var topLeft = {x:percentToPxHor(17.275),y:percentToPxVert(59.691)};
var bottomLeft = {x:percentToPxHor(17.275),y:percentToPxVert(60.72)};
var topRight = {x:percentToPxHor(71.459),y:percentToPxVert(61.407)};
var bottomRight = {x:percentToPxHor(71.352),y:percentToPxVert(62.436)};

main();

function main() {
  check.addEventListener('change', function(e) {
    var className = check.checked ? 'border' : '';
    canvas.setAttribute('class',className);
  });
    
  canvas.addEventListener('mousemove',function(e){
    var rectCanvas = canvas.getBoundingClientRect();
    var positionX = Math.round(((e.clientX - rectCanvas.left) / canvas.width * 100) * 1000) / 1000;
    var positionY = Math.round(((e.clientY - rectCanvas.top) / canvas.height * 100) * 1000) / 1000;

    //alert("WidthnPosition: "   (e.clientX - rectCanvas.left)   "nPercentage Position: "   positionX   "nBack to position: "   percentToPxHor(positionX));

    mouse.p.x = percentToPxHor(positionX);
    mouse.p.y = percentToPxVert(positionY);
    render();
  });
}


function render() {
  // clear canvas
  ctx.fillStyle='white';
  ctx.fillRect(0,0,canvas.width,canvas.height);
  
  // draw points and connect
  drawCircle(topLeft, "blue");
  drawCircle(bottomLeft, "green");
  drawCircle(topRight, "red");
  drawCircle(bottomRight, "orange");
  drawPoly([topLeft, bottomLeft, bottomRight, topRight]);
  
  // draw mouse circle
  drawCircle(mouse.p, 'rgba(255,200,0,.75)', 10);
  
  // draw mouse lines
  var h1 = {x:0, y: mouse.p.y};
  var h2 = {x:canvas.width, y: mouse.p.y};
  var v1 = {x: mouse.p.x, y:0};
  var v2 = {x: mouse.p.x, y:canvas.height};
  drawPoly([h1, h2], "rgba(0,0,0,.25)");
  drawPoly([v1, v2], "rgba(0,0,0,.25)");
}
function percentToPxHor(intPx) {
    return intPx * canvas.width / 100;
}

function percentToPxVert(intPx) {
    return intPx * canvas.height / 100;
}

function drawCircle(p, color, radius) {
  if (typeof radius == 'undefined') radius = 2;
  if (typeof color == 'undefined') color = 'red';
  ctx.beginPath();
  ctx.fillStyle = color;
  ctx.arc(p.x,p.y,radius,0,2*Math.PI);
  ctx.fill();
}

function drawPoly(points, color) {
 ctx.strokeStyle = color ? color : 'red';
 ctx.beginPath();
 var p = points[points.length-1];
 ctx.moveTo(p.x, p.y);
 for(var i = 0; i < points.length; i  ) {
   var p = points[i];
   ctx.lineTo(p.x, p.y); 
 }
 ctx.stroke();
}  
 #rect-canvas {
  
}

.border {
  border:5px solid red;
}

body {
  background-color:#cccccc;
}  
 <canvas id='rect-canvas' class='border' width="300" height="150"></canvas>

<p>
Border
<input type='checkbox' id='no-border-checkbox' value='on' checked>
</p>