HTML5 Eyes that follow the mouse


Your browser has no canvas control support!
I made a cute flash movie of an elephant ages ago. Among other things the eyes of the elephant followed the cursor. Unfortunately subsequent flash security updates broke the functionality (if you want mouse move events outside the flash control you need special permissions). So, I decided to convert it to HTML5. The result you can see to the left of this paragraph. (Note: if you see nothing, it’s because you have an old version of IE.).

How it’s done

I use an onload event handler somewhere in the html file which calls the init() function. E.g.:


<body onload="init()">

In the init() function the images are loaded and the canvas resized and then the render loop is started via requestAnimFrame. All images are stored in the one png (this is known as an image atlas).
function init()
{
  canvas = document.getElementById('nelly');
  if (canvas.getContext)
  {
    context = canvas.getContext('2d');
  }
  else
  {
    return;
  }
  logElement = document.getElementById('log');
  atlas = new Image();  
  atlas.src = 'http://astronautz.com/wordpress/nelly0.png';
  atlas.onload = function()
  { 
    window.addEventListener ("mousemove", getCoords, true);
    xCanvas = canvas.offsetLeft;
    yCanvas = canvas.offsetTop;
    var elem = canvas.offsetParent;
    while (elem)
    {
      xCanvas += elem.offsetLeft;
      yCanvas += elem.offsetTop;
      elem = elem.offsetParent;
    }

    backWidth = 97;
    backHeight = 150;
    canvas.width = backWidth;
    canvas.height = backHeight;
    eyeRight.setSize(4, 4);
    eyeRight.setMin(44, 30);
    eyeRight.setMax(54, 52);
    eyeRight.setAtlas(97, 0);
    eyeRight.init();
    eyeLeft.setSize(4, 4);
    eyeLeft.setMin(34, 30);
    eyeLeft.setMax(39, 52);
    eyeLeft.setAtlas(97, 0);
    eyeLeft.init();
    requestAnimFrame(render);
  };  
}

Note, that I create a event handler which stores the mouse coordinates in two global variables:
var xMouse = 0;
var yMouse = 0;
...
function getCoords(event) 
{
  xMouse = event.clientX;
  yMouse = event.clientY + window.pageYOffset;
};
...
window.addEventListener ("mousemove", getCoords, true);


Each eye is controlled by an Eye object. It takes a min, max as parameters:

It also needs the size of the eye image and it’s position within the image atlas before you initialise:
eyeRight.setSize(4, 4);
eyeRight.setMin(44, 30);
eyeRight.setMax(54, 52);
eyeRight.setAtlas(97, 0);
eyeRight.init();

The eye movement is calculated by getting the slope of the angle between the eyeball center and the current mouse position. Then you need to calculate the position taking into account the quadrant:
  this.update = function()
  {
    var xDiff = xMouse-(xCanvas+this.m_xOrig);
    var yDiff = yMouse-(yCanvas+this.m_yOrig);
    // first calculate x pos
    if (yDiff == 0)
    {
      if (xDiff > 0)
      {
        this.m_x = this.m_xMax;
      }
      else
      {
        this.m_x = this.m_xMin;
      }
      this.m_y = this.m_yOrig;
    }
    else
    {
      var slope = xDiff/yDiff;
      if (yDiff > 0)
      {
        this.m_x = slope*(this.m_xMax-this.m_xMin) + this.m_xMin;
      }
      else
      {
        this.m_x = -slope*(this.m_xMax-this.m_xMin) + this.m_xMin;
      }
    }
    // then calculate y pos
    if (xDiff == 0)
    {
      if (yDiff > 0)
      {
        this.m_y = this.m_yMax;
      }
      else
      {
        this.m_y = this.m_yMin;
      }
      this.m_x = this.m_xOrig;
    }
    else
    {
      var slope = yDiff/xDiff;
      if (xDiff > 0)
      {
        this.m_y = slope*(this.m_yMax-this.m_yMin) + this.m_yMin;
      }
      else
      {
        this.m_y = -slope*(this.m_yMax-this.m_yMin) + this.m_yMin;
      }
    }
    if (this.m_x > this.m_xMax)
    {
      this.m_x = this.m_xMax;
    }
    else if (this.m_x < this.m_xMin)
    {
      this.m_x = this.m_xMin;
    }
    if (this.m_y > this.m_yMax)
    {
      this.m_y = this.m_yMax;
    }
    else if (this.m_y < this.m_yMin)
    {
      this.m_y = this.m_yMin;
    }
  }

The full source code can be downloaded here.

This entry was posted in CodeProject, web development and tagged , , , . Bookmark the permalink.

13 Responses to HTML5 Eyes that follow the mouse

  1. David says:

    Hi there,

    I’ve seen your tutorial i want to make my monster to does the same as your elephant!
    i’ve tried so hard to put it together but at some point it always fail.
    Please, please can you explain me step by step how to do it properly, like what where to write, upload etc. as I’m completely new at javascript.

    Many Thanks,
    David

  2. Ed Welch says:

    Have you checked the javascript error console?
    What problem do you have exactly?

    BTW: HTML5 is not supported by IE8. something to bear in mind

  3. David says:

    All right, i tell you what i’ve done so far, so i downloaded your file and i copied it to my Js folder. With the worldpress editor i put this code to the header hoping it will does something: <script type="text/javascript" src="js/nelly.js”>

    I was hoping that something appears on my page and i can play with adjusting the values.

    So as you see i have clearly no clue about javascript and i really need some help to get this done. Please advise me what to do first. Many thanks!

  4. Ed Welch says:

    You need this line in the html file as well (that’s mentioned in the article):

    
    <body onload="init()">
    

    Also you need nelly0.png in the same directory

  5. David says:

    Thanks, but unfortunately it didn’t work. :(

  6. mooo says:

    Thanks for sharing this.

    David you probably haven’t got a canvas to put the image on, you need to put that in the body of your html.

    Your browser has no canvas control support!

    Ed, I don’t suppose you could tell me how to draw this onto a particular area of an existing canvas? I have a fullscreen canvas game, I had to remove:

    //canvas.width = backWidth;
    //canvas.height = backHeight;

    otherwise the elephant takes up the whole screen, then I added the x and y co-ordinates here:

    context.drawImage(atlas, 0, 0, backWidth, backHeight, 380, 600, backWidth, backHeight);

    but in doing that the eyes have been lost, I’m not sure what to change to get them back?

  7. podatki says:

    How to achieve this effect using only HTML5 tags without javascript?

  8. Hal says:

    Awesome script – thank you for sharing! I’ve got it working, for the most part, but mine seems to keep duplicating the eyes on movement (I am using a different png with transparent background). I understand that with canvas there is no “erase” method, as you are simply drawing the pixels in, but how did you achieve the movement effect without showing the “duplicates” that are rendered? Is there a background or fillrect that is covering up the “old eyes” that I am missing somewhere?

    • Hal says:

      Nevermind – figured it out – I was using an image that had glasses, and the eyepieces of the glasses were transparent. I made them white/opaque (like the elephant’s eyes) so that the new render is on top of the “old eyes”, thus hiding them. Thanks again for sharing the script!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Anti-Spam Quiz: