Learning animations in JavaScript with a marching ant

Teaching myself JavaScript animations by making an ant “crawl” up and down the side of my browser.

First steps

First I had to draw the ant. I made a very basic ant using SVG circles and lines. Here is the little fella.

Next, I needed to figure out the logic for making him zig-zag up and down the page. No ant goes in a straight line, after all. So I created a box for him (just a div with a border) and knew I needed to change his direction if he hits either the side or the top of the div. This is done in pixels, so the count needs to go up if he is going to the left, and go down to go to the right. The pseudocode is something like this:

If (ant hits left border):
direction ++;
If (ant hits right border):
direction --;

And the same for hitting the top (direction ++) or bottom (direction --). I used offsetLeft and offsetTop to track my ant’s position and the clientHeight and clientWidth of my div box.

Problems start

I used JavaScript’s requestAnimationFrame() for smooth animation, but when I opened the browser, my ant wasn’t moving. I quickly realised my error – I hadn’t set the CSS positions! I set the ant’s position to absolute and the div box’s position to relative, and now the ant WAS moving, but just continued diagonally downwards forever. It didn’t seem to notice my if statements telling it to change direction when its offsetLeft was greater than the clientWidth and the same for the offsetTop/ClientHeight.

I opened the console – no errors. So I did the old debugging trick (now discouraged, I believe) of putting in console.log statements to check the offsetLeft and offsetTop positions of my ant. When I opened the console, all my ant’s positions were “undefined”. The browser had no idea where my ant was, so no way of reversing the direction based on my if statement.

Google to the rescue, and I found out that SVGs do not have the same support as other HTML objects for properties such as offsetTop/offsetLeft. Not a problem to fix once you know, so I just wrapped the SVG in yet another div. I reopened the HTML file – there was my ant, zig-zagging perfectly! It was strangely humorous to watch. Then I thought, I think I’ll get him to turn around each time he changes direction. After all, very few ants walk backwards. So, just add a CSS rotation property at each extreme, top and bottom.

Cleaning up

Hmm... a little better, but I wanted him to turn smoothly, so I added a transition property but then realised I needed to reset it to 0 at the beginning of each function call, otherwise the transition meant my ant no longer bounced neatly off the sides of the box, but rather did drunken transitions at each change of direction. So I added an assignment statement to reset the transition property at the beginning of the animation function.

That was quite a bit of fun! I've never created SVGs or done much animation coding in JavaScript, so valuable lessons learned. Next step is to add a toggle button so people can turn off the ant if desired, as it is a little distracting.

Here is the JavaScript logic:

const ant = document.getElementById("ant");
const border = document.getElementById("border");

//position on page
let dirX = 0;
let dirY = 0;
//direction the ant goes in
let velX = 1;
let velY = 1;

let antX = ant.offsetLeft;
let antY = ant.offsetHeight;
const contWidth = border.clientWidth - 50;
const contHeight = border.clientHeight - 90;

function zigzag(){
    ant.style.transition = '0s';
    dirX = dirX + velX;
    dirY = dirY + velY;    

    antX = ant.offsetLeft;
    antY = ant.offsetTop;

    //change direction if at the boundaries
    if (antX < 0){
        velX = 1;
    }
    if(antX > contWidth){
        velX = -1;
    }

    if (antY < 0){
        velY = 1;
        ant.style.transform = 'rotate(' + 360 + 'deg)';
        ant.style.transition = '0.5s';
    }
    if (antY > contHeight){
        velY = -1;
        ant.style.transform = 'rotate(' + 180 + 'deg)';
        ant.style.transition = '0.5s';
    }
    //update the ant's position
    ant.style.left = dirX + "px";
    ant.style.top = dirY + "px";

    window.requestAnimationFrame(zigzag);   
}     
window.requestAnimationFrame(zigzag);