Spider-Man Knows When You Leave His Browser Tab

I’ve been playing around with Ye Olde Canvas again. This time I wanted to use several techniques in one animation, including the Page Visibility API, and combine those with some behaviors outside of the canvas itself. I also wanted to put an Easter egg into my site. Because Spider-Man.

Amazing Spider-Man #3, page 20.

This is another one of those “at the time of this writing” posts, but I do provide a fiddle in case you’re late to the party.

If I still have it posted when you’re reading this, you can use Ctrl+D to make Spider-Man crawl along the upper edge of my site’s main navigation. Just make sure you click on the site first. I thought it was broken for a bit when I had actually clicked on the Chrome Inspector and taken focus away from the page. D’oh!

Anyway, there are a few things going on with our friendly neighborhood web-slinger. They keyboard combo starts him along the top, and he’ll go until he reaches the end. Then he resets. He’ll also reset if you resize the window small enough to push him off the side, or if you go small enough to trigger the media query for the mobile view. Once he resets, you’ll need to hit the keyboard shortcut again, and of course you won’t see him if the header isn’t visible.

So what’s going on with this thing?

We have several tricks in play. First, it uses a tiled sprite with a per-second frequency that you can set. I grabbed a Spider-Man sprite that was littered all over the Internet and used just the crawly part. Then I wrote a temporary bit into my app to convert this into a data url. (Now I don’t need to host an image file anywhere.) The animation for moving him around is pretty straightforward, so I’ll just list the key points on how it’s all put together.

  1. Cool new app pattern. The overall app structure came from this tutorial by Dan Tello. It’s extremely helpful, but he uses cancelAnimationFrame for pausing. It’s a neat feature, but it’s not totally supported yet so I didn’t keep it. Instead, I use a “paused” variable to kill the infinite loop which calls the requestAnimationFrame. The end-result is the same.
  2. I resize the canvas when the window is resized. My site uses Underscore.js, so I have a throttle function available to run a function (no more than) every quarter-second to adjust my canvas. This prevents the content from scaling since the canvas is always at 100% size, and I wrote the animation knowing that the width of the canvas could change. This is also how I reset the animation when the header is no longer visible. (If you don’t need all of UnderscoreJS, then you can use this very lightweight throttle/debounce plugin from Ben Alman.)
  3. It resets when you leave for another tab. I wanted to make sure my animations aren’t crunching numbers when the use is on another tab. I know that cancelAnimationFrame is cool about this sort of thing, but I still wanted to pause the app. I looked around a bit and stumbled upon this very handy Stack Overflow post about the Page Visibility API. Mozilla is also has a very helpful page for this. Very cool stuff with a groovy polyfill! Oh, and here’s a fiddle from a helpful guy who lays it all out for us.

Amazing Spider-Man #3, page 6

If you want to have a look at the code, or if the Easter egg is gone by the time you read this post, then you can have a look at my fiddle right here. Or you can just check out this embed:

The keyboard controls are a bit different in the fiddle because it’s not an Easter egg. Space pauses, and the arrow keys flip Spider-Man over. You can make him face left or right, but keep in mind that his vertical flip is along his hands and feet. The up arrow will put him right-side up, but above the canvas. If you’d like to see Spider-Man right-side up, then no worries. You can just fiddle with his x/y coordinates to move him down from the ceiling. (See what I did there? “Fiddle” with his coordinates? I’m so funny before I’ve finished my coffee!)

And that’s it. A tiled sprite that knows when it’s not in the active tab and keeps track of window resize. And climbs across the ceiling. And fights crime.