Как получить координаты щелчка мыши на элементе холста?

Какой самый простой способ добавить обработчик события клика к элементу canvas, который вернет координаты x и y клика (относительно элемента canvas)?

Совместимость браузера не требуется, Safari, Opera и Firefox будут делать.

Ответ 1

Изменить 2018: этот ответ довольно старый, и он использует проверки для старых браузеров, которые больше не нужны, поскольку свойства clientX и clientY работают во всех текущих браузерах. Возможно, вы захотите проверить ответ Patriques для более простого, более свежего решения.

Оригинальный ответ:
Как описано в статье, которую я нашел тогда, но больше не существует:

var x;
var y;
if (e.pageX || e.pageY) { 
  x = e.pageX;
  y = e.pageY;
}
else { 
  x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 
  y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; 
} 
x -= gCanvasElement.offsetLeft;
y -= gCanvasElement.offsetTop;

Работал отлично для меня.

Ответ 2

Обновить (5/5/16): ответ patriques следует использовать вместо этого, поскольку он более простой и надежный.


Поскольку холст не всегда оформлен относительно всей страницы, canvas.offsetLeft/Top не всегда возвращает то, что вам нужно. Он вернет количество пикселей, которые он смещен относительно своего элемента offsetParent, который может быть чем-то вроде элемента div, содержащего холст с приложенным стилем position: relative. Для этого вам нужно пропустить цепочку offsetParent s, начиная с самого элемента canvas. Этот код отлично работает для меня, проверен в Firefox и Safari, но должен работать для всех.

function relMouseCoords(event){
    var totalOffsetX = 0;
    var totalOffsetY = 0;
    var canvasX = 0;
    var canvasY = 0;
    var currentElement = this;

    do{
        totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
        totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
    }
    while(currentElement = currentElement.offsetParent)

    canvasX = event.pageX - totalOffsetX;
    canvasY = event.pageY - totalOffsetY;

    return {x:canvasX, y:canvasY}
}
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;

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

coords = canvas.relMouseCoords(event);
canvasX = coords.x;
canvasY = coords.y;

Ответ 3

Если вам нравится простота, но все же нужна кросс-браузерная функциональность, я обнаружил, что это решение работает лучше всего для меня. Это упрощение решения @Aldekein, но без jQuery.

function getCursorPosition(canvas, event) {
    const rect = canvas.getBoundingClientRect()
    const x = event.clientX - rect.left
    const y = event.clientY - rect.top
    console.log("x: " + x + " y: " + y)
}

const canvas = document.querySelector('canvas')
canvas.addEventListener('mousedown', function(e) {
    getCursorPosition(canvas, e)
})

Ответ 4

Современный браузер теперь обрабатывает это для вас. Chrome, IE9 и Firefox поддерживают смещениеX/Y, как это, передавая событие из обработчика кликов.

function getRelativeCoords(event) {
    return { x: event.offsetX, y: event.offsetY };
}

Большинство современных браузеров также поддерживают layerX/Y, однако Chrome и IE используют layerX/Y для абсолютного смещения щелчка на странице, включая маржу, отступы и т.д. В Firefox уровни XXXXXXX и YXXXX эквивалентны, но смещение ранее не существовало. Итак, для совместимости с немного более старыми браузерами вы можете использовать:

function getRelativeCoords(event) {
    return { x: event.offsetX || event.layerX, y: event.offsetY || event.layerY };
}

Ответ 5

Согласно свежему Quirksmode методы clientX и clientY поддерживаются во всех основных браузерах. Итак, все готово - хороший рабочий код, который работает в элементе прокрутки на странице с полосами прокрутки:

function getCursorPosition(canvas, event) {
var x, y;

canoffset = $(canvas).offset();
x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - Math.floor(canoffset.left);
y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop - Math.floor(canoffset.top) + 1;

return [x,y];
}

Это также требует jQuery для $(canvas).offset().

Ответ 6

Я сделал полную демострацию, которая работает в каждом браузере с полным исходным кодом решения этой проблемы: Координаты щелчка мыши на холсте в Javascript. Чтобы попробовать демо, скопируйте код и вставьте его в текстовый редактор. Затем сохраните его как example.html и, наконец, откройте файл с помощью браузера.

Ответ 7

Вот небольшая модификация ответа Райана Артекона для холстов с переменной (%) шириной:

 HTMLCanvasElement.prototype.relMouseCoords = function (event) {
    var totalOffsetX = 0;
    var totalOffsetY = 0;
    var canvasX = 0;
    var canvasY = 0;
    var currentElement = this;

    do {
        totalOffsetX += currentElement.offsetLeft;
        totalOffsetY += currentElement.offsetTop;
    }
    while (currentElement = currentElement.offsetParent)

    canvasX = event.pageX - totalOffsetX;
    canvasY = event.pageY - totalOffsetY;

    // Fix for variable canvas width
    canvasX = Math.round( canvasX * (this.width / this.offsetWidth) );
    canvasY = Math.round( canvasY * (this.height / this.offsetHeight) );

    return {x:canvasX, y:canvasY}
}

Ответ 8

Будьте осторожны при выполнении преобразования координат; в событии клика есть несколько значений, отличных от кросс-браузера. Использование ClientX и ClientY в одиночку недостаточно, если окно браузера прокручивается (проверено в Firefox 3.5 и Chrome 3.0).

Этот режим quirks содержит более правильную функцию, которая может использовать либо pageX или pageY, либо комбинацию clientX с document.body.scrollLeft и clientY с document.body.scrollTop, чтобы вычислить координату клика относительно начала документа.

UPDATE: Кроме того, offsetLeft и offsetTop соответствуют размеру элемента, а не внутреннему размеру. Приложенное полотно с дополнением: не будет сообщать левую верхнюю часть области содержимого как offsetLeft. Существуют различные решения этой проблемы; простейшим может быть очистить все границы, padding и т.д. на самом холсте и вместо этого применить их к ящику, содержащему холст.

Ответ 9

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

Метод HTMLElement.getBoundingClientRect предназначен для обработки фактического положения экрана любого элемента. Это включает прокрутку, поэтому материал, такой как scrollTop, не требуется:

(из MDN) Размер прокрутки, выполненный в области области просмотра (или любой другой прокручиваемый элемент) учитывается при вычислении ограничивающий прямоугольник

Нормальное изображение

очень простой подход уже размещен здесь. Это правильно, если не используются никакие правила CSS.

Обработка растянутого холста/изображения

Если ширина пикселя изображения не соответствует ширине CSS, вам необходимо применить некоторое соотношение к значениям пикселей:

/* Returns pixel coordinates according to the pixel that under the mouse cursor**/
HTMLCanvasElement.prototype.relativeCoords = function(event) {
  var x,y;
  //This is the current screen rectangle of canvas
  var rect = this.getBoundingClientRect();
  var top = rect.top;
  var bottom = rect.bottom;
  var left = rect.left;
  var right = rect.right;
  //Recalculate mouse offsets to relative offsets
  x = event.clientX - left;
  y = event.clientY - top;
  //Also recalculate offsets of canvas is stretched
  var width = right - left;
  //I use this to reduce number of calculations for images that have normal size 
  if(this.width!=width) {
    var height = bottom - top;
    //changes coordinates by ratio
    x = x*(this.width/width);
    y = y*(this.height/height);
  } 
  //Return as an array
  return [x,y];
}

Пока холст не имеет границы, работает для растянутых изображений (jsFiddle).

Обработка границ CSS

Если холст имеет плотную границу, вещи становятся немного сложными. Вам буквально нужно будет вычесть границу из ограничивающего прямоугольника. Это можно сделать, используя .getComputedStyle. Этот ответ описывает процесс.

Затем функция немного растет:

/* Returns pixel coordinates according to the pixel that under the mouse cursor**/
HTMLCanvasElement.prototype.relativeCoords = function(event) {
  var x,y;
  //This is the current screen rectangle of canvas
  var rect = this.getBoundingClientRect();
  var top = rect.top;
  var bottom = rect.bottom;
  var left = rect.left;
  var right = rect.right;
  //Subtract border size
  // Get computed style
  var styling=getComputedStyle(this,null);
  // Turn the border widths in integers
  var topBorder=parseInt(styling.getPropertyValue('border-top-width'),10);
  var rightBorder=parseInt(styling.getPropertyValue('border-right-width'),10);
  var bottomBorder=parseInt(styling.getPropertyValue('border-bottom-width'),10);
  var leftBorder=parseInt(styling.getPropertyValue('border-left-width'),10);
  //Subtract border from rectangle
  left+=leftBorder;
  right-=rightBorder;
  top+=topBorder;
  bottom-=bottomBorder;
  //Proceed as usual
  ...
}

Я не могу думать ни о чем, что бы путало эту конечную функцию. Посмотрите на JsFiddle.

Примечания

Если вам не нравится изменять собственный prototype s, просто измените функцию и вызовите ее с помощью (canvas, event) (и замените любой this на canvas).

Ответ 10

Вот очень хороший учебник -

http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/

 <canvas id="myCanvas" width="578" height="200"></canvas>
<script>
  function writeMessage(canvas, message) {
    var context = canvas.getContext('2d');
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.font = '18pt Calibri';
    context.fillStyle = 'black';
    context.fillText(message, 10, 25);
  }
  function getMousePos(canvas, evt) {
    var rect = canvas.getBoundingClientRect();
    return {
      x: evt.clientX - rect.left,
      y: evt.clientY - rect.top
    };
  }
  var canvas = document.getElementById('myCanvas');
  var context = canvas.getContext('2d');

  canvas.addEventListener('mousemove', function(evt) {
    var mousePos = getMousePos(canvas, evt);
    var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
    writeMessage(canvas, message);
  }, false);

надеюсь, что это поможет!

Ответ 11

Используя jQuery в 2016 году, чтобы получить координаты кликов относительно холста, я делаю:

$(canvas).click(function(jqEvent) {
    var coords = {
        x: jqEvent.pageX - $(canvas).offset().left,
        y: jqEvent.pageY - $(canvas).offset().top
    };
});

Это работает, поскольку смещение холста() и jqEvent.pageX/Y относятся к документу независимо от положения прокрутки.

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

var logicalCoords = {
    x: coords.x * (canvas.width / $(canvas).width()),
    y: coords.y * (canvas.height / $(canvas).height())
}

Ответ 12

Я рекомендую эту ссылку - http://miloq.blogspot.in/2011/05/coordinates-mouse-click-canvas.html

<style type="text/css">

  #canvas{background-color: #000;}

</style>

<script type="text/javascript">

  document.addEventListener("DOMContentLoaded", init, false);

  function init()
  {
    var canvas = document.getElementById("canvas");
    canvas.addEventListener("mousedown", getPosition, false);
  }

  function getPosition(event)
  {
    var x = new Number();
    var y = new Number();
    var canvas = document.getElementById("canvas");

    if (event.x != undefined && event.y != undefined)
    {
      x = event.x;
      y = event.y;
    }
    else // Firefox method to get the position
    {
      x = event.clientX + document.body.scrollLeft +
          document.documentElement.scrollLeft;
      y = event.clientY + document.body.scrollTop +
          document.documentElement.scrollTop;
    }

    x -= canvas.offsetLeft;
    y -= canvas.offsetTop;

    alert("x: " + x + "  y: " + y);
  }

</script>

Ответ 14

Вы можете просто сделать:

var canvas = yourCanvasElement;
var mouseX = (event.clientX - (canvas.offsetLeft - canvas.scrollLeft)) - 2;
var mouseY = (event.clientY - (canvas.offsetTop - canvas.scrollTop)) - 2;

Это даст вам точную позицию указателя мыши.

Ответ 15

Смотрите демонстрацию в http://jsbin.com/ApuJOSA/1/edit?html,output.

  function mousePositionOnCanvas(e) {
      var el=e.target, c=el;
      var scaleX = c.width/c.offsetWidth || 1;
      var scaleY = c.height/c.offsetHeight || 1;

      if (!isNaN(e.offsetX)) 
          return { x:e.offsetX*scaleX, y:e.offsetY*scaleY };

      var x=e.pageX, y=e.pageY;
      do {
        x -= el.offsetLeft;
        y -= el.offsetTop;
        el = el.offsetParent;
      } while (el);
      return { x: x*scaleX, y: y*scaleY };
  }

Ответ 16

Так что это простая, но немного более сложная тема, чем кажется.

Во-первых, здесь обычно возникают спорные вопросы.

  1. Как получить элемент относительно координат мыши

  2. Как получить координаты пиксельной мыши для 2D Canvas API или WebGL

итак, ответы

Как получить элемент относительно координат мыши

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

На вопрос "Как получить относительные координаты мыши в холсте" есть 2 простых ответа.

Простой ответ № 1 использует offsetX и offsetY

canvas.addEventListner('mousemove', (e) => {
  const x = e.offsetX;
  const y = e.offsetY;
});

Этот ответ работает в Chrome, Firefox и Safari. В отличие от всех других значений событий offsetX и offsetY учитывают преобразования CSS.

Самая большая проблема с offsetX и offsetY заключается в том, что по состоянию на 2019/05 они не существуют в сенсорных событиях и поэтому не могут использоваться с iOS Safari. Они существуют в Pointer Events, которые существуют в Chrome и Firefox, но не в Safari, хотя, очевидно, Safari работает над этим.

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

Простой ответ №2: используйте clientX, clientY и canvas.getBoundingClientRect

Если вас не волнуют преобразования CSS, следующий простейший ответ - вызвать canvas. getBoundingClientRect() canvas. getBoundingClientRect() и clientX левый из clientX и top из clientY как в

canvas.addEventListener('mousemove', (e) => {
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
});

Это будет работать до тех пор, пока нет CSS-преобразований. Он также работает с сенсорными событиями и будет работать с Safari iOS

canvas.addEventListener('touchmove', (e) => {
  const rect = canvas. getBoundingClientRect();
  const x = e.touches[0].clientX - rect.left;
  const y = e.touches[0].clientY - rect.top;
});

Как получить координаты пиксельной мыши для 2D Canvas API

Для этого нам нужно взять значения, которые мы получили выше, и преобразовать размер отображаемого холста в количество пикселей в самом холсте.

с canvas.getBoundingClientRect и clientX и clientY

canvas.addEventListener('mousemove', (e) => {
  const rect = canvas.getBoundingClientRect();
  const elementRelativeX = e.clientX - rect.left;
  const elementRelativeY = e.clientY - rect.top;
  const canvasRelativeX = elementRelativeX * canvas.width / rect.width;
  const canvasRelativeY = elementRelativeY * canvas.height / rect.height;
});

или со offsetX и offsetY

canvas.addEventListener('mousemove', (e) => {
  const elementRelativeX = e.offsetX;
  const elementRelativeX = e.offsetY;
  const canvasRelativeX = elementRelativeX * canvas.width / canvas.clientWidth;
  const canvasRelativeY = elementRelativeX * canvas.height / canvas.clientHeight;
});

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

Рабочий пример с использованием event.offsetX, event.offsetY

[...document.querySelectorAll('canvas')].forEach((canvas) => {
  const ctx = canvas.getContext('2d');
  ctx.canvas.width  = ctx.canvas.clientWidth;
  ctx.canvas.height = ctx.canvas.clientHeight;
  let count = 0;

  function draw(e, radius = 1) {
    const pos = {
      x: e.offsetX * canvas.width  / canvas.clientWidth,
      y: e.offsetY * canvas.height / canvas.clientHeight,
    };
    document.querySelector('#debug').textContent = count;
    ctx.beginPath();
    ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
    ctx.fillStyle = hsl((count++ % 100) / 100, 1, 0.5);
    ctx.fill();
  }

  function preventDefault(e) {
    e.preventDefault();
  }

  if (window.PointerEvent) {
    canvas.addEventListener('pointermove', (e) => {
      draw(e, Math.max(Math.max(e.width, e.height) / 2, 1));
    });
    canvas.addEventListener('touchstart', preventDefault, {passive: false});
    canvas.addEventListener('touchmove', preventDefault, {passive: false});
  } else {
    canvas.addEventListener('mousemove', draw);
    canvas.addEventListener('mousedown', preventDefault);
  }
});

function hsl(h, s, l) {
  return 'hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)';
}
.scene {
  width: 200px;
  height: 200px;
  perspective: 600px;
}

.cube {
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;
  animation-duration: 16s;
  animation-name: rotate;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

@keyframes rotate {
  from { transform: translateZ(-100px) rotateX(  0deg) rotateY(  0deg); }
  to   { transform: translateZ(-100px) rotateX(360deg) rotateY(720deg); }
}

.cube__face {
  position: absolute;
  width: 200px;
  height: 200px;
  display: block;
}

.cube__face--front  { background: rgba(255, 0, 0, 0.2); transform: rotateY(  0deg) translateZ(100px); }
.cube__face--right  { background: rgba(0, 255, 0, 0.2); transform: rotateY( 90deg) translateZ(100px); }
.cube__face--back   { background: rgba(0, 0, 255, 0.2); transform: rotateY(180deg) translateZ(100px); }
.cube__face--left   { background: rgba(255, 255, 0, 0.2); transform: rotateY(-90deg) translateZ(100px); }
.cube__face--top    { background: rgba(0, 255, 255, 0.2); transform: rotateX( 90deg) translateZ(100px); }
.cube__face--bottom { background: rgba(255, 0, 255, 0.2); transform: rotateX(-90deg) translateZ(100px); }
<div class="scene">
  <div class="cube">
    <canvas class="cube__face cube__face--front"></canvas>
    <canvas class="cube__face cube__face--back"></canvas>
    <canvas class="cube__face cube__face--right"></canvas>
    <canvas class="cube__face cube__face--left"></canvas>
    <canvas class="cube__face cube__face--top"></canvas>
    <canvas class="cube__face cube__face--bottom"></canvas>
  </div>
</div>
<pre id="debug"></pre>

Ответ 17

Эй, это в dojo, просто запустите его, что у меня уже был код для проекта.

Должно быть довольно очевидно, как преобразовать его обратно в не-w591 > ванильный JavaScript.

  function onMouseClick(e) {
      var x = e.clientX;
      var y = e.clientY;
  }
  var canvas = dojo.byId(canvasId);
  dojo.connect(canvas,"click",onMouseClick);

Надеюсь, что это поможет.

Ответ 18

Вот некоторые модификации вышеупомянутого решения Райана Артекона.

function myGetPxStyle(e,p)
{
    var r=window.getComputedStyle?window.getComputedStyle(e,null)[p]:"";
    return parseFloat(r);
}

function myGetClick=function(ev)
{
    // {x:ev.layerX,y:ev.layerY} doesn't work when zooming with mac chrome 27
    // {x:ev.clientX,y:ev.clientY} not supported by mac firefox 21
    // document.body.scrollLeft and document.body.scrollTop seem required when scrolling on iPad
    // html is not an offsetParent of body but can have non null offsetX or offsetY (case of wordpress 3.5.1 admin pages for instance)
    // html.offsetX and html.offsetY don't work with mac firefox 21

    var offsetX=0,offsetY=0,e=this,x,y;
    var htmls=document.getElementsByTagName("html"),html=(htmls?htmls[0]:0);

    do
    {
        offsetX+=e.offsetLeft-e.scrollLeft;
        offsetY+=e.offsetTop-e.scrollTop;
    } while (e=e.offsetParent);

    if (html)
    {
        offsetX+=myGetPxStyle(html,"marginLeft");
        offsetY+=myGetPxStyle(html,"marginTop");
    }

    x=ev.pageX-offsetX-document.body.scrollLeft;
    y=ev.pageY-offsetY-document.body.scrollTop;
    return {x:x,y:y};
}

Ответ 19

Во-первых, как говорили другие, вам нужна функция, чтобы получить позицию элемента холста. Здесь метод, который немного более изящный, чем некоторые другие на этой странице (IMHO). Вы можете передать ему любой элемент и получить его положение в документе:

function findPos(obj) {
    var curleft = 0, curtop = 0;
    if (obj.offsetParent) {
        do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
        return { x: curleft, y: curtop };
    }
    return undefined;
}

Теперь вычислите текущую позицию курсора относительно этого:

$('#canvas').mousemove(function(e) {
    var pos = findPos(this);
    var x = e.pageX - pos.x;
    var y = e.pageY - pos.y;
    var coordinateDisplay = "x=" + x + ", y=" + y;
    writeCoordinateDisplay(coordinateDisplay);
});

Обратите внимание, что я отделил общую функцию findPos от кода обработки событий. (Как и должно быть. Мы должны стараться, чтобы наши функции выполнялись по одной задаче каждый.)

Значения offsetLeft и offsetTop относятся к offsetParent, что может быть некоторой оболочкой div node (или что-то еще, если на то пошло). Когда нет элемента, обертывающего canvas, они относятся к body, поэтому нет смещения для вычитания. Вот почему нам нужно определить положение холста, прежде чем мы сможем сделать что-нибудь еще.

Similary, e.pageX и e.pageY указывают положение курсора относительно документа. Поэтому мы вычитаем смещение холста из этих значений, чтобы достичь истинного положения.

Альтернативой позиционированным элементам является прямое использование значений e.layerX и e.layerY. Это менее надежно, чем метод выше по двум причинам:

  • Эти значения также относятся ко всему документу, когда событие не происходит внутри позиционированного элемента
  • Они не являются частью стандартного

Ответ 20

ThreeJS r77

var x = event.offsetX == undefined ? event.layerX : event.offsetX;
var y = event.offsetY == undefined ? event.layerY : event.offsetY;

mouse2D.x = ( x / renderer.domElement.width ) * 2 - 1;
mouse2D.y = - ( y / renderer.domElement.height ) * 2 + 1;

После нескольких решений. Это сработало для меня. Могу помочь кому-то еще, следовательно, опубликовать. Получил это от здесь

Ответ 21

Вот упрощенное решение (это не работает с границами/прокруткой):

function click(event) {
    const bound = event.target.getBoundingClientRect();

    const xMult = bound.width / can.width;
    const yMult = bound.height / can.height;

    return {
        x: Math.floor(event.offsetX / xMult),
        y: Math.floor(event.offsetY / yMult),
    };
}

Ответ 22

Я создавал приложение, имеющее холст поверх PDF, которое включало в себя множество изменений размера холста, таких как увеличение и уменьшение размера PDF, и, в свою очередь, при каждом увеличении/уменьшении PDF мне приходилось изменять размер холста, чтобы адаптировать его. размер pdf, я просмотрел множество ответов в stackOverflow и не нашел идеального решения, которое в конечном итоге решит проблему.

Я использовал rxjs и angular 6 и не нашел ответа, относящегося к самой новой версии.

Вот весь фрагмент кода, который был бы полезен для тех, кто использует rxjs для рисования поверх холста.

  private captureEvents(canvasEl: HTMLCanvasElement) {

    this.drawingSubscription = fromEvent(canvasEl, 'mousedown')
      .pipe(
        switchMap((e: any) => {

          return fromEvent(canvasEl, 'mousemove')
            .pipe(
              takeUntil(fromEvent(canvasEl, 'mouseup').do((event: WheelEvent) => {
                const prevPos = {
                  x: null,
                  y: null
                };
              })),

              takeUntil(fromEvent(canvasEl, 'mouseleave')),
              pairwise()
            )
        })
      )
      .subscribe((res: [MouseEvent, MouseEvent]) => {
        const rect = this.cx.canvas.getBoundingClientRect();
        const prevPos = {
          x: Math.floor( ( res[0].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
          y:  Math.floor( ( res[0].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
        };
        const currentPos = {
          x: Math.floor( ( res[1].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
          y: Math.floor( ( res[1].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
        };

        this.coordinatesArray[this.file.current_slide - 1].push(prevPos);
        this.drawOnCanvas(prevPos, currentPos);
      });
  }

А вот фрагмент, который исправляет координаты мыши относительно размера холста, независимо от того, как вы увеличиваете/уменьшаете холст.

const prevPos = {
  x: Math.floor( ( res[0].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
  y:  Math.floor( ( res[0].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
};
const currentPos = {
  x: Math.floor( ( res[1].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
  y: Math.floor( ( res[1].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
};