Posts in Category "JavaScript"

Integrating Brackets and Phone Gap Build

Interested in integrating Brackets and PhoneGap Build? There’s an extension for that. Here it is in action:

Video: Building Your First Chrome Extension

The other day, I did a presentation called "Building Your First Chrome Extension." I decided to do it again and record it so I could share it with the world. It’s about 25 minutes long, but at the end of it, you will know just about everything there is to know about Chrome extensions and be ready to start writing your own.


CSS Regions Aren’t Just For Columns

I recently wrote a new text layout engine that uses CSS Regions to do two things:

  1. Wrap text from two different columns around a centered graphic.
  2. Determine the optimal font size for pull-quotes so that they always fill the entire available space. (Quotes that are pulled from a story and displayed in a larger font are frequently referred to as "pull-quotes".)

Here it is in action:


As you can see, CSS regions aren’t just for columns. Regions allows text to flow from one element to another automatically — regardless of size, position, or order — and allows that text to reflow when necessary (when content is added or removed, when elements get resized, when the font sizes changes, etc.). Of course, you can use regions to implement columns if you want to, but remember that you’re not actually creating columns; you’re creating regions that just happen to be arranged as columns. Additionally, you can use the CSS Object Model (CSSOM) to script regions in order to do some very cool things (more on this below).

Here’s a nice simple diagram of CSS regions I stole from Razvan Caliman’s post, Working with CSS Regions and the Shadow DOM:

In the screenshot below, it looks like my content is arranged in columns which wrap around a central graphic and pull-quote:

This is actually done with a series of strategically placed regions:

I’m also using CSS Regions to do something for which it wasn’t really intended: getting text of an arbitrary length to perfectly fill an element of arbitrary size. For example, in the two screenshots below, the first pull-quote is longer, and therefore requires a smaller font size than the one after it.

Using regions, I’m able to dynamically determine the perfect font size regardless of text length or element dimension. It works like this:

  1. All the pull-quotes are extracted from the story (span tags with their class attributes set to "pull_quote").
  2. Pull-quotes are matched to text that appears on each page (since you don’t want a pull-quote that corresponds to text on a previous or subsequent page).
  3. Unique named flows are created for each pull-quote. Those named flows are used for the flow-into CSS property of the quotes, and the flow-from property of the pull-quote elements.
  4. Each region has a regionLayoutUpdate event attached to it.
  5. Each pull quote has a font size of 1em to start. In the regionLayoutUpdate event handler, the regionOverflow property is checked to see if the text fits or not.
  6. If the text fits — if the value of the regionOverflow property is "fit" — the font size is increased by 1/10th of an em. This process is repeated until the text no longer fits (when the regionOverflow property is "overflow").
  7. Now that the text no longer fits, the size is backed down 1/10th of an em and the regionLayoutUpdate event listener gets removed.

In the video, you can actually see the process in action for the first quote (all the quotes are actually being resized simultaneously, but only the quote on the first page is visible). If I really wanted to polish the code, I would optimize the process by using an algorithm that could arrive at the optimal size faster, and I’d probably hide the quotes until they were the right size, but for the demo, I like the visual effect of them expanding.

If you want to check out the code, you can see a demo here (press "Esc" to reveal the regions). Be sure you’re using Google Chrome, and that you have CSS Regions enabled in chrome://flags (and that you’ve restarted your browser after enabling regions). Also, the resizing behavior is still a little buggy, so if you resize your browser window, you might get some unexpected results.

Mapping Global Keyboard Shortcuts to Web Apps

I’ve been playing around with a project that allows me to map global keyboard shortcuts to web apps. The primary motivation was to be able to control web-based music players (Google Music, Amazon Cloud Player, Pandora, etc.) with hotkeys similar to how iTunes can be controlled globally with media keys. There are a lot of moving parts (node.js, a Chrome extension, a third-party app to map global keyboard shortcuts to shell scripts), but I was able to do almost all of it just with JavaScript, and once everything is in place, it works really well:


If you want to check out the code, it’s available over on my personal blog.

Building the Keyboard from TRON Legacy with Web Technologies

I recently re-watched TRON: Legacy, and after the scene where Sam types a few Unix commands on a virtual keyboard, I decided I had to try one out for myself. In addition to wanting to see what it was like to use a virtual keyboard, I also wanted to see if I could build it entirely with web technologies. I discovered I was able to do everything I wanted — except type particularly well.

Below is an explanation of the project (along with some conclusions on virtual keyboards in general). All the source code for the project is available on GitHub.


HTML Animation with requestAnimationFrame and Flash Professional CS6

If you’re curious about the new requestAnimationFrame API, here’s a demo along with a code walk-through:

The requestAnimationFrame API is a replacement for setTimeout and setInterval in the context of HTML animations. It has two primary advantages over timer functions:

  1. It is synchronized with the browser’s repaint loop which allows for more highly optimized animations.
  2. Since it’s specifically designed and intended to be used for animation rather than being a general purpose timer, the browser knows that it’s safe to throttle it back when the tab isn’t active. Throttling back animations means freeing up CPU cycles, and reducing unnecessary battery drain on mobile devices.

If you want to see this animation in action (and/or check out the code), it’s available here (Hint: if it’s running slowly, try making your browser window smaller). Thanks to Chris Georgenes for the “rocker girl” animation. And finally, here are the resources I used to learn about requestAnimationFrame:

Continue reading…

A Short Simple Video Explaining the Shadow DOM and Web Components

I’ve been playing around with the shadow DOM and web components, so I thought I’d put together a quick video explaining the advantages:


You can starting experimenting with these concepts today using Chrome Canary. Just make sure you enable the Shadow DOM and scoped style flags by typing “chrome://flags” into your location bar:

If you want to check out the code used in this example, you can download it here.

If you want to learn more about the shadow DOM and web components, here are the resources I used:

Accessing the Accelerometer and Gyroscope in JavaScript

Now that you can access motion sensors in both Google Chrome and mobile browsers, I decided to demo a couple of sample application which hook into the accelerometer and gyroscope:


If you want to see these applications in action and/or check out the code, the links are below:

Note that the compass application only works on devices with gyroscopes (no laptops that I know if), and requires iOS 5 or newer.

Mouse Pointer Lock in HTML: Video and Sample Code

After watching Paul Irish’s talk at SXSW, I got curious about the new pointer lock APIs in Chrome and decided to create a sample application. Pointer lock refers to the ability to lock the mouse pointer in one location and hide it while still getting certain mouse events like mousemove. This is critical for immersive experiences like games since the user wants to be able to use the mouse to navigate, but doesn’t want the pointer to move off the screen or get in the way.

It’s still early days for navigator.webkitPointer, but I was able to get something to work with Chrome Canary. First, a video demo, then some explanation:


If you want to try the demo firsthand (and maybe experiment with the pointer lock APIs yourself), you can find the code here. But first, you need to follow these steps:

  1. Download Chrome Canary.
  2. Enable pointer lock:
    1. Type "chrome://flags" into your location bar.
    2. Scroll down to "Enable Pointer Lock Mac, Windows, Linux, Chrome OS".
    3. Click "Enable".
    4. Click the "Relaunch Now" button at the bottom to relaunch Chrome Canary.
  3. Allow sites to disable the mouse cursor:
    1. Open settings.
    2. Click "Show advanced settings…".
    3. Click "Content settings…"
    4. Scroll down to the "Mouse Cursor" section.
    5. Select "Allow all sites to disable the mouse cursor". In theory, you should be able to select "Ask me when a site tries to disable the mouse cursor (recommended)," however this isn’t working for me right now (remember: still early days).

Currently, you can only lock the mouse pointer in Chrome while in fullscreen mode. Here’s the relevant code:

document.addEventListener('webkitfullscreenchange', onFullscreenChange, false);

function onFullscreen(event) {
    document.body.webkitRequestFullScreen();
}

function onFullscreenChange(event) {
    if (document.webkitIsFullScreen) {
        id('fullscreenButton').style.display = 'none';
        if (navigator.webkitPointer) navigator.webkitPointer.lock(document.body);
    } else {
        if (navigator.webkitPointer) navigator.webkitPointer.unlock();
        id('fullscreenButton').style.display = 'block';
    }
}

Remember that you have to handle mouse coordinates differently when the pointer is locked versus when the pointer is visible. When it’s locked, even though you can’t see the mouse cursor, it’s actually frozen in one location so it can’t be moved out of the window or onto another monitor which means properties on MouseEvent like clientX and clientY are useless. Instead, you can use properties like webkitMovementX and webkitMovementY that tell you how much (and in which direction) the mouse is being moved as opposed to the location of the mouse cursor. Below is the relevant code from the sample application:

var lastMouse;

function onMouseMove(event) {
    var xDelta, yDelta;
    if (navigator.webkitPointer && navigator.webkitPointer.isLocked) {
        xDelta = event.webkitMovementX;
        yDelta = event.webkitMovementY;
    } else {
        if (lastMouse == null) lastMouse = {x:event.clientX, y:event.clientY};
        xDelta = event.clientX - lastMouse.x;
        yDelta = event.clientY - lastMouse.y;
    }
}

Prototyping in HTML


Over the weekend, I built an e-book reader prototype to demonstrate some ideas that might make the digital book reading experience better. Although I was prototyping a native application, I found the easiest way to build it was with HTML, JavaScript, and CSS. Once CSS regions make it into all mobile browsers, I might put some more time into this concept and build a real browser-based e-book reader (that works exactly how I want it to).