Finding the closest grid coordinate to the mouse position with javascript/jQuery
Asked Answered
K

3

12

What I'm trying to do is make a grid of invisible coordinates on the page equally spaced. I then want a <div> to be placed at whatever grid coordinate is closest to the pointer when onclick is triggered. Here's the rough idea:

alt text

I have the tracking of the mouse coordinates and the placing of the <div> worked out fine. What I'm stuck with is how to approach the problem of the grid of coordinates.

First of all, should I have all my coordinates in an array which I then compare my onclick coordinate to?

Or seeing as my grid coordinates follow a rule, could I do something like finding out which coordinate that is a multiple of whatever my spacing is is closest to the onclick coordinate?

And then, where do I start with working out which grid point coordinate is closest? What's the best way of going about it?

Thanks!

Kazue answered 26/4, 2010 at 10:40 Comment(0)
E
5

I was initially writing an answer similar to bobince's, but he got there before me. I like that way of doing it, but his version has got some floors (though it's still a very good answer).

I presume that what you want is a HTML-less grid (that is, without markup like a table), which bobince supplies a solution for. In that case, the code may be optimised significantly for cross browser compatibility, readability, errors and speed.

So, I suggest the code should be more like this:

#canvas { position: relative; width: 100px; height: 100px; border: solid red 1px; }
#nearest { position: absolute; width: 10px; height: 10px; background: yellow; }

<div id="canvas"><div id="nearest"></div></div>

var
    canvasOffset = $("div#canvas").offset(),
    // Assuming that the space between the points is 10 pixels. Correct this if necessary.
    cellSpacing = 10;

$("div#canvas").mousemove(function(event) {
    event = event || window.event;
    $("div#nearest").css({
        top: Math.round((mouseCoordinate(event, "X") - canvasOffset.left) / cellSpacing) * cellSpacing + "px",
        left: Math.round((mouseCoordinate(event, "Y") - canvasOffset.top) / cellSpacing) * cellSpacing + "px"
    });
});

// Returns the one half of the current mouse coordinates relative to the browser window.
// Assumes the axis parameter to be uppercase: Either "X" or "Y".
function mouseCoordinate(event, axis) {
    var property = (axis == "X") ? "scrollLeft" : "scrollTop";
    if (event.pageX) {
        return event["page"+axis];
    } else {
        return event["client"+axis] + (document.documentElement[property] ? document.documentElement[property] : document.body[property]);;
    }
};

The mouseCoordinate() function is a boiled down version of these two functions:

function mouseAxisX(event) {
    if (event.pageX) {
        return event.pageX;
    } else if (event.clientX) {
        return event.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
    }
};

function mouseAxisY(event) {
    if (event.pageY) {
        return event.pageY;
    } else if (event.clientY) {
        return event.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
    }
};

I really like the idea of your project, perhaps I'll make something similar myself :D

Earwitness answered 26/4, 2010 at 18:42 Comment(5)
Oh yes, I forgot about browser compatibility when getting the mouse position! If you're interested acorn.host22.com/tiles2.html is what I've come up with so far. Very colourful!Kazue
Just realised something else. You need to take into account resizing of the window which will need to be reflected in the canvasOffset by adding this: $(window).resize(function() { canvasOffset = $("#canvas").offset() });Kazue
Good call, that's exactly true. Resizing the window will make the canvasOffset messed up. One thing has got me wondering though, you're using jQuery, but still doing a lot of stuff without jQuery that could be optimised with jQuery?Earwitness
I'm a beginner at both jQuery and javascript so I'm not surprised I have stupid things going on in my script. What in particular? Comments probably aren't the easiest place to discuss my script, but I'd be really grateful for any advice you can give! I added an email address to my profile.Kazue
I'll write you an email, then. I'll pass on your code with some of my changes, so you can see what you're doing wrong ;)Earwitness
S
8

In terms of working out which grid point is closest - say, for example, each block is 10x10 pixels - to get the grid index you would just divide them out -

  1. Click at [ 237 ; 112 ]
  2. Blocks of 10x10
  3. Grid index = [ 237/10 ; 112/10 ] = [ 23.7 ; 11.2 ]
  4. Round them to get the "closest"
  5. Block indices are 24;11

If you need to store the data, you could push the grid co-ordinates to an array on click, to reference later.

Shaggy answered 26/4, 2010 at 10:54 Comment(1)
Ok, this is sounding fantastic! Let me see if I'm fully understanding. So bascially, each grid point has a identifying reference of [0->c, 0->r] (c= number of columns and r= number of rows). I would then just have to convert the grid point reference to the real coordinate. xref=3; startxcoord=10; spacing=50; xcoord=(xref*spacing)+startxcoord;Kazue
G
7

could I do something like finding out which coordinate that is a multiple of whatever my spacing is is closest to the onclick coordinate?

Sure. The whole point of a grid it's it's calculable with simple arithmetic, rather than having to cart around a big array of arbitrary points.

where do I start with working out which grid point coordinate is closest?

It's a simple division with rounding for each axis.

#canvas { position: relative; width: 100px; height: 100px; border: solid red 1px; }
#nearest { position: absolute; width: 10px; height: 10px; background: yellow; }

<div id="canvas"><div id="nearest"></div></div>

var gridspacing= 10;
$('#canvas').mousemove(function(event) {
    var pos= $(this).offset();
    var gridx= Math.round((event.pageX-pos.left)/gridspacing);
    var gridy= Math.round((event.pageY-pos.top)/gridspacing);
    $('#nearest').css('left', (gridx-0.5)*gridspacing+'px').css('top', (gridy-0.5)*gridspacing+'px');
});
Gobbet answered 26/4, 2010 at 11:2 Comment(1)
Beautiful example! Here it is working on a webpage: acorn.host22.com/snappy.htmlKazue
E
5

I was initially writing an answer similar to bobince's, but he got there before me. I like that way of doing it, but his version has got some floors (though it's still a very good answer).

I presume that what you want is a HTML-less grid (that is, without markup like a table), which bobince supplies a solution for. In that case, the code may be optimised significantly for cross browser compatibility, readability, errors and speed.

So, I suggest the code should be more like this:

#canvas { position: relative; width: 100px; height: 100px; border: solid red 1px; }
#nearest { position: absolute; width: 10px; height: 10px; background: yellow; }

<div id="canvas"><div id="nearest"></div></div>

var
    canvasOffset = $("div#canvas").offset(),
    // Assuming that the space between the points is 10 pixels. Correct this if necessary.
    cellSpacing = 10;

$("div#canvas").mousemove(function(event) {
    event = event || window.event;
    $("div#nearest").css({
        top: Math.round((mouseCoordinate(event, "X") - canvasOffset.left) / cellSpacing) * cellSpacing + "px",
        left: Math.round((mouseCoordinate(event, "Y") - canvasOffset.top) / cellSpacing) * cellSpacing + "px"
    });
});

// Returns the one half of the current mouse coordinates relative to the browser window.
// Assumes the axis parameter to be uppercase: Either "X" or "Y".
function mouseCoordinate(event, axis) {
    var property = (axis == "X") ? "scrollLeft" : "scrollTop";
    if (event.pageX) {
        return event["page"+axis];
    } else {
        return event["client"+axis] + (document.documentElement[property] ? document.documentElement[property] : document.body[property]);;
    }
};

The mouseCoordinate() function is a boiled down version of these two functions:

function mouseAxisX(event) {
    if (event.pageX) {
        return event.pageX;
    } else if (event.clientX) {
        return event.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
    }
};

function mouseAxisY(event) {
    if (event.pageY) {
        return event.pageY;
    } else if (event.clientY) {
        return event.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
    }
};

I really like the idea of your project, perhaps I'll make something similar myself :D

Earwitness answered 26/4, 2010 at 18:42 Comment(5)
Oh yes, I forgot about browser compatibility when getting the mouse position! If you're interested acorn.host22.com/tiles2.html is what I've come up with so far. Very colourful!Kazue
Just realised something else. You need to take into account resizing of the window which will need to be reflected in the canvasOffset by adding this: $(window).resize(function() { canvasOffset = $("#canvas").offset() });Kazue
Good call, that's exactly true. Resizing the window will make the canvasOffset messed up. One thing has got me wondering though, you're using jQuery, but still doing a lot of stuff without jQuery that could be optimised with jQuery?Earwitness
I'm a beginner at both jQuery and javascript so I'm not surprised I have stupid things going on in my script. What in particular? Comments probably aren't the easiest place to discuss my script, but I'd be really grateful for any advice you can give! I added an email address to my profile.Kazue
I'll write you an email, then. I'll pass on your code with some of my changes, so you can see what you're doing wrong ;)Earwitness

© 2022 - 2024 — McMap. All rights reserved.