0

Ultimately I like to know which object was being clicked in a canvas, and I wrote the script:

  dist.push(Math.abs(x-ball1.x-88.5) + Math.abs(y-ball1.y-110));
  dist.push(Math.abs(x-ball2.x-88.5) + Math.abs(y-ball2.y-110));
  dist.push(Math.abs(x-ball3.x-88.5) + Math.abs(y-ball3.y-110));
  function sortNumber(a,b) {
    return a - b;
  }
  dist.sort(sortNumber);

Obviously this only give me the sort of the number but I need it to connect with ball1, ball2, and ball3. I figure I could nest an array for this but I haven't figured out the logic...

Or perhaps my approach was wrong to begin with?

P.S., obviously if I only have three balls I can do this:

var b1d = Math.abs(x-ball1.x-88.5) + Math.abs(y-ball1.y-110);
  var b2d = Math.abs(x-ball1.x-88.5) + Math.abs(y-ball1.y-110);
  var b3d = Math.abs(x-ball1.x-88.5) + Math.abs(y-ball1.y-110);
  dist.push(b1d, b2d, b3d);
  function sortNumber(a,b) {
    return a - b;
  }
  dist.sort(sortNumber);
  if (dist[0] == b1d) {
    alert('b1');
  } else if (dist[0] == b2d) {
    alert('b2');
  } else if (dist[0] == d3d) {
    alert('b3');
  } else {
    alert('####');
  }

But if I have hundreds of balls this probably isn't the best way...

GramThanos
  • 3,572
  • 1
  • 22
  • 34
Aero Wang
  • 8,382
  • 14
  • 63
  • 99

1 Answers1

2

How you search depends on what you do with the balls, and how you place them.

Here a simple example that you can click a ball and bring it in the foreground. We create 200 balls.

To find the correct ball we start searching in an one time sorted array, based on the z-index of the ball (from the balls on the back to the balls on the frond), as you can not click the balls on the back, we search starting from the last element of the array.

In this example, this is a good solution, but in your applications it may not be, it depends on many things, like if the balls overlap, or if the possition is not random.

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');

var getRandomColor = function() {
 // Code from : http://stackoverflow.com/questions/1484506/random-color-generator-in-javascript
 var letters = '0123456789ABCDEF';
 var color = '#';
 for (var i = 0; i < 6; i++ ) {
  color += letters[Math.floor(Math.random() * 16)];
 }
 return color;
}

var createRandomBall = function(){
 // The ball object
 var ball = {};

 // Random radius (min 5 max 30)
 ball.radius = Math.floor((Math.random() * 25) + 5);
 ball.radius2 = Math.pow(ball.radius, 2);
 // Random x position
 ball.x = Math.floor((Math.random() * (canvas.width - ball.radius*2)) + ball.radius);
 // Random y position
 ball.y = Math.floor((Math.random() * (canvas.height - ball.radius*2)) + ball.radius);
 // Random color
 ball.color = getRandomColor();

 return ball;
}

// Create many balls
var ballList = [];
var tmp_ball;
for (var i = 0; i < 200; i++) {
 // Make a random ball
 tmp_ball = createRandomBall();
 // Add to the list
 ballList.push(tmp_ball);
}

// Render the balls
var renderBalls = function(){
 var ball;
 // For each ball
 for (var i = 0; i < ballList.length; i++) {
  ball = ballList[i];

  // Stroke ball
  context.beginPath();
  context.arc(ball.x, ball.y, ball.radius - 1, 0, 2 * Math.PI, false);
  context.fillStyle = ball.color;
  context.fill();
  context.lineWidth = 1;
  context.strokeStyle = '#000000';
  context.stroke();
 }
}

// Render balls
renderBalls();

// Add click event
canvas.addEventListener('click', function(event){
 // Get x and y of click
 var click = {
  x : event.clientX - canvas.offsetLeft,
  y : event.clientY - canvas.offsetTop
 };
  
 var ball = null;

 // Find clicked ball
 // we search the array from the back, 
 // because on the back balls are over the frond balls 
 for (var i = ballList.length - 1; i >= 0; i--) {

  if( Math.pow(click.x - ballList[i].x, 2) + Math.pow(click.y - ballList[i].y, 2) <= ballList[i].radius2 ){
   ball = i;
   break;
  }
  
 }

 // If no ball found return
 if(ball == null){
  console.log("No ball clicked");
  return;
 }

 // else ball found
 ball = ballList.splice(ball, 1)[0];

 // Else position ball on the frond
 ballList.push(ball);
 // Re-render
 renderBalls();

}, false);
*{
    padding: 0px;
    margin: 0px;
   }
   #myCanvas{
    position: absolute;
    top: 10px;
    right: 10px;
    bottom: 10px;
    left: 10px;
   }
<canvas id="myCanvas" width="600" height="200"></canvas>

A general solution is to make a map table, a grid of your canvas, and on each cell add the corresponding balls, so that you can match in which grid box the click was made and check a smaller group of balls.

So for example, lets say that you want when you click, all the balls under the click to change color. Here is an example with mapping the balls on smaller groups, we make a grid of 10 columns and 5 lines. Each ball may be in more than 1 group. We create 400 balls.

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');

var getRandomColor = function() {
 // Code from : http://stackoverflow.com/questions/1484506/random-color-generator-in-javascript
 var letters = '0123456789ABCDEF';
 var color = '#';
 for (var i = 0; i < 6; i++ ) {
  color += letters[Math.floor(Math.random() * 16)];
 }
 return color;
}

var gridSize = {x:10, y:5};
var gridList = [];
// Rows
for (var i = 0; i < gridSize.y; i++) {
 gridList.push([]);
 // Columns
 for (var j = 0; j < gridSize.x; j++) {
  gridList[i].push([]);
 }
}

var createRandomBall = function(){
 // The ball object
 var ball = {};

 // Random radius (min 5 max 30)
 ball.radius = Math.floor((Math.random() * 25) + 5);
 ball.radius2 = Math.pow(ball.radius, 2);
 // Random x position
 ball.x = Math.floor((Math.random() * (canvas.width - ball.radius*2)) + ball.radius);
 // Random y position
 ball.y = Math.floor((Math.random() * (canvas.height - ball.radius*2)) + ball.radius);
 // Random color
 ball.color = getRandomColor();

 // Map ball - find cells that the circle overlap
 grid = {
  x : {
   min : Math.floor((ball.x - ball.radius)*gridSize.x/canvas.width),
   max : Math.floor((ball.x + ball.radius)*gridSize.x/canvas.width)
  },
  y : {
   min : Math.floor((ball.y - ball.radius)*gridSize.y/canvas.height),
   max : Math.floor((ball.y + ball.radius)*gridSize.y/canvas.height)
  }
 }

 for (var y = grid.y.min; y <= grid.y.max; y++) {
  for (var x = grid.x.min; x <= grid.x.max; x++) {
   gridList[y][x].push(ball);
  }
 }

 return ball;
}

// Create many balls
var ballList = [];
var tmp_ball;
for (var i = 0; i < 400; i++) {
 // Make a random ball
 tmp_ball = createRandomBall();
 // Add to the list
 ballList.push(tmp_ball);
}

// Render the balls
var renderBalls = function(){
 var ball;
 // For each ball
 for (var i = 0; i < ballList.length; i++) {
  ball = ballList[i];

  // Stroke ball
  context.beginPath();
  context.arc(ball.x, ball.y, ball.radius - 1, 0, 2 * Math.PI, false);
  context.fillStyle = ball.color;
  context.fill();
  context.lineWidth = 1;
  context.strokeStyle = '#000000';
  context.stroke();
 }

 for (var i = 0; i < gridSize.x + 1; i++) {
  context.beginPath();
  context.moveTo((canvas.width/gridSize.x)*i, 0);
  context.lineTo((canvas.width/gridSize.x)*i, canvas.height);
  context.stroke();
 }

 for (var i = 0; i < gridSize.y + 1; i++) {
  context.beginPath();
  context.moveTo(0, (canvas.height/gridSize.y)*i);
  context.lineTo(canvas.width, (canvas.height/gridSize.y)*i);
  context.stroke();
 }
}

// Render balls
renderBalls();

// Add click event
canvas.addEventListener('click', function(event){
 // Get x and y of click
 var click = {
  x : event.clientX - canvas.offsetLeft,
  y : event.clientY - canvas.offsetTop
 };
 var grid = {
  x : Math.floor(click.x*gridSize.x/canvas.width),
  y : Math.floor(click.y*gridSize.y/canvas.height)
 };
  
 var ball = 0;

 var smallerList = gridList[grid.y][grid.x];

 // Find clicked ball
 for (var i = smallerList.length - 1; i >= 0; i--) {

  if( Math.pow(click.x - smallerList[i].x, 2) + Math.pow(click.y - smallerList[i].y, 2) <= smallerList[i].radius2 ){
   ball++;
   smallerList[i].color = getRandomColor();
  }
  
 }

 console.log("Group["+grid.y+"]["+grid.x+"], " + smallerList.length + " balls in group, clicked " + ball + " balls");

 // If no ball found return
 if(ball == 0){
  return;
 }

 // Re-render
 renderBalls();

}, false);
*{
    padding: 0px;
    margin: 0px;
   }
   #myCanvas{
    position: absolute;
    top: 10px;
    right: 10px;
    bottom: 10px;
    left: 10px;
   }
<canvas id="myCanvas" width="600" height="200"></canvas>
GramThanos
  • 3,572
  • 1
  • 22
  • 34