Managing the hovering of a mouse over an image within an isometric grid displayed on a

I have implemented an isometric grid in HTML canvas.

My goal is to handle mouse hover events on the buildings within the grid.

Some buildings will vary in heights.

In the image below, you can see that when hovering over a tile, the highlighted area shifts if the mouse pointer is not directly on the ground tile or is positioned within the building image itself.

I am looking for a solution to enable clicking on each individual building. How can this issue be resolved?

Main basic functions:

  let applied_map = ref([]); // tileMap
  let tile_images = ref([]); // contains loaded images for canvas consumption
  let tile_height = ref(50);
  let tile_width = ref(100);

  const renderTiles = (x, y) => {
    // code block for rendering tiles
  };

  const renderTileBackground = (x, y, width, height) => {
    // function implementation for rendering tile backgrounds
  };

  const renderTexturedTile = (imgSrc, x, y, tileHeight) => {
    // function implementation for rendering textured tiles
  };

  const renderTileHover = (x, y, width, height) => {
    // function implementation for rendering hover effect on tiles
  };

https://i.sstatic.net/ZWbNy.png

Updates after answer below

After following Helder Sepulveda's suggestion, I created a new function called drawCube.

This function has been integrated into my click functionality and the renderTiles method. It generates a cube with three faces, aligning it with the position of the building and storing the path for global reference. The cube adjusts its position based on the isometric grid layout.

https://i.sstatic.net/Y2IGt.jpg https://i.sstatic.net/b1kj0.jpg


      //...some code within click function
      //...
      if (tile_images.value[tileIndex] !== undefined) {
        drawCube(
          hoverTileX.value + tile_height.value,
          hoverTileY.value +
            Number(tile_images.value[tileIndex].img.height / 2) -
            10,
          tile_height.value,
          tile_height.value,
          Number(tile_images.value[tileIndex].img.height / 2),
          ctx.value,
          {
            tile_index: tileIndex - 1 < 0 ? 0 : tileIndex - 1,
          }
        );
      }

This is the drawCube function:


const drawCube = (x, y, wx, wy, h, the_ctx, options = {}) => {
    // function definition for drawing a cube
};

Answer №1

To effectively detect mouseover on intricate shapes, it is recommended to utilize Path2d.
https://developer.mozilla.org/en-US/docs/Web/API/Path2D
This allows for the creation of custom shapes with access to isPointInPath which detects if the mouse is over the shape.
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInPath

Below is a concise illustration:

class Shape {
  constructor(x, y, width, height) {
    this.path = new Path2D()
    this.path.arc(x, y, 12, 0, 2 * Math.PI)
    this.path.arc(x, y - 9, 8, 0, 1.5 * Math.PI)
    this.path.lineTo(x + width / 2, y)
    this.path.lineTo(x, y + height / 2)
    this.path.lineTo(x - width / 2, y)
    this.path.lineTo(x, y - height / 2)
    this.path.lineTo(x + width / 2, y)
  }

  draw(ctx, pos) {
    ctx.beginPath()
    ctx.fillStyle = ctx.isPointInPath(this.path, pos.x, pos.y) ? "red" : "green"
    ctx.fill(this.path)
  }
}

function getMousePos(canvas, evt) {
  var rect = canvas.getBoundingClientRect()
  return {
    x: evt.clientX - rect.left,
    y: evt.clientY - rect.top
  }
}

var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d")

shapes = []
for (var i = 0; i < 4; i++) {
  for (var j = 0; j < 4; j++) {
    shapes.push(new Shape(50 + i * 40, 40 + j * 40, 40, 20))
  }
}

canvas.addEventListener("mousemove", function(evt) {
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    var mousePos = getMousePos(canvas, evt)
    shapes.forEach((s) => {s.draw(ctx, mousePos)})
  },
  false
)
shapes.forEach((s) => {
  s.draw(ctx, {x: 0, y: 0})
})
<canvas id="canvas" width="200" height="200"></canvas>

This example showcases a "complex" shape comprised of arcs and lines, changing color to red upon hovering the shape.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for saving a JavaScript object into a JSON file

Let's discuss how to save the following data: myJSONtable into a JSON file using the following method: fs.writeFile('./users.json', JSON.stringify(myJSONtable, null, 4), 'utf-8', function (err) { if (err) throw err ...

Set a timeout for a single asynchronous request

Is there a way to implement a setTimeout for only one asynchronous call? I need to set a timeout before calling the GetData function from the dataservice, but it should be specific to only one asynchronous call. Any suggestions? Thank you. #html code < ...

Choosing nested JSON elements to populate selection menus

Here is an example of JSON data format: "games": [{ "gameType": "RPG", "publishers": [{ "publisher": "Square", "titles": [{ "title": "Final Fantasy", "gameReleases": [ 2006, 2008, 2010, 2012, 2013, 2014 ] ...

Guide on implementing enums (or const) in VueJS

Seeking help on a seemingly simple task, I am trying to understand how to use "enums" in VueJS. In my file named LandingPage.js, I have the following code: const Form = { LOGIN: 0, SIGN_UP: 1, FORGOT_PASSWORD: 2, }; function main() { new Vue({ ...

The value control input does not get properly updated by ngModelChange

Having difficulty updating an input as the user types. Trying to collect a code from the user that follows this format: 354e-fab4 (2 groups of 4 alphanumeric characters separated by '-'). The user should not need to type the '-', as it ...

Consolidating various JavaScript events into one single event

Whenever a user types a key, my function is triggered. I want to consolidate these events so they only occur at a maximum rate of 500ms. Is there a simple method to achieve this in Javascript or through a commonly used library? Or should I create my own t ...

Troubleshooting Problem with jQuery Function Call in Drupal 7

Encountering a particular issue in Drupal, though it may not be exclusively related to Drupal. This is the simple javascript code I am working with: (function ($) { function testing(){ alert('TEST function responding!'); } })(jQuery); ...

[entity: undefined prototype] { type: 'clip', info: 'Watch my latest video!!' } using nodejs - multer

import routes from "./routes"; import multer from "multer"; const multerVideo = multer({ dest: "videos/" }); export const localsMiddleware = (req, res, next) => { res.locals.siteName = "Webtube"; res.locals.routes = routes; res.locals.user = { isA ...

Is there a way to display a success message once the button has been activated?

<template> <div> <div class="form-group"> <label for="name">Name</label> <input type="text" class="form-control" v-model="firstName" placeholder="Enter ...

Having difficulty retrieving data from JSON file using Backbone framework

When I click on the div, I am attempting to retrieve JSON data but am unable to view the output. I am utilizing Backbone Collection to fetch JSON data. I have tried displaying the JSON data in the console as well as within another div. The contents of the ...

Tips for adjusting the height of MUI Date Picker to fit your preferences

<Box sx={{ m: 1 }}> <LocalizationProvider dateAdapter={AdapterDayjs}> <DatePicker label="Enter Date" slotProps={{ textField: { height: "10px" } }} ...

What is the best way to recycle Vue and Vuetify code?

<script> export default { data () { return { my_alert: false, text: '', color: 'success' } }, methods: { openAlt (text, color) { this.text = text this.color = color this.my_ale ...

Leveraging the power of ES6 syntax in Node scripts with Babel

My npm CLI tool utilizes ES6 syntax from BabelJS, specifically arrow functions. Within the entry point of my tool, I'm using the following require: require('babel-core/register'); var program = require('./modules/program.js'); I ...

Utilizing Browser and Operating System Information as Body Class

In order to achieve pixel-perfect styling, I want to include the OS and browser information in the body class. This is necessary because fonts may appear differently depending on the OS/browser configuration. After some research and experimentation, I came ...

Creating Eye-Catching Images by Incorporating Image Overlays Using Just One Image

I'm facing a bit of a challenge here. I need to figure out how to overlay an image onto another image using just a single image tag. Specifically, I want to add a resize icon to the bottom right corner of an image to let users know they can resize it ...

Ways to restart character count to zero if textarea is blank using jQuery

Can someone assist me with resetting the character count to zero when the textarea field is empty in jQuery? Currently, I have implemented code that successfully adds the character count as shown below; $(document).ready(function() { $('#content& ...

Tips on setting up a dropzone upload feature with a click-trigger option

Is there a way to configure dropzone.js so that the file is uploaded only when a submit button is clicked, rather than automatically? Here's the code snippet I am currently using: $('#myDropzone').dropzone({ url: SITE_URL + 'self_r ...

Error: The property 'combine' of 'winston_1.default.format' cannot be destructured since it is not defined

Encountered an error while using Winston in Node.js, how can we resolve it? The version of Winston I am using is 3.3.3 and winston-daily-rotate-file version is 4.5.0 I attempted npm i winston@next --save, but the error persists. ** Here is the Error Mes ...

Manipulating webpage content with JavaScript

How can I provide visual feedback to a user while an ajax request is in progress? For example, when a user clicks a 'process' button that triggers an AJAX request to a server-side script, they should see a 'loading...' message and a gra ...

Using Yii to attach an onclick event to CLinkPager for every pager link

Is there a way to execute a function with every pager link click in Yii's CLinkPager? I've tried the following code without success. 'pagerCssClass' => 'pagination', 'afterAjaxUpdate'=>"FB.Canvas.scrollTo ...