How To Write a Full Screen Web App Optimized For iOS Devices

How To Write a Full Screen Web App Optimized For iOS Devices

In my last tutorial I showed you how to create a vintage clock in Photoshop. Now it’s time to turn the design into a working web app optimized for mobile devices. We’ll use some cool new features introduced with HTML5 and CSS3 and we’ll also write some lines of Javascript to make the clock tick.

We’ll optimize the app for iOS devices and make it a full screen app including things like an app icon and a start screen. The final app will behave like native app when added to the home screen.

HTML 5’s Application Cache will finally allow us to store all the sources locally on the user’s device, so that the app launches fast as lightning and even works in offline mode.

Preparing The Images

Before we start coding, we need to export the individual parts of the clock to separate images. We’ll use transparent .png files for the moving elements so that we can layer them properly in our .css file later on. Including a background pattern I ended up with six images.

These six images add up to about 340kb already, so we should probably think about ways of caching the heavy load once our clock is ticking. We’ll get there at the end of this tutorial.

The HTML Markup

The HTML structure for this web app is fairly simple. We’ll declare an HTML 5 doctype and use div tags for the markup. So our index.html looks like this:

<!DOCTYPE html>
<html>
   <head>
      <title>Old Clock</title>
      <link rel="stylesheet" href="./style.css" type="text/css" media="all" />
      <script src="./clock-app.js" type="text/javascript"></script>
   </head>
   <body>
      <div id="clock">
         <div class="mechanics">
            <div id="hour"></div>
            <div id="min"></div>
            <div id="sec"></div>
         </div>
         <div id="button"></div>
      </div>
   </body>
</html>

As you can see, I already included a CSS stylesheet and a Javascript file in the head section. The extra div.mechanics is needed to apply the drop-shadow filter.

So, let’s head on to the CSS part.

The CSS

Create a new file by the name of style.css in the same directory as the index.html. First thing we want to do, is to get rid of some browser defaults and add a background image to the body.

body {
   padding: 0;
   margin: 0;
   background: #2b211d url('./images/wood-pattern.jpg') center center repeat;
}

Next, we’ll style the div#clock. Set the background property to the corresponding image path and the width and height to match the dimensions of that image (in this case 650px by 650px). In order to center the clock both horizontally and vertically, we need absolute positioning on this element. Then, we can set its top and left values to 50% and apply negative margins at the top and at the left side of it. This is a common approach used to center elements.

#clock {
   background: transparent url('./images/clock-face.png') 0 0 no-repeat;
   width: 650px;
   height: 650px;
   position: absolute;
   top: 50%;
   left: 50%;
   margin-top: -325px;     /* 650/2 = 325 */
   margin-left: -325px;
}

Styling and positioning the hands requires some more thinking. One thing we’ll definitely need is absolute positioning on all the mechanical parts. This will allow us to specify their positions relative to the surrounding div#clock.

#sec,
#min,
#hour,
#button {
  position: absolute;
}

Next, we’ll have to come up with the correct values for the properties top and left for each of those parts. Lets start with the button object. It needs its background image and the matching size.

#button {
   background: url('./images/button.png') 0 0 no-repeat;
   width: 48px;
   height: 48px;
}

In order to place it in the exact center of the clock face, we need to do some calculation. The center point of the clock is at (325px, 325px) and the button size is (48px, 48px). Since we want the center of the button to be at (325px,325px),  we have to move it up left by half its height and width. This results in (325-24, 325-24) = (301px, 301px). So here’s our complete definition for the button:

#button {
   background: url('./images/button.png') 0 0 no-repeat;
   width: 48px;
   height: 48px;
   top: 301px; /* 650/2 -48/2 = 325 - 24 = 301 */
   left: 301px; /* same here */
}

It gets even trickier for the hands as we’re going to make use of the new transform-origin property. It defines the center of rotation (and other transformations) and is measured relative to the current element. The transform-origin defaults to (0px,0px) which is the upper left corner of the element.

Let’s take the hour hand as an example. It’s image has a width of 60px and a height of 240px. To let the hour hand rotate around its bottom center, we would set the origin to (30px, 240px) which may be read as „half the width, full height“. Setting the origin to (30px, 200px) instead will make it look way more realistic though, since the hand is usually screwed somewhere along its body and not at its very end. So, we’re leaving a piece of 40px at the end which I’ll refer to as offset.

To get the correct position for this hand, we need to make sure that the origin matches the center of the clock face. Let me try to explain the math:

left: (center of the clock) - (half the width of the hand);
top:  (center of the clock) - (height of the hand) + (offset of the origin);

Which solves as:

left: 650px/2 - 60px/2 = 325px - 30px = 295px;
top:  650px/2 - 240px + 40px = 325px - 240px + 40px = 125px;

Now we can write the CSS definition for the hour hand. Different browsers currently use their usual prefixes for the transform-origin property as you’ll see:

#hour {
  background: url('./images/hour-hand.png') 0 0 no-repeat;
  width: 60px;
  height: 240px;
  left: 295px;
  top: 125px;
  -webkit-transform-origin: 30px 200px;
  -moz-transform-origin: 30px 200px;
  -ms-transform-origin: 30px 200px;
  -o-transform-origin: 30px 200px;
  transform-origin: 30px 200px;
}

I know that these calculations might seem a little confusing, so feel free to ask questions in the comments if you need further explanation.

Once you understood the principles described above, you can go on with the remaining hands.

#sec {
  background: url('./images/sec-hand.png') 0 0 no-repeat;
  width: 42px;
  height: 348px;
  left: 304px;
  top: 37px;
  -webkit-transform-origin: 21px 288px;
  -moz-transform-origin: 21px 288px;
  -ms-transform-origin: 21px 288px;
  -o-transform-origin: 21px 288px;
  transform-origin: 21px 288px;
}
#min {
  background: url('./images/min-hand.png') 0 0 no-repeat;
  width: 42px;
  height: 324px;
  left: 304px;
  top: 61px;
  -webkit-transform-origin: 21px 264px;
  -moz-transform-origin: 21px 264px;
  -ms-transform-origin: 21px 264px;
  -o-transform-origin: 21px 264px;
  transform-origin: 21px 264px;
}

To finish things off, we should add a nice shadow to the mechanical parts. But the box-shadow property won’t work here as it would generate shadows based on the rectangle shapes of the image files – which looks poor. Instead, we can use the new drop-shadow filter introduced with CSS3. It works great on transparent images (at least in modern browsers of course).

Applying it to the div.mechanics container will work best:

.mechanics {
  -webkit-filter: drop-shadow(7px 7px 7px rgba(0, 0, 0, 0.5));
}

Here’s the final CSS we’ve created so far:

body {
  padding: 0;
  margin: 0;
  background: #2b211d url('./images/wood-pattern.jpg') center center repeat;
}
#clock {
  width: 650px;
  height: 650px;
  background: transparent url('./images/clock-face.png') 0 0 no-repeat;
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -325px 0 0 -325px;
}
.mechanics {
  -webkit-filter: drop-shadow(7px 7px 7px rgba(0, 0, 0, 0.5));
}
#sec,
#min,
#hour,
#button {
  position: absolute;
}
#sec {
  background: url('./images/sec-hand.png') 0 0 no-repeat;
  width: 42px;
  height: 348px;
  left: 304px;
  top: 37px;
  -webkit-transform-origin: 21px 288px;
  -moz-transform-origin: 21px 288px;
  -ms-transform-origin: 21px 288px;
  -o-transform-origin: 21px 288px;
  transform-origin: 21px 288px;
}
#min {
  background: url('./images/min-hand.png') 0 0 no-repeat;
  width: 42px;
  height: 324px;
  left: 304px;
  top: 61px;
  -webkit-transform-origin: 21px 264px;
  -moz-transform-origin: 21px 264px;
  -ms-transform-origin: 21px 264px;
  -o-transform-origin: 21px 264px;
  transform-origin: 21px 264px;
}
#hour {
  background: url('./images/hour-hand.png') 0 0 no-repeat;
  width: 60px;
  height: 240px;
  left: 295px;
  top: 125px;
  -webkit-transform-origin: 30px 200px;
  -moz-transform-origin: 30px 200px;
  -ms-transform-origin: 30px 200px;
  -o-transform-origin: 30px 200px;
  transform-origin: 30px 200px;
}
#button {
  background: url('./images/button.png') 0 0 no-repeat;
  width: 48px;
  height: 48px;
  left: 301px;
  top: 301px;
  -webkit-transform-origin: 24px 24px;
  -moz-transform-origin: 24px 24px;
  -ms-transform-origin: 24px 24px;
  -o-transform-origin: 24px 24px;
  transform-origin: 24px 24px;
}

Visiting the current project in your browser should give you something like this:

As you can see, the positioning worked out just fine. The lower parts of the hands can still be seen on the opposite side of the button. And the shadows are working as expected, too. I guess we’re ready to make that clock tick now.

The Javascript

Create a file called clock-app.js and place it next to the index.html file. I’d usually go with jQuery but I chose to keep it straight Javascript here, since there is no real need to load an extra library for our slim purposes and the app’s load is quite high already.

We wait for the onload event to make sure, all images are loaded and ready. Then we look up all the moving elements in the DOM and store them in variables. That way we don’t have to look for them every time again which is a good thing in terms of performance.
We also declare some more variables that will be used frequently.

window.onload = function() {
   // get the elements needed:
   var sec     = document.getElementById('sec');
   var min     = document.getElementById('min');
   var hour    = document.getElementById('hour');
   // declare some variables, we constantly use:
   var tmpRotValue  = "";
   var curDate, curSec, curMin, curHour, secRot, minRot, hourRot;

Next, we need a function, that sets the transform property of a given element to a given rotation in degrees. The setAttribute method can be used to set style attribute. Again, we’re faced with some browser specifics.

   /*
    * function setRotation(elem, degree)
    * Set the current rotation of an element to the value of degree
    */
   function setRotation(elem, degrees) {
      tmpRotValue = "rotate(" + degrees + "deg)";
      elem.setAttribute(
         "style",
         "-webkit-transform:"+tmpRotValue+"; -moz-transform:"+tmpRotValue+"; -ms-transform:"+tmpRotValue+"; -o-transform:"+tmpRotValue+"; transform:"+tmpRotValue+";" 
      );
   }

This function needs to be triggered on every second for each of the moving parts. Therefor, we write a tick() function that first calculates all the required values depending on the current time and then fires the setRotation function per element:

   /*
    * function tick();
    * Gets the current time, calculates all the required values and
    * triggers the setRotation function for each element
    */
   function tick() {
      // get the current date and time:
      curDate = new Date();
      // extract the values for sec, min and hour:
      curSec   = curDate.getSeconds();
      curMin   = curDate.getMinutes();
      curHour  = curDate.getHours();
      // make sure, the hour is in the range of [0..11] and not in [12..23]
      if ( curHour > 11 ) {
         curHour -= 12;
      }
      // calculate the rotations per hand:
      secRot   = curSec * 6;              // 360°/60sec   = 6° per second
      minRot   = curMin * 6;              // 360°/60min   = 6° per minute
      hourRot  = curHour * 30 + curMin/2; // 360°/12hours = 30° per hour
      // apply rotations:
      setRotation(sec,  secRot);
      setRotation(min,  minRot);
      setRotation(hour, hourRot);
   }

In the previous post, we talked about the calculations and the question, why we need to divide by 6 and 30.

Last thing we need to do, is calling this tick function one time on launch for initialization and then repeatedly every second using the setInterval function.

   // on time call on launch:
   tick();
   // set interval to 1 sec
   setInterval(function(){tick()}, 1000);
};

And that’s pretty much all we need the Javascript to do. Refresh the page in your browser and you should see the clock showing the current time with rotating hands.

Making It a Mobile Web App

If you view this app on your mobile device right now, it isn’t too charming as you might need to zoom and things. Adding a few meta tags to the HTML markup will turn this little website into a full featured standalone mobile app that behaves like a native iOS application.

Here’s what you need to add to the <head> section of your index.html:

<!-- set viewport width to 660px and disable zooming/scaling -->
<meta name="viewport" content="width=660,user-scalable=no">
<!-- tell iOS that this app can work in full screen mode. No need for navbars -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- link the app icons - shown on the home screen -->
<link rel="apple-touch-icon" href="./images/touch-icon-iphone.png" />
<link rel="apple-touch-icon" sizes="72x72" href="./images/touch-icon-ipad.png" />
<link rel="apple-touch-icon" sizes="114x114" href="./images/touch-icon-iphone4.png" />
<!-- link the start screen image - shown while the app launches -->
<link rel="apple-touch-startup-image" href="./images/startscreen-320x460.png">

You will obviously need to provide those images to make this work. Once the images are in place, revisit the page again with you iOS device and add the page to your home screen.

Launching the app from your home screen will now give you the feeling of native app. You got your icon, the custom start screen and the full screen app without any navigation bars and toolbars. The only problem left is, that the loading takes forever.

Performance Boosting with HTML 5’s Offline Storage

The app we created makes heavy use of images and therefor takes a lot of time to load – especially if you’re on low bandwidth. Also, there’s no chance to use it offline so far. HTML 5 provides a great new feature to fix those issues and this feature is called Application Cache. Implementing it for offline storage is as easy as linking one more file named cache.manifest.

Just change the <html> tag in the index.html file to this:

<html manifest="./cache.manifest">

Now, create a matching file named cache.manifest and link all required files we used above. The syntax for such files looks like this:

CACHE MANIFEST

# Version: 1.0

CACHE:
index.html
style.css
clock-app.js
images/button.png
images/clock-face.jpg
images/hour-hand.png
images/min-hand.png
images/sec-hand.png
images/wood-pattern.jpg
images/favicon.ico
images/touch-icon-ipad.png
images/touch-icon-iphone.png
images/touch-icon-iphone4.png
images/startscreen-320x460.png

This will cache all the files permanently on the device until the app gets removed from the home screen. So from now on, the app will even work offline or in flight mode and it will launch blazing fast.

You can watch your apache log files to check, that only a single request is made on launch now and that one’s done to verify, that the caching manifest is still up-to-date.

Note: Back in 2008 a somewhat related tutorial written by Toby Pitman was published on css-tricks.com. You’ll find similarities and meanderings as well when comparing this post to the one from Toby. I think, both are worth reading.

15 Comments on “How To Write a Full Screen Web App Optimized For iOS Devices

  1. This is awesome,

    How would you cover the IE fallback on this?

    Thanks a lot!

    • Thanks for your comment, Kyle. This app will only work in modern browsers as it’s making use of some of the latest CSS3 techniques. If you are targeting IE, you might want look into some jquery libs for rotating images. This one seems to support IE 6+, but I haven’t tested it:
      http://code.google.com/p/jqueryrotate/

      • Hey Daniel,

        Thanks for the response, yeah I came across that plugin when doing some research.

        Another question, Im having trouble using your calculation on my own clock in terms of the -webkit-transform-origin part just dont seem to display correctly. My clock is w:116, h:116 and my min hand is w:9, h:47 so what should my -webkit-transform-origin be as well as my left position etc?

        Thanks again
        Kyle

        • Hey Kyle,
          the transform-origin would be somewhere around (4.5px / 40px). That’s the horizontal center (half the width) and almost at the bottom of the hand.
          For the positioning, you’ll have:
          left: 116/2 – 4.5 = 53.5px;
          top: 116/2 -47 +7 = 18px;
          You might want to change the hand’s width to an even number, say 10px, to avoid fractional values. In that case, you’d end up with:
          transform-origin: 5px 40px;
          left: 53px;
          top: 18px;

          I hope, this will work for you.

  2. Sure- i replace your code by same one!But your is better for my clients- real oldscul clocks but background that i need-white

  3. Sorry- Thanks a lot for answers anyway

  4. Man, i really like this tutorial is very useful, the only thing i miss to make make a more realistic native app experience is to remove the „elastic effect“ (safari) when we drag on the clock. Would be nice if you can tell us how to do it.
    Thanks for your time and effort.

  5. HI there,

    I’m currently having a look at this. I’m trying to get the second hand to move more smoothly. Highend watches tend to beat somewhere around 6-8 times a second. I thought it would be a case of dividing everything by 6 but that doesn’t seem to work. Any Ideas?
    cheers
    Mike

    • Hey Mike,
      one way to get really smooth rotations would be to use the new CSS3 transition attribute applied to the transform attribute with a duration of 1 second. This might get the best results. I’ve been playing around with it myself and it works really well.
      In case you’d rather set the ticking to 8 Ticks Per Second, there’s a little more work to be done. You need to change the tick() function, so that it is based on milliseconds rather then just seconds. Also, when setting the interval for calling the tick() function, make sure to set the timer to 125ms instead of 1000ms. This will cause the tick function to be triggered 8 times per second.
      Personally, I would have a look at the CSS transitions first. Might save you a lot of trouble and you wouldn’t even need to change a single line of javascript.
      Good luck to you,
      Daniel

  6. Hi, I’ve already been a lurker close to your blog for a few months. I really like this article and your entire site! Looking forward to studying more!

  7. Can we add sound. like on the hour or every hour it makes a different sound. like a grandfather clock or coo-coo clock?

Leave a Reply

Comments are moderated, so don't spam please.

TOP