Обновить холст при перемещении мыши

Основная идея исходит из карты игры. Согласно моему обзору кода, карта представляет собой полностраничный холст. У меня нет проблем с рисованием изображений на холсте. Мой вопрос заключается в том, как обнаружить дома на карте и обновить холст или даже добавить к нему возможность щелчка. Я прикрепил код GIF и HTML из оригинальной игры, чтобы лучше понять мой запрос.

<div id = "canvasBorder"><canvas id = "canvasMap"></canvas></div>

Хорошо, это мой код. Это просто. Я нарисовал дома на карте в соответствии с основным изображением, которое большое на холсте.

function onClick2() {
  const imagePath = '/lobby/map.png';

  //Image Positions and Width/Height
  const array = [
    { x: 1764, y: 1104, w: 126, h: 84 },
    { x: 0, y: 1188, w: 126, h: 84 },
    { x: 126, y: 1188, w: 126, h: 84 },
    { x: 2090, y: 340, w: 126, h: 68 },
    { x: 126, y: 1188, w: 126, h: 84 },
  ];

  if (canvasRef?.current) {
    let x = canvasRef?.current.getContext('2d');

    let img = new Image();
    img.src = path;

    //Draw Map Blocks
    //Here I deleted the extra codes, I just wanted to show that it was done this way.
    if (x) {
      x.drawImage(
        img,
        array[3].x,
        array[3].y,
        array[3].w,
        array[3].h,
        0,
        0,
        array[3].w,
        array[3].h
      );
    }
  }
}

Это мой результат:

Здесь мне нужно ваше руководство, чтобы понять хитрость реализации. Здесь нам нужно распознавать движение мыши на изображении или нам нужно несколько квадратов, которые вращаются и имеют изображение и работают с функцией isPointInPath. Если мы продолжим второй способ, который я упомянул, то для рисования квадратов нам понадобится rotate(-0.25 * Math.PI);

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
3
0
246
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Предлагаю изучить события JavaScript.

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

const canvasMap = document.getElementById("canvasMap");

// First method
canvasMap.onmousemove = function(event) {
    console.info(event);
}

// Second method
canvasMap.addEventListener("mousemove", function(event) {
    console.info(event);
    // If collision with tile, draw white border around it.
});

// For click events
canvasMap.addEventListener("click", function(event) {
    console.info(event);
});

Затем вы можете взять свойства clientX и clientY и использовать их, чтобы вычислить, с каким тайлом на холсте пересекается мышь.

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

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

Для обнаружения столкновений вы можете обратиться к этому посту stackoverflow и провести небольшое исследование.

Ответ принят как подходящий

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

  • Позиция курсора v_C = (x, y) находится в каноническом базисе, C.

  • У вас есть альтернативный базис B (ваша 2,5D-сетка) и его альтернативные базисные векторы в базисе C:

    columnVector_C = (cellHalfSizeLong, cellHalfSizeShort)
    rowsVector_C = (-cellHalfSizeLong, cellHalfSizeShort)
    
  • Вы помещаете их в столбцы матрицы M_BC:

    const M_BC = [
      [cellHalfSizeLong, -cellHalfSizeLong],
      [cellHalfSizeShort, cellHalfSizeShort],
    ];
    

    Эта матрица помогает вам переводить векторы из базиса B в C.

  • Инвертируйте эту матрицу, чтобы получить M_CB. Эта матрица помогает вам переводить векторы из базиса C в B.

  • v_B = M_CB * v_C

  • Наконец, укажите координаты. Это подскажет вам, какую ячейку выбрать/выделить в сетке 2.5D.

Все еще:

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

  • Это легче увидеть, чем прочитать, поэтому для начала посмотрите это: 3Blue1Brown - Смена основы | Глава 13, Сущность линейной алгебры.

Вот рабочий пример:

// Some basic utils to work with matrices and vectors:

function matrixVectorMultiply(matrix, vector) {
  let result = [];

  for (let i = 0; i < matrix.length; i++) {
    let sum = 0;

    for (let j = 0; j < vector.length; j++) {
      sum += matrix[i][j] * vector[j];
    }

    result.push(sum);
  }

  return result;
}

function invertMatrix(matrix) {
  const n = matrix.length;

  let identity = [];

  for (let i = 0; i < n; i++) {
    identity.push([]);

    for (let j = 0; j < n; j++) {
      identity[i].push(i === j ? 1 : 0);
    }
  }

  // Apply Gauss-Jordan elimination:

  for (let i = 0; i < n; i++) {
    let pivot = matrix[i][i];

    for (let j = 0; j < n; j++) {
      matrix[i][j] /= pivot;
      identity[i][j] /= pivot;
    }

    for (let k = 0; k < n; k++) {
      if (k !== i) {
        let factor = matrix[k][i];

        for (let j = 0; j < n; j++) {
          matrix[k][j] -= factor * matrix[i][j];
          identity[k][j] -= factor * identity[i][j];
        }
      }
    }
  }

  return identity;
}

// Define the grid data (colors of each cell):

const gridData = [
  ['#008800', '#00FF00', '#008800', '#00FF00', '#008800', '#00FF00'],
  ['#00FF00', '#008800', '#00FF00', '#008800', '#00FF00', '#008800'],
  ['#008800', '#00FF00', '#000088', '#00FF00', '#008800', '#00FF00'],
  ['#00FF00', '#008800', '#00FF00', '#008800', '#00FF00', '#000088'],
  ['#008800', '#00FF00', '#008800', '#00FF00', '#000088', '#0000FF'],
  ['#00FF00', '#008800', '#00FF00', '#000088', '#0000FF', '#000088'],
];

// This is just for the demo. In a real application, the grid data matrix would
// probably contain all the information on each cell objects (array items):

const gridColorToType = {
  '#008800': 'Grass',
  '#00FF00': 'Grass',
  '#000088': 'Water',
  '#0000FF': 'Water',
};

const selectedCellBolor = '#000000';

// Get the UI elements:

const positionLabelElement = document.getElementById('positionLabel');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

positionLabelElement.textContent = ' ';

// Adjust the canvas to the window:

const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;

// Grid sizing params:

const cellSizeLong = 100;
const cellHalfSizeLong = cellSizeLong / 2;
const cellSizeShort = cellSizeLong / 3;
const cellHalfSizeShort = cellSizeShort / 2;

// Keep track of the selected/highlighted cell:

let currentRow = 0;
let currentCol = 0;

// Drawing functions:

function drawCell(ctx, color, row, col) {
  ctx.fillStyle = color;

  // Calculate the position of the cell
  const x = (col - row) * cellHalfSizeLong + width / 2;
  const y = (col + row) * cellHalfSizeShort;

  // Fill:
  
  ctx.beginPath();
  ctx.moveTo(x, y);
  ctx.lineTo(x + cellHalfSizeLong, y + cellHalfSizeShort);
  ctx.lineTo(x, y + cellSizeShort);
  ctx.lineTo(x - cellHalfSizeLong, y + cellHalfSizeShort);
  ctx.closePath();
  ctx.fill();

  // Border:
  ctx.strokeStyle = '#000000';
  ctx.stroke();
}

function drawBoard() {
  ctx.clearRect(0, 0, width, height);
      
  const numRows = gridData.length;
  const numCols = gridData[0].length;

  // Draw all the cells in their respective color:
  
  for (let row = 0; row < numRows; ++row) {
    for (let col = 0; col < numCols; ++col) {      
      drawCell(ctx, gridData[row][col], row, col);
    }
  }
  
  // And re-draw the selected one on top (you might want to do this differently):
  drawCell(ctx, selectedCellBolor, currentRow, currentCol);
}

canvas.addEventListener('mousemove', () => {
    const x_C = width / 2 - event.clientX;
    const y_C = event.clientY;
    
    // First column is the columns vector in the 2.5D grid.
    // Second column is the rows vector in the 2.5 grid.
    const M_BC = [
      [cellHalfSizeLong, -cellHalfSizeLong],
      [cellHalfSizeShort, cellHalfSizeShort],
    ];
    
    // We need the inverse of that matrix to translate canonical basis
    // coordinates to coordinates in the 2.5D space's base:
    const M_CB = invertMatrix(M_BC);
    
    const [x_B, y_B] = matrixVectorMultiply(M_CB, [x_C, y_C]);
    const int_x_B = Math.floor(x_B);
    const int_y_B = Math.floor(y_B);
    
    currentRow = int_x_B;
    currentCol = int_y_B;
    
    const cellType = gridColorToType[gridData[currentRow]?.[currentCol]] || 'Void';
    
    positionLabelElement.textContent = `(${
      (x_C | 0).toFixed().padStart(4, ' ')
    }, ${
      (y_C | 0).toFixed().padStart(4, ' ')
    }) => (${
      x_B.toFixed(2).padStart(5, ' ')
    }, ${
      y_B.toFixed(2).padStart(5, ' ')
    }) => (${
      int_x_B.toFixed().padStart(2, ' ')
    }, ${
      int_y_B.toFixed().padStart(2, ' ')
    }) => ${ cellType }`;    
    
    requestAnimationFrame(() => {
      drawBoard();
    });
});

drawBoard();
body {
  background: #777;
}

#canvas {
  position: fixed;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
}

#positionLabel {
  position: fixed;
  bottom: 0;
  left: 0;
  background: rgba(255, 255, 255, .5); 
  padding: 8px;
  border-radius: 0 4px 0 0;
  font-family: monospace;
  font-weight: bold;
  white-space: pre;
  backdrop-filter: blur(8px);
  pointer-events: none;
}
<canvas id = "canvas"></canvas>

<div id = "positionLabel"> <div>

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

Другие вопросы по теме