Faces following the mouse

Physiognomy - A JS experiment

This is an old experiment I did where I had my face follow the mouse around the browser document. Originally I wrote it using jQuery, but I've updated it to vanilla js. Source available on Github. You can see it here: Physiognomy - A JS experiment.

Step one is to create an image with the nine different head positions:

Head positions in a single image

Next create a function to take the mouse position and image position which will then be fed to another function which calculates which image to show. imagew and imageh are the size of the final element, in other words should be a third of the height and width

function changeFace(mousex, mousey, face_id){  
    var face = document.querySelector(face_id)
    var imagew = 160;
    var imageh = 160;
    var facepos = { // image position
      top:  face.offsetTop,
      left: face.offsetLeft
    };
    face.style.backgroundPosition = calucateBp(imagew, imageh, mousex, mousey, facepos);
}

Next create a function to calculate which the CSS background-position and return it. This looks complicated but should be self explanatory. Each if statement tests where the mouse is relative to the element and sets the background-position accordingly.

function calucateBp(imagew, imageh, mousex, mousey, facepos){  
    // TOP LEFT
    if(mousex < facepos.left && mousey < facepos.top){ 
      return '0px 0px'; 
    }
    // TOP
    if((facepos.left + imagew) > mousex && mousex > facepos.left && mousey < facepos.top){ 
      return '-'+ imagew +'px 0px'; 
    }
    // TOP RIGHT
    if(mousex > (facepos.left + imagew) && mousey < facepos.top){ 
      return '-'+ (2 * imagew) +'px 0px'; 
    }
    // LEFT
    if(mousex < facepos.left && mousey < (facepos.top + imageh) && mousey > facepos.top){ 
      return '0px -'+ imageh +'px'; 
    }
    // FRONT
    if((facepos.left + imagew) > mousex && mousex > facepos.left && mousey < (facepos.top + imageh) && mousey > facepos.top){
      return '-'+ imagew +'px -'+ imageh +'px' 
    }
    // RIGHT
    if(mousex > (facepos.left + imagew) && mousey < (facepos.top + imageh) && mousey > facepos.top){ 
      return '-'+ (2 * imagew) +'px -'+ imageh +'px'; 
    }
    // BOTTOM LEFT
    if(mousex < facepos.left && mousey > (facepos.top + imageh)){ 
      return '0px -'+ (2 * imageh) +'px'; 
    }
    // BOTTOM
    if((facepos.left + imagew) > mousex && mousex > facepos.left && mousey > (facepos.top + imageh)){ 
      return '-'+ imagew +'px -'+ (2 * imageh) +'px'; 
    }
    // BOTTOM RIGHT
    if(mousex > (facepos.left + imagew) && mousey > (facepos.top + imageh)){ 
      return '-'+ (2 * imagew) +'px -'+ (2 * imageh) +'px'; 
    }
}

To make it all work create an event listener for onmousemove passing in the x and y coordinates of the mouse and the ID of the element to apply.

  document.addEventListener('mousemove', (e) => {
    changeFace(e.pageX, e.pageY, '#jerry');
    changeFace(e.pageX, e.pageY, '#george');            
    changeFace(e.pageX, e.pageY, '#elaine');            
    changeFace(e.pageX, e.pageY, '#kramer');            
  });

Note .offsetTop and .offsetLeft return the offset relative to the parent element, not the document. Some extra maths are involved to allow for this if the element isn't at the root of the DOM.