Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building HTML5 Tablet Apps

Building HTML5 Tablet Apps

Co-presented with Mike Bollinger at MinneWebCon 2012

Sam Kirchmeier

May 07, 2012
Tweet

More Decks by Sam Kirchmeier

Other Decks in Programming

Transcript

  1. Native -- Expensive to develop HTML5 + Cheaper to develop

    -- One platform at a time + Snappy + Refined (true native feel) + Full featured -- Limited features -- Unrefined -- Slower + All platforms at a time
  2. 5 Design Patterns T A B L E T S

      H A V E   F L E X I B L E  
  3. <!DOCTYPE html> <html> <head> ... </head> <body> <div id="container"> <div

    id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body> </html>
  4. <!DOCTYPE html> <html> <head> ... </head> <body> <div id="container"> <div

    id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body> </html>
  5. <!DOCTYPE html> <html> <head> ... </head> <body> <div id="container"> <div

    id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body> </html>
  6. <!DOCTYPE html> <html> <head> ... </head> <body> <div id="container"> <div

    id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body> </html>
  7. <!DOCTYPE html> <html> <head> ... </head> <body> <div id="container"> <div

    id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body> </html>
  8. <!DOCTYPE html> <html> <head> ... </head> <body> <div id="container"> <div

    id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body> </html>
  9. <!DOCTYPE html> <html> <head> ... </head> <body> <div id="container"> <div

    id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body> </html>
  10. <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>
  11. <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>
  12. <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>
  13. <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>
  14. <!DOCTYPE html> <html> <head> ... </head> <body> <div id="container"> <div

    id="splash" class="page"> ... </div> <div id="content" class="page"> ... </div> </div> </body> </html>
  15. 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>
  16. Touch Events Touch Multi-Touch Gesture iOS P P P Android

    2.3 P Android 3 P P Android 4 P P
  17. Touch Events Touch Multi-Touch Gesture iOS P P P Android

    2.3 P Android 3 P P Android 4 P P
  18. Touch Events slides.bind(touchstart, function (e) { if (touchId !== null)

    { e.stopPropagation(); e.preventDefault(); } else { var touch = e.originalEvent.touches[0]; touchId = touch.identifier; touchStart(touch.clientX); } });
  19. Touch Events slides.bind(touchstart, function (e) { if (touchId !== null)

    { e.stopPropagation(); e.preventDefault(); } else { var touch = e.originalEvent.touches[0]; touchId = touch.identifier; touchStart(touch.clientX); } });
  20. Touch Events slides.bind(touchstart, function (e) { if (touchId !== null)

    { e.stopPropagation(); e.preventDefault(); } else { var touch = e.originalEvent.touches[0]; touchId = touch.identifier; touchStart(touch.clientX); } });
  21. Touch Events slides.bind(touchstart, function (e) { if (touchId !== null)

    { e.stopPropagation(); e.preventDefault(); } else { var touch = e.originalEvent.touches[0]; touchId = touch.identifier; touchStart(touch.clientX); } });
  22. Touch Events slides.bind(touchmove, function (e) { var touch = e.originalEvent.touches[0];

    if (touch.identifier == touchId) { touchMove(touch.clientX); } });
  23. Touch Events slides.bind(touchend, function (e) { var touch = e.originalEvent.changedTouches[0];

    if (touch.identifier == touchId) { touchEnd(touch.clientX); } });
  24. Touch Events function touchEnd(x) { ... if (Math.abs(deltaX) > threshold)

    { swipe(direction); } else if (Math.abs(deltaX) > Config.slideshowSwipeDistanceThreshold && touchDuration < Config.slideshowSwipeDurationThreshold) { swipe(direction); } else { snapBack(); } }
  25. Touch Events function touchEnd(x) { ... if (Math.abs(deltaX) > threshold)

    { swipe(direction); } else if (Math.abs(deltaX) > Config.slideshowSwipeDistanceThreshold && touchDuration < Config.slideshowSwipeDurationThreshold) { swipe(direction); } else { snapBack(); } }
  26. Touch Events function touchEnd(x) { ... if (Math.abs(deltaX) > threshold)

    { swipe(direction); } else if (Math.abs(deltaX) > Config.slideshowSwipeDistanceThreshold && touchDuration < Config.slideshowSwipeDurationThreshold) { swipe(direction); } else { snapBack(); } }
  27. Touch Events function touchEnd(x) { ... if (Math.abs(deltaX) > threshold)

    { swipe(direction); } else if (Math.abs(deltaX) > Config.slideshowSwipeDistanceThreshold && touchDuration < Config.slideshowSwipeDurationThreshold) { swipe(direction); } else { snapBack(); } }
  28. Touch Events function touchEnd(x) { ... if (Math.abs(deltaX) > threshold)

    { swipe(direction); } else if (Math.abs(deltaX) > Config.slideshowSwipeDistanceThreshold && touchDuration < Config.slideshowSwipeDurationThreshold) { swipe(direction); } else { snapBack(); } }
  29. 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>
  30. Touch Events Gotcha #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';
  31. Hardware (GPS) JS - iOS JS - Android PhoneGap Accelerometer

    P P Compass P P Gyroscope P P Camera P Contacts P Local Storage P P P Geolocation P P P Notifications P
  32. Hardware (GPS) JS - iOS JS - Android PhoneGap Accelerometer

    P P Compass P P Gyroscope P P Camera P Contacts P Local Storage P P P Geolocation P P P Notifications P
  33. 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 } ); } };
  34. Work Offline var hasLocalStorage = (function () { try {

    localStorage.setItem('test', 'test'); localStorage.removeItem('test'); return true; } catch(e) { return false; } })();
  35. Work Offline var 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); ... }
  36. Work Offline var 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); ... }
  37. Work Offline var 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); ... }
  38. Work Offline var 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); ... }
  39. Sizing Support different screens Easier in HTML than native %

    based layouts background-size: cover; Absolute positioning is often necessary
  40. Sizing Disable 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;
  41. Distribution Two ways to distribute: 1. Web only Reminder to

    bookmark 2. App gallery Custom native wrapper Framework like PhoneGap  
  42. Distribution Two ways to distribute: 1. Web only Reminder to

    bookmark 2. App gallery Custom native wrapper Framework like PhoneGap