Перейти к содержимому
Все статьи
Разработка

Клик в Canvas попадает мимо: как согласовать координаты

·27 октября 2026 г.· 6 мин чтения
Сколько будет стоить ваш проект?Рассчитать стоимость

Если клик в Canvas попадает мимо объекта после масштабирования, проверьте всю цепочку координат: окно, отображаемый размер элемента, внутреннее разрешение и пространство сцены. Координату указателя нужно преобразовать в ту систему, где проверяются границы объекта. Универсальное деление на devicePixelRatio не решает задачу, потому что CSS-масштаб и преобразования сцены могут быть независимыми.

Сначала назовите систему каждой точки

В описании clientX MDN координата относится к области просмотра, а не к началу документа или холста. Для проверки попадания недостаточно сравнить её с положением объекта внутри сцены. Одинаково называющиеся x и y в разных частях программы могут обозначать совершенно разные величины.

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

Ограниченный учебный пример

Предположим, холст расположен без CSS-поворота, рамки и внутренних отступов. Его отображаемый размер —400×200 CSS-пикселей, внутренний —800×400. Левый верхний угол относительно окна находится в точке (100; 50). Контекст рисования имеет только масштаб 2×2; иных преобразований сцены нет. Это вымышленные данные для проверки арифметики, не измерения реального устройства.

Пользователь кликает в точке (300; 150) относительно окна. После вычитания положения холста получаем 200,100 в отображаемых CSS-координатах. Для внутреннего разрешения умножаем каждую ось на отношение соответствующих размеров: получаем 400,200. Затем обращаем заданный масштаб контекста и получаем 200,100 в координатах сцены.

ПространствоИсточникПреобразование в примереПолученная точкаОшибка при смешении
Область просмотраclientX и соответствующая вертикальная координатаИсходное событие300; 150Сравнение с объектом сцены без вычитания положения элемента
Отображаемый холстСвежие границы элементаВычесть 100 по X и 50 по Y200; 100Повторно добавить прокрутку к уже согласованной системе окна
Внутреннее разрешениеРазмер буфера 800×400 и CSS-размер 400×200Умножить обе оси на 2400; 200Считать CSS-точку готовой координатой буфера
СценаЗаданный масштаб контекста 2×2Применить обратный масштаб200; 100Проверять границы сцены координатой внутреннего буфера

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

Уменьшим CSS-размер, сохранив внутреннюю сцену

Оставим внутренний размер 800×400 и масштаб контекста 2×2, но отобразим холст в 200×100 CSS-пикселях. Клик в центре теперь будет иметь локальную CSS-точку 100,50. Отношение внутреннего и отображаемого размера стало 4, поэтому координата буфера снова 400,200, а сцены —200,100. Объект в центре остаётся тем же.

Простое деление локальной точки на 2 дало бы 50,25 и попало в другое место. Здесь нельзя заменить отношение размеров предполагаемым значением плотности экрана. В документации devicePixelRatio оно описывает отношение физических и CSS-пикселей устройства. Внутренний размер вашего Canvas задаётся отдельно принятой схемой рисования.

Где заканчивается простая формула

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

Если есть рамка, отступы, CSS-поворот или преобразованные предки, требуется соответствующая модель геометрии. Окружающий прямоугольник повёрнутой фигуры не является готовой системой её локальных осей. Не распространяйте сокращённую формулу на такие условия без вывода и испытаний. Для интерактивного веб-приложения эти ограничения нужно зафиксировать в задаче.

Камера и масштаб сцены — ещё один этап

Объект может рисоваться после сдвига камеры, масштабирования и других преобразований. Проверка попадания должна использовать согласованный обратный путь к его системе. Метод getTransform позволяет получить текущую матрицу контекста. Само получение матрицы не преобразует точку автоматически: её нужно правильно применить по принятой архитектуре.

Различайте преобразование контекста и собственную модель сцены. Если игра хранит камеру отдельно и вручную рассчитывает позиции, одна матрица контекста может не описывать весь путь. При разработке браузерной игры полезно иметь единые согласованные преобразования для рисования и проверки попадания, вместо независимых формул в каждом обработчике.

Изменение окна должно обновлять согласованные величины

После изменения CSS-размера, прокрутки или размещения элемента проверьте актуальность используемых границ. Кешировать их можно только с понятным правилом обновления. Иначе расчёт верен для вчерашнего прямоугольника, а объект уже находится в другом месте. При изменении внутреннего разрешения отдельно проверьте восстановление нужного состояния рисования.

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

Проверять центр недостаточно

  1. Зафиксируйте размеры отображаемой области, внутреннего буфера и системы сцены. Отдельно перечислите рамки, отступы и все преобразования.
  2. На учебном объекте выберите центр, точки у краёв и точку снаружи. Рассчитайте ожидаемую принадлежность до обработки события.
  3. Проследите каждую точку по этапам, не записывая только итог. Найдите первый шаг, где фактическая величина отличается от ожидаемой.
  4. Измените CSS-размер, прокрутите страницу и повторите проверку после перемещения блока. Используемые границы должны оставаться актуальными.
  5. Добавьте только те преобразования камеры и условия экрана, которые входят в поддержку продукта. Проверьте обратный путь отдельно для них.
  6. Сверьте выделенный объект с нарисованным на конечном экране. Не скрывайте ошибку произвольным расширением области попадания без согласованного смысла.

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

Вопросы и ответы

Почему Canvas mouse coordinates не совпадают с объектом?

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

Нужно ли делить координаты Canvas на devicePixelRatio?

Только если это действительно следует из вашей схемы размеров и преобразований. Универсального правила нет: CSS-масштаб, внутренний буфер и камера могут задаваться независимо.

Что возвращает getBoundingClientRect для этой проверки?

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

Нужна помощь с проектом?

Обсудим задачу и предложим решение — от сайта до SaaS и защиты.

Связаться