首页 > 解决方案 > 有没有办法在 JavaScript 中获得画布的自然偏移量?

问题描述

我正在尝试像这样获得鼠标 x 和 y 位置:

canvas.addEventListener('mousemove', (e) => {
    mouse.x = e.clientX - canvas.offsetLeft;
    mouse.y = e.clientY - canvas.offsetTop;
});

但是由于我使用的样式表居中并调整画布大小,因此位置非常不准确,甚至不一致!

标签: javascriptcanvascoordinatesoffset

解决方案


我创建了一个较小的演示(如果我理解正确的话) ,当您移动和/或缩放周围时bounds,如何canvas获取 以及它如何变化。canvas我希望它能帮助你得到你想要的。

笔记:

为了使此演示按预期工作,请单击右侧的“整页”按钮,此时您将运行代码片段。

// get the canvas and store its original dimensions
var canvas = document.querySelector('.canvas');
var originalWidth = canvas.width; // px
var originalHeight = canvas.height; // px

// draw something on the canvas
var context = canvas.getContext('2d');
var blockSize = originalWidth / 5; // px
context.fillStyle = '#6495ED'; // nice cornflowerblue

for (var i = 0; i < originalWidth; i += blockSize) {
  for (var j = 0; j < originalHeight; j += blockSize) {
    context.fillRect(i, j, blockSize / 2, blockSize / 2);
  }
}

// get the bounds of the canvas element
function getBounds() {
  return canvas.getBoundingClientRect();
}

// get the offset of the canvas relative to the document
function getOffset() {
  var bounds = getBounds();
  return {
    x: bounds.left + window.scrollX,
    y: bounds.top + window.scrollY,
  };
}

// init the buttons for demonstration purposes
var resizeButton = document.querySelector('.resize');
var isResized = false;

resizeButton.addEventListener('click', function() {
  var newWidth = 50; // px
  var newHeight = 50; // px

  if (!isResized) {
    canvas.setAttribute('style', 'width: ' + newWidth + 'px; height: ' + newHeight + 'px');
  } else {
    canvas.removeAttribute('style');
  }

  updateDataList();
  isResized = !isResized;
});

var toggleButton = document.querySelector('.toggle');
var container = document.querySelector('.container');
var alignment = 'center'; // centered by default

// align the container element between left, center, and right
function alignContainer() {
  var label = '';

  switch (alignment) {
    // align to center
    case 'center':
      container.setAttribute('class', 'container');
      label = 'Centered';
      // next alingment;
      alignment = 'right';
      break;

      // align to right
    case 'right':
      container.setAttribute('class', 'container to-right');
      label = 'Right Aligned';
      // next alingment;
      alignment = 'bottom';
      break;

      // align to bottom
    case 'bottom':
      container.setAttribute('class', 'container to-bottom');
      label = 'Bottom Aligned';
      // next alingment;
      alignment = 'left';
      break;

      // align to left
    case 'left':
      container.setAttribute('class', 'container to-left');
      label = 'Left Aligned';
      // next alingment;
      alignment = 'top';
      break;

      // align to top
    case 'top':
      container.setAttribute('class', 'container to-top');
      label = 'Top Aligned';
      // next alingment;
      alignment = 'center';
      break;
  }

  toggleButton.innerHTML = label;
  updateDataList();
}

toggleButton.addEventListener('click', function() {
  alignContainer();
});

// update the data list according to the given data
var list = document.querySelector('.data');

function updateDataList(data) {
  var bounds = getBounds();
  var offset = getOffset();
  var currentWidth = bounds.width;
  var currentHeight = bounds.height;
  var scaleX = currentWidth / originalWidth;
  var scaleY = currentHeight / originalHeight;
  var value;
  var mouse;
  var mouseScaledX;
  var mouseScaledY;

  // update original dimensions
  value = 'width: ' + originalWidth + 'px, height: ' + originalHeight + 'px';
  list.querySelector('.original .value').innerHTML = value;

  // update scale
  value = 'x: ' + scaleX + ', y: ' + scaleY;
  list.querySelector('.scale .value').innerHTML = value;

  // update bounds
  value = 'x: ' + bounds.x + 'px, y: ' + bounds.y + 'px, ' +
    'width: ' + bounds.width + 'px, height: ' + bounds.height + 'px';
  list.querySelector('.bounds .value').innerHTML = value;

  // update mouse
  if (data && data.mouse) {
    mouse = data.mouse;
    mouseScaledX = mouse.unscaled.x * (1 / scaleX);
    mouseScaledY = mouse.unscaled.y * (1 / scaleY);

    value = 'unscaledX: ' + mouse.unscaled.x + 'px, unscaledY: ' + mouse.unscaled.y + 'px, ' +
      'scaledX: ' + mouseScaledX + 'px, scaledY: ' + mouseScaledY + 'px';
    list.querySelector('.mouse .value').innerHTML = value;
  }
}

// initially align container
alignContainer();

// update data according to the event
canvas.addEventListener('mousemove', (e) => {
  var mouseX = e.clientX - canvas.offsetLeft;
  var mouseY = e.clientY - canvas.offsetTop;

  updateDataList({
    mouse: {
      unscaled: {
        x: mouseX,
        y: mouseY
      }
    }
  });
});
.container {
  position: absolute;
  display: flex;
  
  flex-direction: column;
  justify-content: center;
  align-items: center;
  
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  
  background: #eee;
}

.container.to-top {
  justify-content: flex-start;
}

.container.to-bottom {
  justify-content: flex-end;
}

.container.to-left {
  align-items: flex-start;
}

.container.to-right {
  align-items: flex-end;
}

.wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
}

.canvas {
  background: white;
}

.button {
  margin-left: 30px;
  padding: 8px 14px;
  
  line-height: 1.5;
  font-size: 12px;
  font-weight: bold;
  text-transform: uppercase;
}

.data {
  list-style-type: none;
  display: block;
  margin-top: 25px;  
}

.data li + li {
  margin-top: 5px;
}

.data .title {
  color: #777;
  font-size: 13px;
  font-weight: bold;
  text-transform: uppercase;
}

.data .value {
  font-size: 12px;
  font-weight: bold;
}
<div class="container">
  <div class="wrapper">
    <canvas class="canvas" width="100" height="100"></canvas>
    <button class="button resize">resize<br>canvas</button>
    <button class="button toggle">toggle<br>alignment</button>
  </div>
  <ul class="data">
    <li class="original">
      <span class="title">Original:</span>
      <span class="value">width: 0, height: 0</span>
    </li>
    <li class="scale">
      <span class="title">Scale:</span>
      <span class="value">x: 0, y: 0</span>
    </li>
    <li class="bounds">
      <span class="title">Bounds:</span>
      <span class="value">[...]</span>
    </li>
    <li class="mouse">
      <span class="title">Mouse:</span>
      <span class="value">[...]</span>
    </li>
  </ul>
</div>


推荐阅读