Today I gave a public Neudesic webcast, The Modern Web, Part 3: Social Networking. Below is the presentation from the webcast. The webcast recording should also be available soon at neudesic.com .
Tuesday, April 24, 2012
Monday, April 23, 2012
Adventures in Windows 8: Task Board, Part 1: An HTML5-JavaScript Metro App
I had an opportunity today to demo a Windows 8 Metro app at
the Windows 8 Developer event in LA along with my Neudesic colleague Mickey
Williams. I’ll describe here in Part 1 the demo app, Task Board, and in
subsequent parts we’ll explore how it was built. Task Board is not a fully
completed application, but its well along and is the result of only 3 days’
work—a testimony to how quickly you can get proficient at Metro app
development. This is also not what I would call a very sophisticated Metro app –
in fact it’s my second Metro app ever, and I’m still learning the ropes.
Tile and Splash Screen
The App Bar is typically shown on the bottom of applications, but I thought it worked better at top in this case. If you find yourself using the App Bar a lot, which you might when heavily editing tasks, you can pin it in place.
Storage
The commands at the right of the App Bar allow new projects to be created, existing projects to be opened, and projects to be saved. There’s also a Sample button which loads a sample project of tasks, making it easy for the application to be demoed.
You can develop Metro apps 3 ways: in C++/Xaml, in
.NET/Xaml, or in HTML5-JavaScript. I can work in all three, but since I’m
investing a lot of time these days in HTML5/JavaScript that was a natural
choice for me. Although WinRT (the Windows runtime) is careful to treat all
three development approaches as equal-class citizens, there is an advantage I
think to the HTML5/JavaScript approach in that you can leverage the mammoth
amount of open source libraries that are out there for the web such as jQuery
and many others.
One of the things you notice with tablets today is that while
they are really popular, most people use them to consume content (books, video),
browse the web, or interact with business-to-consumer apps. There aren’t all that
many examples of productivity apps or business apps on tablets yet. This is
where I think Windows 8 and Metro have an opportunity to really shine: you’ll
be able to get actual work done on Windows 8 tablets.
To have something decent to demo, I decided to create a new Metro application. I chose a task board application, in which you can put “cards on a wall” as an aid in project management. Each card represents a task. A task board is a useful visual aid in project team meetings where you can move cards around and consider "what if's".
Let's take a walk-through of the application.
Tile and Splash Screen
Like all Metro apps, Task Board has a tile in the Start
screen. It’s not a “live tile” yet in that the application doesn’t pass any
notification information up to the tile, but that’s a behavior that could be
added in the future.
Touching or clicking the tile launches Task Board, which
briefly displays this splash screen:
Both the tile and splash screen are simple PNG bitmap
images. There are 4 images in all, named logo.png, smalllogo.png, storelogo.png,
and splash.png.
Views and Navigation
Task Board has two views (with more to come): Task Board and
Rock Wall. Navigation between the views is accomplished with two simple links
at top. Down the road you may see this become sections that you can horizontally
slide between.
Additional views we’re likely to add in the future might also
include an expense view and a workload-per-team member view.
Task Board View
Task Board view is
the “cards on a wall” surface on which you can drag around cards. Dragging is a
good interface for touch devices, but we’ve been careful to make the experience
equally good for mouse and keyboard users as well. If you touch or click on a
card’s border or task number, it will be selected (indicated with a thick
border). If cards overlap, selecting a card brings it topmost.
The cards have fields for title, description, who, hours,
and status (to do, in progress, to verify, or completed). The status field
controls the card color. A task number is displayed at upper right.
To create a task card, or remove a selected task card, you use
the left-side commands on the App Bar. You can bring the App Bar into view by
swiping from the top or bottom of the screen; or via the shortcut key Windows +
Z. Other App Bar commands include Remove All, which deletes all task cards; and
Arrange, which nicely arranges the cards with a smooth animation where they
glide into place.
Rock Wall View
The Rock Wall is a
view of the tasks in terms of size, where tasks are rendered as boulders,
rocks, and stones based on the number of hours. Up to 8 hours is a pebble, up
to 40 hours is a rock, and anything larger is a boulder. The rocks fall into
place in a nice bounce animation.
Background
The default background is a green board, but there are other
choices. There’s a drop-down in the App Bar that lets you select other background
images or background colors. Below is the “Wood” theme.Storage
The commands at the right of the App Bar allow new projects to be created, existing projects to be opened, and projects to be saved. There’s also a Sample button which loads a sample project of tasks, making it easy for the application to be demoed.
There’s some additional work to be done here on storage,
including project naming and file browsing. What I have in mind is the
device/cloud model, as you may have seen in apps like the latest Kindle app for
iPad. The idea is your central storage is in the cloud (thus freeing you to
change devices whenever you want to), but you can work with the data locally on
your device if you choose to. Not in place yet, but coming. This is part of the
“personal cloud” concept that I am very much a believer in.
Development Approach
This gives you an overview of the application. In Part 2, we’ll
take a look at the internals. Stay tuned!
Monday, April 16, 2012
Outside-the-Box Pizza, Part 4: Social Integration with Twitter
In this
series, we’re looking at Outside-the-Box Pizza, a web-mobile-social-cloud
demo, from a variety of perspectives that include design, technologies, and
techniques. The online demo is at http://outsidetheboxpizza.com.
Here in Part 4, we’ll be looking at the social experience which in this app
revolves around the use of Twitter. We’ll
look at three things:
• Social Media Strategy
• Gamification in the Online Experience
• Twitter Integration with @Anywhere
• Gamification in the Online Experience
• Twitter Integration with @Anywhere
Social Media Strategy:
“Pizza as Individual as You Are”
Outside-the-Box Pizza knows the power of the mobile and
social phenomenon, and has crafted their company around appealing to the young,
hip crowd who live the digital lifestyle. From Day 1, their strategy has been
emphasizing the uniqueness and creativity of individuals. That’s why they have
the name they do, and it’s why they provide some very unusual pizza toppings (like
elk and mashed potato), shapes (square, heart), and sauces (BBQ, chocolate).
This fits very well with today’s ever-growing mobile-and-social communities.
Their tag line is, “Pizza as individual as you are”.
Outside-the-Box Pizza is savvy about modern online marketing
and is very much aware of the important role social media plays in keeping an
ongoing relationship with new and existing customers (see Flip
the Funnel by Joseph Jaffe for more on that).
There are many ways to integrate with social networks: you
can view/search content, post content, and allow sign-in with social
identities. There are also many social networks to choose from. Outside-the-Box
Pizza has chosen to focus their efforts around Twitter, where clients will
tweet about unusual pizzas. Outside-the-Box Pizza will tweet as well.
Gamification in the
Online Experience
When a customer orders a unique pizza, Outside-the-Box Pizza
wants them to tweet about it. On the Tweetza Pizza page, clients can see the twitter
feed for #outsideboxpizza and, if they wish, sign in with a Twitter id and add
their own tweets. Of course, they can also do this directly via Twitter itself,
but there’s value in integrating it with the application.
It isn’t necessary for users to identify themselves to view
the Twitter feed, but in order to send tweets they do need to sign in. Clicking
on the Connect with Twitter button
brings up a Twitter sign-in dialog.
To promote the most interesting pizzas, Outside-the-Box
Pizza has a section of the site called Cool Pizzas which is manually curated by
an editor.
Twitter Integration
with Twitter @Anywhere
Social network integration is possible from the front-end or
the back-end of a web application. In this case, Outside-the-Box Pizza uses
front-end integration. That is, all of the integration work is being done in
the HTML/JavaScript layer that runs in the web browser. Twitter provides an
easy-to-use API for this named @Anywhere.
The first step in using @Anywhere is to register an
application. In doing this you’ll identify the ultimate deployment URL of your
application and receive both a consumer key and a consumer secret which will be
used to positively identify your application as legitimate.
In the web application, we load the Twitter @Anywhere
JavaScript library with a script line like this in the section of
the web site.
<script src="http://platform.twitter.com/anywhere.js?id=yxWqT5gTZ4pfhw&v=1"
type="text/javascript">script>
The result of this is that we’ll have a JavaScript object named twttr that we can reference for various behaviors.
Connect Button
To add a Connect to
Twitter button to the site, we turn a placeholder div into a live sign-in
interface using this invocation on the twttr object:
<script>
twttr.anywhere(function (T) {
T("#login").connectButton({ size: "medium" });
});
script>
Tweet Box
To put up a tweet box, we invoke it this way. This pre-loads
the tweet box with the hashtag “#outsideboxpizza”.
twttr.anywhere(function
(T) {
var width = document.getElementById('logo').offsetWidth - 20;
T("#tbox").tweetBox(
{
height: 60,
width: width,
defaultContent: "#outsideboxpizza "
});
});
Twitter Feed
Although we can also use the twttr object to get the Twitter
feed for our hashtag, we chose here to use jTweetsAnywhere jQuery
library which encapsulates the logic and nicely formats the display for us.
After loading its JavaScript and CSS scripts, this code gives us our feed:
$('#tweetFeed').jTweetsAnywhere({
searchParams: 'q=outsideboxpizza',
count: 5,
showTweetFeed: {
showProfileImages: true,
showUserScreenNames: true,
showUserFullNames: true,
showActionReply: true,
showActionRetweet: true,
showActionFavorite: true,
autorefresh: {
mode: 'auto-insert',
interval: 30
}
},
showTweetBox: false
});
Summary
Social integration is a key concern for modern web software.
Outside-the-Box Pizza focuses on Twitter integration in the front end, using
the Twitter @Anywhere API. The result is an ongoing social touchpoint with
customers. Through the use of gamification, what might be viewed as a commodity,
pizza, has been made into a fun, creative experience that consumers want to brag
about over Twitter.
Next: Part 5: Hosting The Front End in the Cloud
Next: Part 5: Hosting The Front End in the Cloud
Saturday, April 14, 2012
Outside-the-Box Pizza, Part 3: Mobility & Responsive Web Design
In this
series, we’re looking at Outside-the-Box Pizza, a web-mobile-social-cloud
demo, from a variety of perspectives that include design, technologies, and
techniques. The online demo is at http://outsidetheboxpizza.com.
Here in Part 3, we’ll be looking at how the app supports mobile devices,
primarily through the technique of Responsive Web Design. We’ve seen the
desktop browser experience, but how does Outside the Box Pizza render on
smartphones and tablets? We’ll look at four things:
·
Responsive Web Design and CSS3 Media Queries
·
Font Size and Responsive Text
·
Running Full-Screen on the iPad
·
Sticky Footers
The Goal: Broad Reach
We want our web applications to have broad reach, to work on
as many devices as possible. Below you can see some examples of Outside the Box
Pizza on various devices. It renders acceptably on smartphones (iPhone,
Android, Windows Phone 7), tablets (iPad, Windows 8), and desktop browsers
(Chrome, Firefox, Safari, Internet Explorer). It also handles orientation
changes on smartphones and tablets.
Outside-the-Box Pizza on various Phones and Tablets
Responsive Web Design & CSS3 Media Queries
The key technique to support a wide variety of modern
devices well is Responsive Web
Design, the brainchild of Ethan Marcotte. His book on the subject is highly
recommended. Responsive Web Design is about adapting to the device we find
ourself running on and not making assumption in advance. The most articulated aspect
of RWD is adaptive layout for different size and orientation devices, but the
principal can be extended to other areas. Let’s first look at device
dimensions.
We can use the CSS3 media queries feature to have conditional
CSS styling that only applies to devices meeting certain criteria. For example,
this is how we express conditional styling in our CSS that should apply to
smartphones, which generally have a width of 320 to 480 pixels:
/******************** * * * Mobile Styles * * * ********************/ @media only screen and (max-width: 480px) { header .float-left, header .float-right { float: none; } header .site-title { margin: 10px; text-align: center; } .selectGroup li { display: inline-block; width: 120px } h1 { font-size: 1.4em; } h2 { display: none; } .logo { font-size: 1.75em } .logo i { display: none } .text i { display: none } .text b { display: none } span b { display: none } span i { display: none } button b { display: none } article { font-size: 0.8em } article i { display: none } article B { display: none } ...
In our case the default CSS is for the desktop browser and our conditional CSS adapts to smaller mobile device dimensions, but I should point out that an alternative way to go is to have your default styles target phones and use conditional styling for larger sizes. Today we have pretty widespread support for HTML5 and CSS3 media queries in our mobile browsers, but when that was less common doing what I just described was important so that the default rendering of the site on “dumb phones” would still be acceptable. Supporting dumb phones wasn’t a priority in this application.
None of the styles in these conditional sections are new;
that is, all of the style rules have been previously defined in the base CSS.
Rather, what we’re doing here is overriding many of our styles for the
small device. There are several reasons to override styles for smaller size
screens:
1.
Spacing. We may need to change margins and padding
to reduce white space.
2.
Sizes. We may need to change widths, or heights.
3.
Type. We may need to change font characteristics
such as size or font family.
4.
Hiding. We may need to hide elements for which
we don’t have room to show.
5.
Layout. We may choose to arrange elements differently.
You can reduce the amount of work you need to do here by
thinking in percentages as RW advocates. The more we use percentages in our CSS
styling rather than fixed units, the less need there is for conditional styles.
Layout Flow
We can solve a lot of problems simply by choosing our layout
flow wisely. For example, Outside-the-Box Pizza has an orders page in which
many choices are listed for pizza shape, sauce, meat toppings, and veggie
toppings. These are all expressed as list items, which are styled such that the
browser will intelligently wrap as many across as possible. Notice how this
renders on various screen sizes:
Also notice the toppings icons are designed to be easily
touched as well as clicked. When a topping is active, we mark the item as
checked and use styling to show its bounding circle in green rather than gray (the
“circles” are actually rectangular borders with a generous border radius).
Here’s the style for the order list items and how the list
items are defined:
.topping { font-family: Arial, Helvetica; font-size: 1.0em; color: Black } .topping input { display: none; vertical-align: middle; margin-right: 4px; } .topping span { font-family: Arial, Helvetica; font-size: 1.2em; color: Black } .topping label { display: inline; font-family: Arial, Helvetica; font-size: 1.2em; color: Black; vertical-align: middle; } .topping:checked { background-color: #C00000 } .topping:checked+label { background-color: #C00000 } .topping img { display: inline; vertical-align: middle; margin-right: 4px; height: 48px; border: 4px solid Silver; -webkit-border-radius: 32px; -moz-border-radius: 32px; border-radius: 32px; }
<div class="selectGroup"> <div class="selectGroupTitle">Meat Toppings</div> <ul> <li class="topping"><label for="topPepperoni"><img src="~/Images/topping_pepperoni.png" alt="image"/><input id="topPepperoni" name="jqdemo" value="value1" type="checkbox"/>pepperoni</label></li> <li class="topping"><label for="topSausage"><img src="~/Images/topping_sausage.png" alt="image"/><input id="topSausage" name="jqdemo" value="value1" type="checkbox"/>sausage</label></li> <li class="topping"><label for="topHam"><img src="~/Images/topping_ham.png" alt="image"/><input id="topHam" name="jqdemo" value="value1" type="checkbox"/>ham</label></li> <li class="topping"><label for="topBacon"><img src="~/Images/topping_bacon.png" alt="image"/><input id="topBacon" name="jqdemo" value="value1" type="checkbox"/>bacon</label></li> <li class="topping"><label for="topBeef"><img src="~/Images/topping_beef.png" alt="image"/><input id="topBeef" name="jqdemo" value="value1" type="checkbox"/>beef</label></li> <li class="topping"><label for="topChicken"><img src="~/Images/topping_chicken.png" alt="image"/><input id="topChicken" name="jqdemo" value="value1" type="checkbox"/>chicken</label></li> <li class="topping"><label for="topTurkey"><img src="~/Images/topping_turkey.png" alt="image"/><input id="topTurkey" name="jqdemo" value="value1" type="checkbox"/>turkey</label></li> <li class="topping"><label for="topElk"><img src="~/Images/topping_elk.png" alt="image"/><input id="topElk" name="jqdemo" value="value1" type="checkbox"/>elk</label></li> <li class="topping"><label for="topSardines"><img src="~/Images/topping_sardine.png" alt="image"/><input id="topSardines" name="jqdemo" value="value1" type="checkbox"/>sardines</label></li> <li class="topping"><label for="topEgg"><img src="~/Images/topping_egg.png" alt="image"/><input id="topEgg" name="jqdemo" value="value1" type="checkbox"/>egg</label></li> </ul> </div> <div class="selectGroup"> <div class="selectGroupTitle">Veggie Toppings</div> <ul> <li class="topping selectedTopping"><label for="topCheese"><img src="~/Images/topping_cheese.png" alt="image"/><input id="topCheese" name="jqdemo" value="value1" type="checkbox" checked="checked"/>cheese</label></li> <li class="topping"><label for="topTomato"><img src="~/Images/topping_tomato.png" alt="image"/><input id="topTomato" name="jqdemo" value="value1" type="checkbox"/>tomatoes</label></li> <li class="topping"><label for="topPineapple"><img src="~/Images/topping_pineapple.png" alt="image"/><input id="topPineapple" name="jqdemo" value="value1" type="checkbox"/>pineapple</label></li> <li class="topping"><label for="topOnions"><img src="~/Images/topping_onion.png" alt="image"/><input id="topOnions" name="jqdemo" value="value1" type="checkbox"/>onions</label></li> <li class="topping"><label for="topPeppers"><img src="~/Images/topping_pepper.png" alt="image"/><input id="topPeppers" name="jqdemo" value="value1" type="checkbox"/>peppers</label></li> <li class="topping"><label for="topSprouts"><img src="~/Images/topping_sprouts.png" alt="image"/><input id="topSprouts" name="jqdemo" value="value1" type="checkbox"/>sprouts</label></li> <li class="topping"><label for="topArtichoke"><img src="~/Images/topping_artichoke.png" alt="image"/><input id="topArtichoke" name="jqdemo" value="value1" type="checkbox"/>artichoke</label></li> <li class="topping"><label for="topGorgonzola"><img src="~/Images/topping_gorgonzola.png" alt="image" /><input id="topGorgonzola" name="jqdemo" value="value1" type="checkbox"/>gorgonzo</label></li> <li class="topping"><label for="topBroccoli"><img src="~/Images/topping_broccoli.png" alt="image" /><input id="topBroccoli" name="jqdemo" value="value1" type="checkbox"/>broccoli</label></li> <li class="topping"><label for="topPotato"><img src="~/Images/topping_potato.png" alt="image" /><input id="topPotato" name="jqdemo" value="value1" type="checkbox"/>m. potato</label></li> </ul> </div>
Font Size and Responsive
Text
The main thing RWD has to say about text is to set font
sizes based on “em” units rather than fixed unit sizes like pixels. In
typography, an “em” is the width of the capital letter M in a typeface. Since
we want to preserve a user’s right to control the default font size in their
browser (for accessibility reasons, for example), doing all of our type setting
relative to the “em” is the best approach. Thus our type settings for headings,
text, button captions, etc. are all based on the em. Again, we may choose to
use different proportions in our conditional styles for some devices.
h1 {
font-size: 1.8em;
}
h2 {
font-size: 1.5em;
}
h3 {
font-size: 1.2em;
}
Another thing we can do to apply the philosophy of RWD to text is to responsive text, which has to do with right-sizing the amount of text content we display. For example, the navigation buttons and top-of-page text on a desktop browser look like this:
Desktop Button Captions and Page Text
…whereas on a phone they look like this, with shorter button
captions and shorter page text. The button caption shortening prevents the
buttons from wrapping to the next line on many phones. The text shortening
prevents the text from taking up too much of the screen so that the content
below can be seen without scrolling.
Implementing responsive text is a simple matter of choosing
some text emphasis style (like <b> or <i>) and defining it to be
visible for large screens and hidden on small screens. This is how the button
captions and page text are defined in the HTML:
<nav> <div class="content-wrapper"> <button id="buttonSpecialOffers" class="buttonUnselected" #nclick="javasc#ipt:window.location.href = '/';"><b>Special </b>Offers</button> <button id="buttonOrderPizza" class="buttonUnselected" #nclick="javasc#ipt:window.location.href = '/Order';">Order<b> Pizza</b></button> <button id="buttonTweet" class="buttonUnselected" #nclick="javasc#ipt:window.location.href = '/Share';">Tweet<b>za Pizza</b></button> <button id="buttonPizzaSightings" class="buttonUnselected" #nclick="javasc#ipt:window.location.href = '/Cool';">Cool<b> Pizzas</b></button> </div> </nav>
All it takes to apply this simply technique with finesse is to think through where you use longer and shorter text and ensure both versions communicate your intent well. Here’s another example of responsive page text:
<span class="text">Ordered an unusual pizza for an
unusual occasion? Tweet it and you may win a discount<i> on your next
order. We regularly award prizes for most unusual pizza, most unusual occasion,
and even most unusual customer<i>!<span>
Full Screen on the
iPad
When you access a web application on the iPad, this is how
the app will look in the Safari Mobile browser by default. While there’s
nothing wrong with the web display, we’d really like the app to have more the
look of a native app, preferably running full screen without the browser’s tabs
or address bar being visible.
Outside the Box Pizza on iPad - Default Appearance
We can achieve this on the iPad by 1) adding the HTML meta
element below to our markup and 2) getting the user to add the web site to
their home screen.
<meta name="apple-mobile-web-app-capable" content="yes" />
With the above accomplished, the “app” now has an app icon
on the home screen and when invoked runs full-screen. The entire experience is
now much closer to that of a native app.
Outside the Box Pizza on iPad - Full Screen
Responsive Images
One other area you should be looking at is sizing your
images appropriate for your target device. You don’t want to send a large,
hi-resolution image to a phone that is incapable of showing it that way: it’s a
waste of bandwidth and slows down you app’s loading and rendering time.
We haven’t put responsive images into place yet in Outside the
Box Pizza, but we will be doing so (and will update this post when that
happens).
Sticky Footers
A sticky footer
will smartly snap to the bottom of the display, which looks a lot more app-like
than simply appearing at the end of the content. Sticky footers are best done
in CSS (rather than JavaScript): they flow more smoothly and are less-taxing. A
well-done sticky footer will also detect and handle long scrolling pages and in
that case simply put the footer at the end of the content. Here’s a technique
for a sticky footer in CSS you can use (source: http://ryanfait.com/sticky-footer/).
Here’s the
relevant CSS for Outside-the-Box Pizza:
html { margin: 0; padding: 0; } html, body { height: 100%; } #wrap { min-height: 100%; height: auto !important; height: 100%; margin: 0 auto -3.0em; /* Set footer height. */ } footer, .push { height: 3.0em; /* Set footer height. */ }Here’s what the rules do:
·
The first rule performs a reset of margin and
padding for everything in the document.
·
The second rule sets the height to 100% for the
html and body elements.
·
The #wrap rule defines a wrapper style, which
will enclose everything in the body except the footer.
·
The .footer / .push rule makes space for the
footer.
Notice the
height in the #wrap rule is -1 x the height specified in the footer / .push
rule. You can use any height you wish, but they must match.
In the HTML
markup, we enclose the entire contents of your body in a wrapper div, except
the footer – wrap that in a div with the footer class. The wrapper div should
include a div with class push at the bottom which is empty. That’s all there is
to it.
<div id="wrap"> <header> <div id="banner" class="content-wrapper"> <div id="logo" class="logo"> <div>outside the box <b>pizza</b> <img src="http://outsidetheboxpizza.blob.core.windows.net/images/icon.png" alt="icon" /></div> <div><i>pizza as individual as you are.</i></div> </div> </div> </header> <nav> <div class="content-wrapper"> <button id="buttonSpecialOffers" class="buttonUnselected" onclick="javascript:window.location.href = '/';"><b>Special </b>Offers</button> <button id="buttonOrderPizza" class="buttonUnselected" onclick="javascript:window.location.href = '/Order';">Order<b> Pizza</b></button> <button id="buttonTweet" class="buttonUnselected" onclick="javascript:window.location.href = '/Share';">Tweet<b>za Pizza</b></button> <button id="buttonPizzaSightings" class="buttonUnselected" onclick="javascript:window.location.href = '/Cool';">Cool<b> Pizzas</b></button> </div> </nav> <div id="body"> <section class="content-wrapper main-content clear-fix"> @RenderBody() </section> </div> <div class="push"><!--Sticky Footer Push--></div> </div> <footer> <div class="content-wrapper" style="line-height: 3.0em"> <div class="logo"> <span style="float: left; color: White; font-size: 0.65em; margin-right: 8px;"><a style="color: White; font-size: 0.75em" href="/Activity" >Sales</a></span> <span style="float: left; color: White; font-size: 0.65em; margin-right: 8px;"><a id="StoreLink" style="color: White; font-size: 0.75em" href="/Store/Orders/@ViewBag.StoreId">Store</a></span> <span style="float: left; color: White; font-size: 0.65em; margin-right: 8px;"><a id="DeliveryLink" style="color: White; font-size: 0.75em" href="/Store/Driver/@ViewBag.StoreId">Driver</a></span> <span style="float: right; color: White; font-size: 0.65em; margin-right: 10px"><a style="color: White; text-decoration: none; font-size: 0.75em" href="/Home/About">About</a></span> </div> </div> </footer>
Summary
Outside-the-Box Pizza leverages responsive web design to
adapt its layout to many kinds and sizes of device in order to extend its
reach. It renders well on desktop browsers, tablets, and smartphones. Beyond
layout, the principle of responsive web design is also seen in the use of responsive
text and, in the future, responsive images. Using sticky footers makes the web
application seem more like a native app, as does going full-screen if the platform/browser
provides a means of achieving that.
Next: Part 4: Social Integration with Twitter
Next: Part 4: Social Integration with Twitter
Tuesday, April 10, 2012
CSS3 Animation and Transitions Tutorial: Soccer Ball Animation
In this post we’ll show you how we created a soccer ball animation using CSS3 animation and transitions. We’ll do this in 6 steps. You can see each step as well as the final result here (you’ll need a screen width of 1650 pixels or more).
The soccer ball shoots up into the air from the left side of the field and goes back down to the right side of the field. This goes back and forth for a total of 3 animations. There is also a transition you can trigger once the animation completes: hovering over the ball hurls it up into the face of a spectator in the foreground at bottom right.
In Step 1 we start with a basic page that has a div with an image for the soccer field (‘field’) and another with an image for the ball (‘ball’).
Now our ball grows in size and has a movement path that aligns with the background. It even pops out of its bounding frame, seemingly.
Now we can perform the animation with IE10 and the latest versions of Chrome, Firefox, and Safari. If we wanted to also support Opera or additional browsers that support animation, it’s just a matter of also including their prefixes as well.
We define a hover style rule for ball that changes position, size, and rotation:
We then reference the transition in the rule for ball, specifying a 10 second duration.
Here’s the updated style:
To see the source, visit the online demo, pull up any of the steps, and view source (each is a single page).
The soccer ball shoots up into the air from the left side of the field and goes back down to the right side of the field. This goes back and forth for a total of 3 animations. There is also a transition you can trigger once the animation completes: hovering over the ball hurls it up into the face of a spectator in the foreground at bottom right.
Here are the steps:
1. Setting up a Foundation with Modernizr
2. Animating Size
3. Animating Position
4. Animation Rotation
5. Supporting Multiple Browsers
6. Adding a Transition
Step 1: Setting up a
Foundation with Modernizr
In Step 1 we start with a basic page that has a div with an image for the soccer field (‘field’) and another with an image for the ball (‘ball’).
DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" class="no-js">
<head>
...
<head>
<body>
<div id="notsupported">Sorry, Your Browser DOES NOT Support
CSS Animations and Transitionsdiv>
<div id="supported">Yay, Your Browser DOES Support CSS
Animations and Transitionsdiv>
<div class="field">
<img src="background.jpg"
alt="field"
/>
div>
<div class="ball">
<img src="ball.png"
alt="ball"
/>
div>
<body>
<html>
We want to use the Modernizr library to test for the
availability of the features we need, which in this case is css animations and
transitions. We include it in the
area of our page.
<script src="http://ajax.aspnetcdn.com/ajax/modernizr/modernizr-2.0.6-development-only.js"
type="text/javascript"<script>
If css animations and transitions are not present, we want
to display the following red sorry message and hide the field and ball.
If we do have animations and transitions available, we
display a green confirming message, show the field and ball:
We accomplish this with the following CSS styles. This works
because Modernizr will set classes on the element with names like
cssanimations, no-cssanimations, etc. based on the features it detects.
<style type="text/css">
/* Display whether or not CSS Animations and
Transitions are supported */
.cssanimations #notsupported
{
display: none;
}
.cssanimations #supported
{
display: inline;
padding: 8px;
background-color: green;
color: white;
font-family: Sans-Serif;
}
.no-cssanimations #notsupported
{
display: inline;
padding: 8px;
background-color: red;
color: white;
font-family:
Sans-Serif;
}
.no-cssanimations #supported
{
display: none;
}
.no-cssanimations .field
{
display: none;
}
.no-cssanimations .ball
{
display: none;
}
.csstransitions #notsupported
{
display: none;
}
.csstransitions #supported
{
display: inline;
padding: 8px;
background-color: Green;
color: White;
}
.no-csstransitions #notsupported
{
display: inline;
padding: 8px;
background-color: Red;
color: White;
}
.no-csstransitions #supported
{
display: none;
}
.no-csstransitions .field
{
display: none;
}
.no-csstransitions .ball
{
display: none;
}
/* Image of soccer stadium, field, and crowd */
.field {
position: absolute;
top: 150px;
left: 0px;
height: 300px;
width: 1500px;
text-align: left;
}
/* Ball container */
.ball
{
position: absolute;
top: 300px;
left: 700px;
height: 200px;
width: 200px;
}
/* Ball image */
.ball img
{
max-height: 100%;
max-width: 100%;
}
<style>
When we run this in common browsers, we get the red not
supported message in IE9 and the green confirming message in IE10, Chrome,
Firefox, and Safari.
Step 2: Animating
Size
Now we want to put some animation in place, starting with
the size of the ball. To do CSS animation today, you need to use vendor
prefixes since theimplementations are pre-ratification of the standards. That
means instead of using style properties like animate: we’ll be using –webkit-animate
for web kit (Chrome, Safari), -moz-animate for Firefox, and –ms-animate for IE.
For now, we’ll just put in the –webkit prefix and add the others in a later
step.
In the CSS, we achieve the animation by defining a keyframes
animation (‘throwing’) and then referring the animation in the style rule for
ball. Let’s parse the animation reference in the .ball rule first. The line
below means, for webkit browsers, apply the animation ‘throwing’ over a
duration of 10 seconds, do it three times, and alternate direction every other
time.
-webkit-animation: throwing
10s 3 alternate;
The keyframes animation indicates changes at 0%, 50%, and
100% of the animation. Size goes from 5px height and width to 200px at the
midpoint and back down to 5px at end. This gives the effect of a ball getting
closer and closer at it soars toward us, then falls back again down to the
field.
/* Animation keyframes */
@-webkit-keyframes throwing {
0% { height: 5px; width: 5px; }
50% {
height: 200px; width: 200px; }
100%
{ height: 5px; width: 5px; }
}
Animating size is a good first step, but we’ll need to
animate some other characteristics in order to make this effect more
convincing.
Step 3: Animating Position
Now that we have the ball size animating, let’s control the
start, middle, and end position of the ball to correspond with areas of the
background soccer field image. To do this, we simply expand the keyframes
animation to also modify the top and left positions.
/* Animation keyframes */
@-webkit-keyframes throwing {
0% { height: 5px; width: 5px; top: 580px; left: 660px }
50% {
height: 200px; width: 200px; top: 50px; left: 800px }
100%
{ height: 5px; width: 5px; top: 400px; left: 875px }
}
Now our ball grows in size and has a movement path that aligns with the background. It even pops out of its bounding frame, seemingly.
Step 4: Animating Rotation
A final touch on the animation is to have the ball rotate.
We can achieve that by also adding a transform that rotates the ball. We’ll
rotate to 180 degrees by midpoint and on to 360 by the end.
/* Animation keyframes */
@-webkit-keyframes throwing {
0% { height: 5px; width: 5px; top: 580px; left: 660px }
50% { -webkit-transform:
rotate(180deg); height:
200px; width: 200px; top: 50px; left: 800px }
100%
{ -webkit-transform: rotate(360deg);
height: 5px; width: 5px; top: 400px; left: 875px }
}
Step 5: Supporting Multiple
Browsers
Up till now we’ve just been putting the –webkit-xxx
animation keywords, which gets us working in Chrome and Safari. Now we want to
support Firefox and IE as well. We’ll do that by adding –moz-xxx and –ms-xxx
keywords. Here’s our updated style.
<style type="text/css">
...
/* Ball container */
.ball
{
position: absolute;
top: 400px;
left: 875px;
height: 5px;
width: 5px;
-webkit-animation: throwing
10s 3 alternate;
-moz-animation: throwing
10s 3 alternate;
-ms-animation: throwing
10s 3 alternate;
animation: throwing 10s 3 alternate;
}
/* Ball image */
.ball img
{
max-height: 100%;
max-width: 100%;
}
/* Animation keyframes */
@-webkit-keyframes throwing {
0% { height: 5px; width: 5px; top: 580px; left: 660px }
50% {
-webkit-transform: rotate(180deg);
height: 200px; width: 200px; top: 50px; left: 800px }
100%
{ -webkit-transform: rotate(360deg);
height: 5px; width: 5px; top: 400px; left: 875px }
}
@-moz-keyframes throwing {
0% { height: 5px; width: 5px; top: 580px; left: 660px }
50% {
-moz-transform: rotate(180deg);
height: 200px; width: 200px; top: 50px; left: 800px }
100%
{ -moz-transform: rotate(360deg);
height: 5px; width: 5px; top: 400px; left: 875px }
}
@-ms-keyframes throwing {
0% { height: 5px; width: 5px; top: 580px; left: 660px }
50% {
-ms-transform: rotate(180deg);
height: 200px; width: 200px; top: 50px; left: 800px }
100%
{ -ms-transform: rotate(360deg);
height: 5px; width: 5px; top: 400px; left: 875px }
}
@keyframes throwing {
0% { height: 5px; width: 5px; top: 580px; left: 660px }
50% {
transform: rotate(180deg);
height: 200px; width: 200px; top: 50px; left: 800px }
100%
{ transform: rotate(360deg);
height: 5px; width: 5px; top: 400px; left: 875px }
}
<style>
Now we can perform the animation with IE10 and the latest versions of Chrome, Firefox, and Safari. If we wanted to also support Opera or additional browsers that support animation, it’s just a matter of also including their prefixes as well.
Step 6: Adding a
Transition
We’ve done animations up till now, which are applied immediately
upon definition. Transitions, in contrast, are trigged by some action. We’ll
add a transition that hurls the ball into a spectator, and we’ll trigger it
when the ball is hovered over.
We define a hover style rule for ball that changes position, size, and rotation:
/* Hover transition */
.ball:hover
{
height: 200px;
width: 200px;
top: 480px;
left: 1100px;
-webkit-transform: rotate(360deg);
/* -moz-transform: rotate(360deg); <= breaks hover transition in Firefox,
presumably a bug */
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
We then reference the transition in the rule for ball, specifying a 10 second duration.
/* Ball container */
.ball
{
position: absolute;
top: 400px;
left: 875px;
height: 5px;
width: 5px;
-webkit-animation: throwing
10s 3 alternate;
-moz-animation: throwing
10s 3 alternate;
-ms-animation: throwing
10s 3 alternate;
animation: throwing 10s 3 alternate;
-webkit-transition: all
10s;
-moz-transition: all 10s;
-ms-transition: all 10s;
transition: all 10s;
}
Here’s the updated style:
<style type="text/css">
...
/* Image of soccer stadium, field, and crowd */
.field {
position: absolute;
top: 150px;
left: 0px;
height: 300px;
width: 1500px;
text-align: left;
}
/* Ball container */
.ball
{
position: absolute;
top: 400px;
left: 875px;
height: 5px;
width: 5px;
-webkit-animation: throwing
10s 3 alternate;
-moz-animation: throwing
10s 3 alternate;
-ms-animation: throwing
10s 3 alternate;
animation: throwing 10s 3 alternate;
-webkit-transition: all
10s;
-moz-transition: all 10s;
-ms-transition: all 10s;
transition: all 10s;
}
/* Ball image */
.ball img
{
max-height: 100%;
max-width: 100%;
}
/* Animation keyframes */
@-webkit-keyframes throwing {
0% { height: 5px; width: 5px; top: 580px; left: 660px }
50% {
-webkit-transform: rotate(180deg);
height: 200px; width: 200px; top: 50px; left: 800px }
100%
{ -webkit-transform: rotate(360deg);
height: 5px; width: 5px; top: 400px; left: 875px }
}
@-moz-keyframes throwing {
0% { height: 5px; width: 5px; top: 580px; left: 660px }
50% {
-moz-transform: rotate(180deg);
height: 200px; width: 200px; top: 50px; left: 800px }
100%
{ -moz-transform: rotate(360deg);
height: 5px; width: 5px; top: 400px; left: 875px }
}
@-ms-keyframes throwing {
0% { height: 5px; width: 5px; top: 580px; left: 660px }
50% {
-ms-transform: rotate(180deg);
height: 200px; width: 200px; top: 50px; left: 800px }
100%
{ -ms-transform: rotate(360deg);
height: 5px; width: 5px; top: 400px; left: 875px }
}
@keyframes throwing {
0% { height: 5px; width: 5px; top: 580px; left: 660px }
50% {
transform: rotate(180deg);
height: 200px; width: 200px; top: 50px; left: 800px }
100%
{ transform: rotate(360deg);
height: 5px; width: 5px; top: 400px; left: 875px }
}
/* Hover transition */
.ball:hover
{
height: 200px;
width: 200px;
top: 480px;
left: 1100px;
-webkit-transform: rotate(360deg);
/* -moz-transform: rotate(360deg); <= breaks hover transition in Firefox,
presumably a bug */
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
<style>
Summary
CSS animations and transitions are extremely powerful and
fairly easy to learn, but their lack of ubiquity requires discipline in
checking for feature availability for which Modernizr is invaluable. A good animation or transition needs to be
planned out and storyboarded, much like a movie scene, before it is clear what
needs to be done to cleanly implement it.
Subscribe to:
Posts (Atom)