Make an Image "run away" from another

4

I must say first that everything, that I am a newbie , I am recycling myself on programming issues. Even being a programmer for more than 6 years I did not play anything. So I implore you patience.

I am practicing things with JavaScript , it turns out that I want to make an image move with the arrow keys and when "encountering" another image, the latter flee.

The code to move the first image with the arrows is already working, what does not work for me is the "when they are found, the other one changes position".

function leftArrowPressed () {
  var element = document.getElementById("pimientos");
  element.style.left = parseInt(element.style.left) - 5 + 'px';
}

function rightArrowPressed () {
  var element = document.getElementById("pimientos");
  element.style.left = parseInt(element.style.left) + 5 + 'px';
}

function upArrowPressed () {
  var element = document.getElementById("pimientos");
  element.style.top = parseInt(element.style.top) - 5 + 'px';
}

function downArrowPressed () {
  var element = document.getElementById("pimientos");
  element.style.top = parseInt(element.style.top) + 5 + 'px';
}

function moveSelection (evt) {
  switch (evt.keyCode) {
    case 37:
      leftArrowPressed();
      break;
    case 39:
      rightArrowPressed();
      break;
    case 38:
      upArrowPressed();
      break;
    case 40:
      downArrowPressed();
      break;
  }
};

function docReady () {
  window.addEventListener('keydown', moveSelection, quemepilla);
}

function quemepilla (evt) {
  var elementPimi = document.getElementById("pimientos");
  var elementPC = document.getElementById("pc");

  if (elementPC.style.top || elementPC.style.left === elementPimi.style.top || elementPimi.style.left) {
    elementPC.style.top = parseInt(elementPC.style.top) + 5 + 'px';
    elementPC.style.top = parseInt(elementPC.style.top) - 5 + 'px';
    elementPC.style.left = parseInt(elementPC.style.left) + 5 + 'px';
    elementPC.style.left = parseInt(elementPC.style.left) - 5 + 'px';

  }

}

document.addEventListener("DOMContentLoaded", docReady);
<img src="carapimi.png" alt="" id="pimientos" style="position:absolute;left:0; top:0;" height="45" width="45">
<img src="pc.svg" alt="" id="pc" style="position:absolute;left:100px; top:100px;" height="45" width="45">
    
asked by Josep Salvat 09.04.2017 в 13:06
source

2 answers

2

First you must correct the call to quemepilla (?) because it is never done. The third parameter is used to indicate whether said handler should capture the event as soon as it is triggered ( capture ) or if it should be executed at the end ( bubble ).

  

Handlers added by addEventListener are executed in the order in which they are added to a certain event . In this way, if you pass true as the third parameter, you are saying that when an event is triggered said handler will capture the same to proceed with its execution, thus having priority over the rest of handlers.

The call to quemepilla , then, you must do it, after the switch to evaluate if there is a collision.

The first thing to do is to obtain in each painting the coordinates (top, right, bottom and left) of the images. Then, by comparing them, know on which side the collision occurs to move in the opposite direction.

Example (view in full screen)

let $cheese;
let $mouse;
let $debug;

//document.addEventListener('DOMContentLoaded', () => {
  $cheese = document.getElementById('cheese');
  $mouse = document.getElementById('mouse');
  $debug = document.getElementById('debug');
  const $board = document.getElementById('board');

  const width = $cheese.offsetWidth;
  const height = $cheese.offsetHeight;
  const boardWidth = board.offsetWidth;
  const boardHeight = board.offsetHeight;
  const axisX = (boardWidth / 2) - (width / 2);
  const axisY = (boardHeight / 2) - (height / 2);
  $cheese.style.left = axisX + 'px';
  $cheese.style.top = axisY + 'px';

  document.addEventListener('keydown', moveSelection);
//});

function leftArrowPressed() {
  const left = parseInt($mouse.style.left);
  $mouse.style.left = left - 5 + 'px';
}

function rightArrowPressed() {
  const left = parseInt($mouse.style.left);
  $mouse.style.left = left + 5 + 'px';
}

function upArrowPressed() {
  const top = parseInt($mouse.style.top);
  $mouse.style.top = top - 5 + 'px';
}

function downArrowPressed() {
  const top = parseInt($mouse.style.top);
  $mouse.style.top = top + 5 + 'px';
}

function moveSelection(evt) {
  switch (evt.keyCode) {
    case 37:
      leftArrowPressed();
      break;
    case 39:
      rightArrowPressed();
      break;
    case 38:
      upArrowPressed();
      break;
    case 40:
      downArrowPressed();
      break;
  }
  draw();
}

function debug({
  mouse,
  cheese
}) {
  const paragraph = document.createElement('p');
  const mouseSpan = document.createElement('span');
  const cheeseSpan = document.createElement('span');
  paragraph.appendChild(mouseSpan);
  paragraph.appendChild(cheeseSpan);
  mouseSpan.textContent = '
      Mouse: Top: ${mouse.top}, Right: ${mouse.right},
      Bottom: ${mouse.bottom}, Left: ${mouse.left}
    '.replace(/\n/g, '');
  cheeseSpan.textContent = '
      Cheese: Top: ${cheese.top}, Right: ${cheese.right},
      Bottom: ${cheese.bottom}, Left: ${cheese.left}';
  // add paragraph to debug
  $debug
    .querySelector('article')
    .appendChild(paragraph);
  // set a fixed width to draw correctly the bottom border
  paragraph.style.width = '${paragraph.scrollWidth}px';
}

function draw(evt) {
  const {
    top: mouseTop,
    right: mouseRight,
    bottom: mouseBottom,
    left: mouseLeft
  } = $mouse.getBoundingClientRect();
  const {
    top: cheeseTop,
    right: cheeseRight,
    bottom: cheeseBottom,
    left: cheeseLeft
  } = $cheese.getBoundingClientRect();
  debug({
    mouse: {
      top: mouseTop,
      right: mouseRight,
      bottom: mouseBottom,
      left: mouseLeft
    },
    cheese: {
      top: cheeseTop,
      right: cheeseRight,
      bottom: cheeseBottom,
      left: cheeseLeft
    }
  });
  // El queso se mueve hacia la derecha
  if (
    (cheeseLeft - mouseRight <= 5 && cheeseLeft - mouseRight > 0) &&
    (mouseBottom >= cheeseTop && mouseBottom <= cheeseBottom)
  ) {
    $cheese.style.left = '${cheeseLeft + 5}px';
  }

  // El queso se mueve hacia la izquierda
  else if (
    (mouseLeft - cheeseRight <= 5 && mouseLeft - cheeseRight > 0) &&
    (mouseBottom >= cheeseTop && mouseBottom <= cheeseBottom)
  ) {
    $cheese.style.left = '${cheeseLeft - 5}px';
  }

  // El queso se mueve hacia abajo
  else if (
    (cheeseTop - mouseBottom <= 5 && cheeseTop - mouseBottom > 0) &&
    (mouseLeft >= cheeseLeft && mouseRight <= cheeseRight + 5)
  ) {
    $cheese.style.top = '${cheeseTop + 5}px';
  }

  // El queso se mueve hacia arriba
  else if (
    (mouseTop - cheeseBottom <= 5 && mouseTop - cheeseBottom > 0) &&
    (mouseLeft >= cheeseLeft && mouseRight <= cheeseRight + 5)
  ) {
    $cheese.style.top = '${cheeseTop - 5}px';
  }
}
*,
*:before,
*:after {
  box-sizing: border-box;
  margin: 0;
  paddign: 0;
}

body {
  display: flex;
  height: 100vh;
}

#board {
  flex: 1;
}

#debug {
  border-left: 1px solid #ddd;
  display: flex;
  flex: 0 0 35%;
  flex-flow: column nowrap;
  max-width: 280px;
}

#debug h3 {
  border-bottom: 1px solid #ddd;
  color: #555;
  flex: 0 0 50px;
  font-family: sans-serif;
  font-weight: 400;
  line-height: 50px;
  margin: 0;
  text-align: center;
  width: 100%;
}

#debug article {
  flex: 1;
  padding: 15px 0;
  overflow: auto;
}

#debug article p {
  border-bottom: 1px solid #eee;
  padding: 10px;
}

#debug article p span {
  color: #1976D2;
  font-family: monospace;
  font-size: 13px;
  display: block;
  white-space: nowrap;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>DOM collision</title>
</head>

<body>
  <article id="board">
    <img src="http://www.clipartlord.com/wp-content/uploads/2014/07/mouse-face.png" alt="mouse" id="mouse" style="position:absolute;left:0; top:0;" height="45" width="45" />
    <img src="http://images.wikia.com/clubpenguin/images/e/e0/Cheese_Emote.png" alt="cheese" id="cheese" style="position:absolute" height="45" width="45" />
  </article>
  <section id="debug">
    <h3>Debug</h3>
    <article>

    </article>
  </section>
</body>

</html>

It's a fairly simple prototype, for reasons of time I can not put something more complex, but it will help you to leave from there. For the displacement to occur correctly, make sure that the mouse is between the (average) dimensions of the cheese .

    
answered by 10.04.2017 / 17:25
source
2

Well, basically you have to do two things:

  • Detecting if the two images overlap (I see you have made an attempt on your code)
  • If the images overlap, move the image to a new position.
  • One problem you have in your code is that you are trying to call two functions from a single addEventListener , the third parameter of a addEventListener is useCapture and is a boleano .

    Another problem is the way you try to make the conditions, in the following condition:

    elementPC.style.top || elementPC.style.left === elementPimi.style.top || elementPimi.style.left
    

    What you are saying is, if elementPC.style.top is true or elementPC.style.left is equal to elementPimi.style.top or elementPimi.style.left is true (Those variable will be taken as true if its value is different from false , null , undefined , NaN or 0 ). The previous condition will not check if the images overlap because it does not do the necessary checks.

    Another thing that would not work well in your code is the new position you assign to the image:

    elementPC.style.top = parseInt(elementPC.style.top) + 5 + 'px';
    elementPC.style.top = parseInt(elementPC.style.top) - 5 + 'px';
    elementPC.style.left = parseInt(elementPC.style.left) + 5 + 'px';
    elementPC.style.left = parseInt(elementPC.style.left) - 5 + 'px';
    

    Basically you move the image five positive pixels and then five negative pixels, so the result will be that the image stays in the same place.

    Analyze the algorithm that I propose to you to know if the two images intersect in any zone, basically it is to check if the images do not overlap and apply to this check a LOGICAL NOT :

    if (!(left1 + width1 < left2 || left2 + width2 < left1 || top1 + height1 < top2 || top2 + height2 < top1)) {
        ...
    }
    

    After checking that the images intersect you must move the image 2 to a new position, and this depends on what you want to do and you do not make it clear in your question, but a solution would be to move the image with a value that is double its dimensions, and as the two images have the same dimensions, we make sure that the new position will not match the position of the first image.

      

    In order to detect the coordinates of the elements, a very useful method is getBoundingClientRect . If you need to support browsers smaller than IE9 this method would not work and I think it would be more useful to use jQuery or similar.

         

    A new challenge you will have is to ensure that the images when moving do not go beyond the limits of the viewport . I think that based on the following code it will be easier for you to achieve it.

    var pimientos = document.getElementById("pimientos");
    var elementPC = document.getElementById("pc");
    
    function moveElement (element, prop, value) {
    
      var offset = element.getBoundingClientRect();
      
      element.style[prop] = (offset[prop] + value) + "px";
      
      checkCollision();
      
    }
    
    function moveSelection (evt) {
      evt.preventDefault();
      switch (evt.keyCode) {
        case 37:
          moveElement(pimientos, "left", -5);
          break;
        case 39:
          moveElement(pimientos, "left", 5);
          break;
        case 38:
          moveElement(pimientos, "top", -5);
          break;
        case 40:
          moveElement(pimientos, "top", 5);
          break;
      }
    };
    
    function runAway () {
    
      var offset = elementPC.getBoundingClientRect();
      
      var rX = Math.round(1 - Math.random() * 2);
      var rY = Math.round(1 - Math.random() * 2);
      var posX = offset.left + offset.width * 2 * rX;
      var posY = offset.top + offset.height * 2 * rY;
      
      elementPC.style.left = posX + "px";
      elementPC.style.top = posY + "px";
    
    }
    
    function checkCollision () {
    
      var offset1 = pimientos.getBoundingClientRect();
      var offset2 = elementPC.getBoundingClientRect();
      
      if (
        !(offset1.left + offset1.width < offset2.left ||
          offset2.left + offset2.width < offset1.left ||
          offset1.top + offset1.height < offset2.top ||
          offset2.top + offset2.height < offset1.top)
      ) {
      
        runAway();
      
      }
    
    }
    
    document.addEventListener("DOMContentLoaded", function () {
    
      window.addEventListener("keydown", moveSelection);
    
    });
    #pimientos,
    #pc {
      height: 45px;
      position: absolute;
      width: 45px;
    }
    
    #pimientos {
      background: red;
      left: 0;
      top: 0;
    }
    
    #pc {
      background: blue;
      left: 100px;
      top: 100px;
    }
    <div id="pimientos"></div>
    <div id="pc"></div>

    Here I leave you a jsfiddle with the same code located above. I do not know why it works better there than in the snippet of SO .

        
    answered by 09.04.2017 в 14:35