Building HTML5Tablet Appsfor iOS and Android@skirchmeier@mikebollingerMINNEWEBCON 2012
The elephant in the room:!HTML5 apps kinda suck.
Native vs. HTML5
Native                         HTML5-- Expensive to develop        + Cheaper to develop-- One platform at a time      + Al...
Reasons
1Money
2Time
3Audience
4Control
1       2       3         4Money   Time   Audience   Control
5  T A B L E T S 	   H A V E 	   F L E X I B L E 	  Design Patterns
1       2       3         4Money   Time   Audience   Control              5           Flexible           Design           ...
Change the  game.
Examples
Best Buyhttp://bby-uv.appspot.com/    Financial Times    http://app.ft.com         Proof  http://proofwhisky.com
Best Buy              http://bby-uv.appspot.com/iOS Only >!       Financial Times                  http://app.ft.comiOS On...
Frameworks
jQuery Mobile   jQTouch Sencha Touch
Native Wrappers
PhoneGap Titanium
Our Custom App
http://livefront.com/explore
How do we build     this?
Step 1:Travel the world
Step 2:HTML (sorry)
<!DOCTYPE html><html>  <head>    ...  </head>  <body>    <div id="container">       <div id="splash" class="page">        ...
<!DOCTYPE html><html>  <head>    ...  </head>  <body>    <div id="container">       <div id="splash" class="page">        ...
<!DOCTYPE html><html>  <head>    ...  </head>  <body>    <div id="container">       <div id="splash" class="page">        ...
<!DOCTYPE html><html>  <head>    ...  </head>  <body>    <div id="container">       <div id="splash" class="page">        ...
<!DOCTYPE html><html>  <head>    ...  </head>  <body>    <div id="container">       <div id="splash" class="page">        ...
<!DOCTYPE html><html>  <head>    ...  </head>  <body>    <div id="container">       <div id="splash" class="page">        ...
<!DOCTYPE html><html>  <head>    ...  </head>  <body>    <div id="container">       <div id="splash" class="page">        ...
<div id="content" class="page">  <div id="map"></div>  <div id="photos">    <div id="slides-container">       <div class="...
<div id="content" class="page">  <div id="map"></div>  <div id="photos">    <div id="slides-container">       <div class="...
<div id="content" class="page">  <div id="map"></div>  <div id="photos">    <div id="slides-container">       <div class="...
<div id="content" class="page">  <div id="map"></div>  <div id="photos">    <div id="slides-container">       <div class="...
<!DOCTYPE html><html>  <head>    ...  </head>  <body>    <div id="container">       <div id="splash" class="page">        ...
Techniques
Transitions      Work OfflineTouch Events     SizingHardware (GPS)   Distribution
Transitions      Work OfflineTouch Events     SizingHardware (GPS)   Distribution
Transitions Two ways to animate:  1. jQuery (.animate)  2. CSS (-webkit-transform)  	  
Transitions Two ways to animate:  1. jQuery (.animate)  2. CSS (-webkit-transform)  	  
TransitionsWhat can we do with CSS?
Transitions   -webkit-transform       translate          scale          skew         rotate   -webkit-animation
Transitions   -webkit-transform       translate          scale          skew         rotate   -webkit-animation
Transitions<!DOCTYPE html><html>  <head>    ...  </head>  <body>    <div id="container">       <div id="splash" class="pag...
Transitions   div#splash         div#content                -webkit-transform:                  translate3d(100%, 0, 0);
Transitions        div#splash                  div#content-webkit-transition:           -webkit-transform:     all 600ms e...
Transitions      Work OfflineTouch Events     SizingHardware (GPS)   Distribution
Touch EventsHow touch events work
Touch Events               Touch   Multi-Touch   GestureiOS             P         P          PAndroid 2.3     PAndroid...
Touch Events               Touch   Multi-Touch   GestureiOS             P         P          PAndroid 2.3     PAndroid...
Touch Events       touchstart      touchmove       touchend      touchcancel
Touch Eventsslides.bind(touchstart, function (e) {  if (touchId !== null) {    e.stopPropagation();    e.preventDefault();...
Touch Eventsslides.bind(touchstart, function (e) {  if (touchId !== null) {    e.stopPropagation();    e.preventDefault();...
Touch Eventsslides.bind(touchstart, function (e) {  if (touchId !== null) {    e.stopPropagation();    e.preventDefault();...
Touch Eventsslides.bind(touchstart, function (e) {  if (touchId !== null) {    e.stopPropagation();    e.preventDefault();...
Touch Eventsslides.bind(touchmove, function (e) {  var touch = e.originalEvent.touches[0];  if (touch.identifier == touchI...
Touch Eventsslides.bind(touchend, function (e) {  var touch = e.originalEvent.changedTouches[0];  if (touch.identifier == ...
Touch Eventsfunction touchEnd(x) {  ...  if (Math.abs(deltaX) > threshold) {    swipe(direction);  } else if (Math.abs(del...
Touch Eventsfunction touchEnd(x) {  ...  if (Math.abs(deltaX) > threshold) {    swipe(direction);  } else if (Math.abs(del...
Touch Eventsfunction touchEnd(x) {  ...  if (Math.abs(deltaX) > threshold) {    swipe(direction);  } else if (Math.abs(del...
Touch Eventsfunction touchEnd(x) {  ...  if (Math.abs(deltaX) > threshold) {    swipe(direction);  } else if (Math.abs(del...
Touch Eventsfunction touchEnd(x) {  ...  if (Math.abs(deltaX) > threshold) {    swipe(direction);  } else if (Math.abs(del...
Touch Events<div id="content" class="page">  <div id="map"></div>  <div id="photos">    <div id="slides-container">       ...
Touch Events
Touch Events            div#slides-containerdiv.slide       div.slide          div.slide
Touch Events            div#slides-containerdiv.slide       div.slide          div.slide
Touch EventsGotcha #1: Android browser
Touch EventsGotcha #1: Android browser
Touch EventsGotcha #1: Android browser  <img> vs. background-image
Touch EventsGotcha #2: Mobile Safari Bounce  $(document).bind(touchmove’, function(event){    event.preventDefault();  });
Touch EventsGotcha #3: Testing on Desktop  var   hasTouch = ontouchstart in document.documentElement;  var   touchstart = ...
Touch EventsGotcha #4: Click Events are Slow  Click Event vs. Touch Event
Transitions      Work OfflineTouch Events     SizingHardware (GPS)   Distribution
Hardware (GPS)                JS - iOS   JS - Android   PhoneGapAccelerometer      P                        PCompass    ...
Hardware (GPS)                JS - iOS   JS - Android   PhoneGapAccelerometer      P                        PCompass    ...
Hardware (GPS)
Hardware (GPS)function displayCurrentLocation() {    if (navigator.geolocation) {      navigator.geolocation.getCurrentPos...
Transitions      Work OfflineTouch Events     SizingHardware (GPS)   Distribution
Work Offline     Local Storage
Work Offline      Gotcha: 5mb
Work Offlinevar hasLocalStorage = (function () {  try {    localStorage.setItem(test, test);    localStorage.removeItem(te...
Work Offlinevar localStoragePhotoData = null;if (hasLocalStorage) {  localStoragePhotoData = localStorage.getItem(key);}if...
Work Offlinevar localStoragePhotoData = null;if (hasLocalStorage) {  localStoragePhotoData = localStorage.getItem(key);}if...
Work Offlinevar localStoragePhotoData = null;if (hasLocalStorage) {  localStoragePhotoData = localStorage.getItem(key);}if...
Work Offlinevar localStoragePhotoData = null;if (hasLocalStorage) {  localStoragePhotoData = localStorage.getItem(key);}if...
Transitions      Work OfflineTouch Events     SizingHardware (GPS)   Distribution
Sizing   Support different screens       Handle rotation    Hide browser chrome        Disable zoomDisable other browser f...
SizingSupport different screens  Easier in HTML than native  % based layouts  background-size: cover;  Absolute positionin...
SizingHandle rotation  $(window).resize(doSomething);
SizingHandle rotation  $(window).resize(reposition);
SizingHandle rotation  Gotcha: You get notification after rotation is  complete
SizingHide browser chrome  <meta name="apple-mobile-web-app-capable"     content="yes" />
SizingDisable zoom  <meta name="viewport" content="user-scalable=no,     initial-scale=1.0, maximum-scale=1.0" />
SizingDisable other browser features  /* turn off default press states */  -webkit-tap-highlight-color: transparent;  /* d...
Transitions      Work OfflineTouch Events     SizingHardware (GPS)   Distribution
Distribution
DistributionTwo ways to distribute:1. Web only     Reminder to bookmark2. App gallery     Custom native wrapper     Framew...
DistributionTwo ways to distribute:1. Web only     Reminder to bookmark2. App gallery     Custom native wrapper     Framew...
Distributionhttps://github.com/cubiq/   add-to-homescreen
Distribution
Distribution<link rel="stylesheet" href="styles/   add2home.css"><script type="application/javascript"   src="scripts/exte...
Distribution   Home Screen Icon
Distribution               144 x 144
Distribution<link rel="apple-touch-icon" href="icon.png"/>
Transitions      Work OfflineTouch Events     SizingHardware (GPS)   Distribution
Go explore.
Thanks!@skirchmeier@mikebollingerMINNEWEBCON 2012
APP:http://livefront.com/explore    SOURCE CODE:http://github.com/livefront/minnewebcon2012-explore          SLIDES:http:/...
Building HTML5 Tablet Apps for iOS and Android
Building HTML5 Tablet Apps for iOS and Android
Upcoming SlideShare
Loading in...5
×

Building HTML5 Tablet Apps for iOS and Android

64,454

Published on

Published in: Technology, Design
5 Comments
116 Likes
Statistics
Notes
No Downloads
Views
Total Views
64,454
On Slideshare
0
From Embeds
0
Number of Embeds
19
Actions
Shares
0
Downloads
0
Comments
5
Likes
116
Embeds 0
No embeds

No notes for slide

Transcript of "Building HTML5 Tablet Apps for iOS and Android"

  1. 1. Building HTML5Tablet Appsfor iOS and Android@skirchmeier@mikebollingerMINNEWEBCON 2012
  2. 2. The elephant in the room:!HTML5 apps kinda suck.
  3. 3. Native vs. HTML5
  4. 4. Native HTML5-- Expensive to develop + Cheaper to develop-- One platform at a time + All platforms at a time+ Snappy -- Slower+ Refined (true native feel) -- Unrefined+ Full featured -- Limited features
  5. 5. Reasons
  6. 6. 1Money
  7. 7. 2Time
  8. 8. 3Audience
  9. 9. 4Control
  10. 10. 1 2 3 4Money Time Audience Control
  11. 11. 5 T A B L E T S   H A V E   F L E X I B L E  Design Patterns
  12. 12. 1 2 3 4Money Time Audience Control 5 Flexible Design Patterns
  13. 13. Change the game.
  14. 14. Examples
  15. 15. Best Buyhttp://bby-uv.appspot.com/ Financial Times http://app.ft.com Proof http://proofwhisky.com
  16. 16. Best Buy http://bby-uv.appspot.com/iOS Only >! Financial Times http://app.ft.comiOS Only >! Proof http://proofwhisky.com
  17. 17. Frameworks
  18. 18. jQuery Mobile jQTouch Sencha Touch
  19. 19. Native Wrappers
  20. 20. PhoneGap Titanium
  21. 21. Our Custom App
  22. 22. http://livefront.com/explore
  23. 23. How do we build this?
  24. 24. Step 1:Travel the world
  25. 25. Step 2:HTML (sorry)
  26. 26. <!DOCTYPE html><html> <head> ... </head> <body> <div id="container"> <div id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body></html>
  27. 27. <!DOCTYPE html><html> <head> ... </head> <body> <div id="container"> <div id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body></html>
  28. 28. <!DOCTYPE html><html> <head> ... </head> <body> <div id="container"> <div id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body></html>
  29. 29. <!DOCTYPE html><html> <head> ... </head> <body> <div id="container"> <div id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body></html>
  30. 30. <!DOCTYPE html><html> <head> ... </head> <body> <div id="container"> <div id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body></html>
  31. 31. <!DOCTYPE html><html> <head> ... </head> <body> <div id="container"> <div id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body></html>
  32. 32. <!DOCTYPE html><html> <head> ... </head> <body> <div id="container"> <div id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body></html>
  33. 33. <div id="content" class="page"> <div id="map"></div> <div id="photos"> <div id="slides-container"> <div class="slide"></div> <div class="slide"></div> <div class="slide"></div> </div> <section id="slide-info"> <h1></h1> <div id="slide-details"> <p></p> <p class="location"></p> </div> </section> </div></div>
  34. 34. <div id="content" class="page"> <div id="map"></div> <div id="photos"> <div id="slides-container"> <div class="slide"></div> <div class="slide"></div> <div class="slide"></div> </div> <section id="slide-info"> <h1></h1> <div id="slide-details"> <p></p> <p class="location"></p> </div> </section> </div></div>
  35. 35. <div id="content" class="page"> <div id="map"></div> <div id="photos"> <div id="slides-container"> <div class="slide"></div> <div class="slide"></div> <div class="slide"></div> </div> <section id="slide-info"> <h1></h1> <div id="slide-details"> <p></p> <p class="location"></p> </div> </section> </div></div>
  36. 36. <div id="content" class="page"> <div id="map"></div> <div id="photos"> <div id="slides-container"> <div class="slide"></div> <div class="slide"></div> <div class="slide"></div> </div> <section id="slide-info"> <h1></h1> <div id="slide-details"> <p></p> <p class="location"></p> </div> </section> </div></div>
  37. 37. <!DOCTYPE html><html> <head> ... </head> <body> <div id="container"> <div id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body></html>
  38. 38. Techniques
  39. 39. Transitions Work OfflineTouch Events SizingHardware (GPS) Distribution
  40. 40. Transitions Work OfflineTouch Events SizingHardware (GPS) Distribution
  41. 41. Transitions Two ways to animate: 1. jQuery (.animate) 2. CSS (-webkit-transform)  
  42. 42. Transitions Two ways to animate: 1. jQuery (.animate) 2. CSS (-webkit-transform)  
  43. 43. TransitionsWhat can we do with CSS?
  44. 44. Transitions -webkit-transform translate scale skew rotate -webkit-animation
  45. 45. Transitions -webkit-transform translate scale skew rotate -webkit-animation
  46. 46. Transitions<!DOCTYPE html><html> <head> ... </head> <body> <div id="container"> <div id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body></html>
  47. 47. Transitions div#splash div#content -webkit-transform: translate3d(100%, 0, 0);
  48. 48. Transitions div#splash div#content-webkit-transition: -webkit-transform: all 600ms ease-in-out; translate3d(100%, 0, 0);-webkit-transform: translate3d(0, 0, 0);  
  49. 49. Transitions Work OfflineTouch Events SizingHardware (GPS) Distribution
  50. 50. Touch EventsHow touch events work
  51. 51. Touch Events Touch Multi-Touch GestureiOS P P PAndroid 2.3 PAndroid 3 P PAndroid 4 P P
  52. 52. Touch Events Touch Multi-Touch GestureiOS P P PAndroid 2.3 PAndroid 3 P PAndroid 4 P P
  53. 53. Touch Events touchstart touchmove touchend touchcancel
  54. 54. Touch Eventsslides.bind(touchstart, function (e) { if (touchId !== null) { e.stopPropagation(); e.preventDefault(); } else { var touch = e.originalEvent.touches[0]; touchId = touch.identifier; touchStart(touch.clientX); }});
  55. 55. Touch Eventsslides.bind(touchstart, function (e) { if (touchId !== null) { e.stopPropagation(); e.preventDefault(); } else { var touch = e.originalEvent.touches[0]; touchId = touch.identifier; touchStart(touch.clientX); }});
  56. 56. Touch Eventsslides.bind(touchstart, function (e) { if (touchId !== null) { e.stopPropagation(); e.preventDefault(); } else { var touch = e.originalEvent.touches[0]; touchId = touch.identifier; touchStart(touch.clientX); }});
  57. 57. Touch Eventsslides.bind(touchstart, function (e) { if (touchId !== null) { e.stopPropagation(); e.preventDefault(); } else { var touch = e.originalEvent.touches[0]; touchId = touch.identifier; touchStart(touch.clientX); }});
  58. 58. Touch Eventsslides.bind(touchmove, function (e) { var touch = e.originalEvent.touches[0]; if (touch.identifier == touchId) { touchMove(touch.clientX); }});
  59. 59. Touch Eventsslides.bind(touchend, function (e) { var touch = e.originalEvent.changedTouches[0]; if (touch.identifier == touchId) { touchEnd(touch.clientX); }});
  60. 60. Touch Eventsfunction touchEnd(x) { ... if (Math.abs(deltaX) > threshold) { swipe(direction); } else if (Math.abs(deltaX) >Config.slideshowSwipeDistanceThreshold &&touchDuration <Config.slideshowSwipeDurationThreshold) { swipe(direction); } else { snapBack(); }}
  61. 61. Touch Eventsfunction touchEnd(x) { ... if (Math.abs(deltaX) > threshold) { swipe(direction); } else if (Math.abs(deltaX) >Config.slideshowSwipeDistanceThreshold &&touchDuration <Config.slideshowSwipeDurationThreshold) { swipe(direction); } else { snapBack(); }}
  62. 62. Touch Eventsfunction touchEnd(x) { ... if (Math.abs(deltaX) > threshold) { swipe(direction); } else if (Math.abs(deltaX) >Config.slideshowSwipeDistanceThreshold &&touchDuration <Config.slideshowSwipeDurationThreshold) { swipe(direction); } else { snapBack(); }}
  63. 63. Touch Eventsfunction touchEnd(x) { ... if (Math.abs(deltaX) > threshold) { swipe(direction); } else if (Math.abs(deltaX) >Config.slideshowSwipeDistanceThreshold &&touchDuration <Config.slideshowSwipeDurationThreshold) { swipe(direction); } else { snapBack(); }}
  64. 64. Touch Eventsfunction touchEnd(x) { ... if (Math.abs(deltaX) > threshold) { swipe(direction); } else if (Math.abs(deltaX) >Config.slideshowSwipeDistanceThreshold &&touchDuration <Config.slideshowSwipeDurationThreshold) { swipe(direction); } else { snapBack(); }}
  65. 65. Touch Events<div id="content" class="page"> <div id="map"></div> <div id="photos"> <div id="slides-container"> <div class="slide"></div> <div class="slide"></div> <div class="slide"></div> </div> <section id="slide-info"> <h1></h1> <div id="slide-details"> <p></p> <p class="location"></p> </div> </section> </div></div>
  66. 66. Touch Events
  67. 67. Touch Events div#slides-containerdiv.slide div.slide div.slide
  68. 68. Touch Events div#slides-containerdiv.slide div.slide div.slide
  69. 69. Touch EventsGotcha #1: Android browser
  70. 70. Touch EventsGotcha #1: Android browser
  71. 71. Touch EventsGotcha #1: Android browser <img> vs. background-image
  72. 72. Touch EventsGotcha #2: Mobile Safari Bounce $(document).bind(touchmove’, function(event){ event.preventDefault(); });
  73. 73. Touch EventsGotcha #3: Testing on Desktop var hasTouch = ontouchstart in document.documentElement; var touchstart = hasTouch ? touchstart : mousedown; var touchmove = hasTouch ? touchmove : mousemove; var touchend = hasTouch ? touchend : mouseup;
  74. 74. Touch EventsGotcha #4: Click Events are Slow Click Event vs. Touch Event
  75. 75. Transitions Work OfflineTouch Events SizingHardware (GPS) Distribution
  76. 76. Hardware (GPS) JS - iOS JS - Android PhoneGapAccelerometer P PCompass P PGyroscope P PCamera PContacts PLocal Storage P P PGeolocation P P PNotifications P
  77. 77. Hardware (GPS) JS - iOS JS - Android PhoneGapAccelerometer P PCompass P PGyroscope P PCamera PContacts PLocal Storage P P PGeolocation P P PNotifications P
  78. 78. Hardware (GPS)
  79. 79. Hardware (GPS)function displayCurrentLocation() { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( function (position) { var lat = position.coords.latitude; var lng = position.coords.longitude; ... // Display lat/lng } ); } };
  80. 80. Transitions Work OfflineTouch Events SizingHardware (GPS) Distribution
  81. 81. Work Offline Local Storage
  82. 82. Work Offline Gotcha: 5mb
  83. 83. Work Offlinevar hasLocalStorage = (function () { try { localStorage.setItem(test, test); localStorage.removeItem(test); return true; } catch(e) { return false; }})();
  84. 84. Work Offlinevar localStoragePhotoData = null;if (hasLocalStorage) { localStoragePhotoData = localStorage.getItem(key);}if (localStoragePhotoData === null) { if (hasLocalStorage) { // Save photo data to localStorage. var photoDataString = JSON.stringify(PhotoData); localStorage.setItem(key); ... }} else { // Use photo data cached in localStorage. var photoDataObject = JSON.parse(localStoragePhotoData); ...}
  85. 85. Work Offlinevar localStoragePhotoData = null;if (hasLocalStorage) { localStoragePhotoData = localStorage.getItem(key);}if (localStoragePhotoData === null) { if (hasLocalStorage) { // Save photo data to localStorage. var photoDataString = JSON.stringify(PhotoData); localStorage.setItem(key); ... }} else { // Use photo data cached in localStorage. var photoDataObject = JSON.parse(localStoragePhotoData); ...}
  86. 86. Work Offlinevar localStoragePhotoData = null;if (hasLocalStorage) { localStoragePhotoData = localStorage.getItem(key);}if (localStoragePhotoData === null) { if (hasLocalStorage) { // Save photo data to localStorage. var photoDataString = JSON.stringify(PhotoData); localStorage.setItem(key); ... }} else { // Use photo data cached in localStorage. var photoDataObject = JSON.parse(localStoragePhotoData); ...}
  87. 87. Work Offlinevar localStoragePhotoData = null;if (hasLocalStorage) { localStoragePhotoData = localStorage.getItem(key);}if (localStoragePhotoData === null) { if (hasLocalStorage) { // Save photo data to localStorage. var photoDataString = JSON.stringify(PhotoData); localStorage.setItem(key); ... }} else { // Use photo data cached in localStorage. var photoDataObject = JSON.parse(localStoragePhotoData); ...}
  88. 88. Transitions Work OfflineTouch Events SizingHardware (GPS) Distribution
  89. 89. Sizing Support different screens Handle rotation Hide browser chrome Disable zoomDisable other browser features
  90. 90. SizingSupport different screens Easier in HTML than native % based layouts background-size: cover; Absolute positioning is often necessary
  91. 91. SizingHandle rotation $(window).resize(doSomething);
  92. 92. SizingHandle rotation $(window).resize(reposition);
  93. 93. SizingHandle rotation Gotcha: You get notification after rotation is complete
  94. 94. SizingHide browser chrome <meta name="apple-mobile-web-app-capable" content="yes" />
  95. 95. SizingDisable zoom <meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0" />
  96. 96. SizingDisable other browser features /* turn off default press states */ -webkit-tap-highlight-color: transparent; /* disable save image on long press */ -webkit-touch-callout: none; /* disable selection copy/paste */ -webkit-user-select: none; /* disable dragging images */ -webkit-user-drag: none;
  97. 97. Transitions Work OfflineTouch Events SizingHardware (GPS) Distribution
  98. 98. Distribution
  99. 99. DistributionTwo ways to distribute:1. Web only Reminder to bookmark2. App gallery Custom native wrapper Framework like PhoneGap  
  100. 100. DistributionTwo ways to distribute:1. Web only Reminder to bookmark2. App gallery Custom native wrapper Framework like PhoneGap  
  101. 101. Distributionhttps://github.com/cubiq/ add-to-homescreen
  102. 102. Distribution
  103. 103. Distribution<link rel="stylesheet" href="styles/ add2home.css"><script type="application/javascript" src="scripts/external/add2home.js"></script>
  104. 104. Distribution Home Screen Icon
  105. 105. Distribution 144 x 144
  106. 106. Distribution<link rel="apple-touch-icon" href="icon.png"/>
  107. 107. Transitions Work OfflineTouch Events SizingHardware (GPS) Distribution
  108. 108. Go explore.
  109. 109. Thanks!@skirchmeier@mikebollingerMINNEWEBCON 2012
  110. 110. APP:http://livefront.com/explore SOURCE CODE:http://github.com/livefront/minnewebcon2012-explore SLIDES:http://slideshare.net/livefront

×