Saturday, September 8, 2012

Simple Web-App for Android and iOS

So as of late, I have been working on websites which function on mobile devices. Its often easier to make code changes and updates when creating applications out of html,css and javascript. If you want a really easy jumping off point, I have created a super simple little web app which will play nicely on Android and iOS devices including tablets.

To create this package I use the following libraries:
-jQuery - to abbreviate javascript
-iScroll - to help with cross device scrolling
-less.js - cause its a nifty way of styling with css
-touchSwipe - help with swipes

This web app (at the present time) is one screen with scroll-able content and swipe zones, along with scroll-able and inner scroll-able zones. I wrote some javascript to tie the libraries all together and create a nice and responsive application.

At the bottom of this page I'll have a link to the files, He're is a brief over view of the html, js and css/less.


HTML Template:


<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
    <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
    <link rel="apple-touch-icon" href="theme/touch-icon-iphone.png" />
    <link rel="apple-touch-icon" sizes="72x72" href="theme/touch-icon-ipad.png" />
    <link rel="apple-touch-icon" sizes="114x114" href="theme/touch-icon-iphone4.png" />
 
    <link rel="apple-touch-startup-image" href="theme/startup.jpg" media="screen and (min-device-width: 200px) and (max-device-width: 480) and (orientation:portrait)"> <!-- iPod/Phone 320 x 460 image -->
    <link rel="apple-touch-startup-image" href="theme/startup-iPad-portrait.jpg" media="(device-width: 768px) and (orientation: portrait)" /> <!--iPad Portrait 768 x 1004 -->
    <link rel="apple-touch-startup-image" href="theme/startup-iPad-landscape.jpg" media="(device-width: 768px) and (orientation: landscape)" /> <!--iPad LandScape 1024 x 748-->
 
 
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="default" />
 
    <meta name="HandheldFriendly" content="true" />
<title>Cross Device Example</title>
<link rel="stylesheet/less" type="text/css" href="css/styles.less" />
<link rel="stylesheet" href="css/style.css" />
<link rel="stylesheet" href="css/iscroll.css" />
<script type="text/javascript" src="js/jquery-1.8.0.min.js"></script>
    <script type="text/javascript" src="js/jquery.touchwipe.min.js"></script>
<script type="text/javascript" src="js/iscroll.js"></script>
<script type="text/javascript" src="js/less-1.3.0.min.js"></script>
<script type="text/javascript" src="js/brenFunctions.js"></script>
</head>
<body>
<div id="wrap">
<div id="main" class="clearfix"> <div id="header">
<h1>Bren's App</h1>
</div><!--End Header-->
<div id="displayRegion">
<div id="displayContent">
<div id="swipeRegionLeft">
<div id="leftContentScroller" class="iScrollWrapper">
<div class="iScrollScroller">
<div style="padding:5px 10px;">
<ul>
<li><a href="#">Option 1</a></li>
<li><a href="#">Option 2</a></li>
<li><a href="#">Option 3</a></li>
<li><a href="#">Option 4</a></li>
</ul>
<ul>
<li><a href="#">Option 1</a></li>
<li><a href="#">Option 2</a></li>
<li><a href="#">Option 3</a></li>
<li><a href="#">Option 4</a></li>
</ul>
</div>
</div>
</div>
</div>
<div id="iScrollWrapper" class="iScrollWrapper">
<div class="iScrollScroller">
<div style="padding:15px 30px;">
<!-- nested! -->
<div class="smallContentBlockRight">
<div id="wrapperNestedRight" class="iScrollWrapper inneriScroll">
<div  class="iScrollScroller">
<div style="padding:5px 10px;">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla nisi nulla, molestie nec vestibulum et, molestie eget lectus. Nam imperdiet gravida sapien eu tincidunt. Curabitur pellentesque nibh orci. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi rhoncus nunc vel massa pharetra rutrum. Donec pellentesque augue eget tortor pellentesque consequat. Nam molestie consectetur metus, non tristique neque pretium venenatis. Mauris id massa eu ligula sagittis facilisis consectetur et augue. Cras ullamcorper blandit massa, quis vestibulum felis ornare et. Sed quis lorem a mauris tristique ullamcorper. Etiam ultricies gravida dolor, non imperdiet ligula dapibus nec. Nulla lacinia bibendum massa eget porttitor.</p>
<p>Vivamus eros massa, varius sit amet venenatis at, malesuada sit amet quam. Praesent massa nunc, suscipit nec ullamcorper a, ullamcorper et nibh. Morbi vel sapien quis turpis mattis volutpat et vitae velit. Suspendisse imperdiet nisi ac augue ullamcorper vehicula. Morbi vel diam eget risus faucibus pharetra. Phasellus posuere mauris ut diam tristique id aliquet lacus condimentum. Fusce pharetra tempus felis, in sollicitudin velit ullamcorper sed.</p>
</div>
</div>
</div>
</div>
<h1>Chapter Bren</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla nisi nulla, molestie nec vestibulum et, molestie eget lectus. Nam imperdiet gravida sapien eu tincidunt. Curabitur pellentesque nibh orci. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi rhoncus nunc vel massa pharetra rutrum. Donec pellentesque augue eget tortor pellentesque consequat. Nam molestie consectetur metus, non tristique neque pretium venenatis. Mauris id massa eu ligula sagittis facilisis consectetur et augue. Cras ullamcorper blandit massa, quis vestibulum felis ornare et. Sed quis lorem a mauris tristique ullamcorper. Etiam ultricies gravida dolor, non imperdiet ligula dapibus nec. Nulla lacinia bibendum massa eget porttitor.</p>
...
                                    <div class="smallContentBlockLeft">
<div id="wrapperNestedLeft" class="iScrollWrapper inneriScroll">
<div  class="iScrollScroller">
<div style="padding:5px 10px;">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla nisi nulla, molestie nec vestibulum et, molestie eget lectus. Nam imperdiet gravida sapien eu tincidunt. Curabitur pellentesque nibh orci. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi rhoncus nunc vel massa pharetra rutrum. Donec pellentesque augue eget tortor pellentesque consequat. Nam molestie consectetur metus, non tristique neque pretium venenatis. Mauris id massa eu ligula sagittis facilisis consectetur et augue. Cras ullamcorper blandit massa, quis vestibulum felis ornare et. Sed quis lorem a mauris tristique ullamcorper. Etiam ultricies gravida dolor, non imperdiet ligula dapibus nec. Nulla lacinia bibendum massa eget porttitor.</p>
<p>Vivamus eros massa, varius sit amet venenatis at, malesuada sit amet quam. Praesent massa nunc, suscipit nec ullamcorper a, ullamcorper et nibh. Morbi vel sapien quis turpis mattis volutpat et vitae velit. Suspendisse imperdiet nisi ac augue ullamcorper vehicula. Morbi vel diam eget risus faucibus pharetra. Phasellus posuere mauris ut diam tristique id aliquet lacus condimentum. Fusce pharetra tempus felis, in sollicitudin velit ullamcorper sed.</p>
</div>
</div>
</div>
</div>
                                 
.... <p>Proin auctor feugiat quam at tempor. Duis est nulla, interdum porta laoreet non, faucibus vitae sem. Suspendisse in semper lorem. Ut ante arcu, varius ac rutrum in, faucibus in mauris. Etiam condimentum diam eu nisi porttitor a varius mauris eleifend. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Duis vel quam odio, sit amet facilisis nulla. Nullam faucibus ipsum eget leo mattis non volutpat odio vehicula. Mauris vestibulum, enim vel eleifend aliquam, est neque commodo nibh, nec dignissim arcu dolor a mi. Integer non odio quis ipsum pulvinar venenatis. Cras ac nibh at tellus ornare consequat. Etiam cursus libero vitae ante venenatis non auctor libero gravida.</p>
</div> <!--pdding -->
</div> <!--iScrollScroller-->
</div> <!--iScrollWrapper-->

</div><!--End Display Content -->
</div> <!--End DisplayRegion -->
</div>
</div>
<div id="footer">
</div><!--End Footer-->
</body>
</html>



style.css 



* { margin:0; padding:0; }
html, body, #wrap { height: 100%; }
body > #wrap {height: auto; min-height: 100%;}
body,ul,li {
padding:0;
margin:0;
border:0;
}
body {
font-size:12px;
-webkit-user-select:none;
    -webkit-text-size-adjust:none;
display: none;

overflow: hidden;
height: 100%;
width: 100%;
background-color: #ddd; /* Background color */
    color: #222;            /* Foreground color used for text */
    font-family: Helvetica;
    font-size: 14px;

-moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box;



Style.less





@headerHeight: 43px;
@footerHeight: 43px;
@borderlistRadius: 10px;


.boxModel{ -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }


#main { padding-bottom: @footerHeight;  .boxModel; }  /* must be same height as the footer */

#displayRegion{
position: relative;
height: 100%;
width: 100%;
display:block;
overflow: hidden;

.boxModel;
}

#displayContent{
position: relative;
background-color: #fff;
display:block;
clear: both;
overflow: hidden;
width: 100%;
height: 100%;
.boxModel;
}

#swipeRegionLeft{
max-width: 100%;
width: 0%;
display: none;
position: absolute;
z-index: 1000;
height: 100%;
top: 0px;
left: 0px;
overflow: hidden;
border-right: 1px solid #000;

background-image: url('../images/pinstripes.png');
background-repeat: repeat;
background-color: #C5CCD4;

.boxModel;
}



#header{
position: relative;
clear: both;
height: @headerHeight;
width: 100%;
display:block;
}

#header h1{
width: 100%;
color: #fff;
line-height:  @headerHeight;
font-size: @headerHeight - 18px;


font-weight: normak;
margin: 0px;
height:  @headerHeight;
text-align: center;
text-decoration: none;
display:inline-block;
}


#leftContentScroller ul li{
margin: 0px;
padding: 0px;
min-height: 42px;
}

.border-radius-top (@radius) {
-webkit-border-top-left-radius: @radius;
-webkit-border-top-right-radius: @radius;
-moz-border-radius-topleft: @radius;
-moz-border-radius-topright: @radius;
border-top-left-radius: @radius;
border-top-right-radius: @radius;
}

.border-radius-bottom (@radius) {
-webkit-border-bottom-right-radius: @radius;
-webkit-border-bottom-left-radius: @radius;
-moz-border-radius-bottomright: @radius;
-moz-border-radius-bottomleft: @radius;
border-bottom-right-radius: @radius;
border-bottom-left-radius: @radius;
}

#swipeRegionLeft  ul { margin:5px 0 15px 0; padding:0; list-style:none; -webkit-border-radius:0; border-width:0; background-color:transparent; }

#swipeRegionLeft  ul > li { margin:0; padding:0; border-width:0; }

#swipeRegionLeft  ul > li a { text-align:left; padding:11px 0 9px 10px; background:url('../images/listArrow.png') center right no-repeat; 
display:block; text-decoration:none; overflow:hidden; text-overflow:ellipsis; color:#000; font-weight:bold; font-size:17px; 
 margin:0; border:1px solid #aaa; border-bottom-width:0 !important; margin-top:-1px; background-color:#fff; }

#swipeRegionLeft ul > li:first-child a { .border-radius-top (10px); }
#swipeRegionLeft  ul > li:last-child a { .border-radius-bottom (10px); border-bottom-width:1px !important; }


p{
margin: 0 8px;
font-size: 15px;
line-height: 20px;
font-family: Helvetica, arial;
}





#footer { 
position: relative;
margin-top: -@footerHeight; /* negative value of footer height */
height: @footerHeight;
clear:both;


/* CLEAR FIX*/
.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}

.clearfix {
display: inline-block;
}

/* Hides from IE-mac \*/
* html .clearfix { 
height: 1%;
}
.clearfix {
display: block;
}
/* End hide from IE-mac */


.gradient{
background: #4c4c4c; /* Old browsers */
background: -moz-linear-gradient(top,  #4c4c4c 0%, #595959 12%, #666666 25%, #474747 39%, #2c2c2c 50%, #000000 51%, #111111 60%, #2b2b2b 76%, #1c1c1c 91%, #131313 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(12%,#595959), color-stop(25%,#666666), color-stop(39%,#474747), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(60%,#111111), color-stop(76%,#2b2b2b), color-stop(91%,#1c1c1c), color-stop(100%,#131313)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top,  #4c4c4c 0%,#595959 12%,#666666 25%,#474747 39%,#2c2c2c 50%,#000000 51%,#111111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top,  #4c4c4c 0%,#595959 12%,#666666 25%,#474747 39%,#2c2c2c 50%,#000000 51%,#111111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top,  #4c4c4c 0%,#595959 12%,#666666 25%,#474747 39%,#2c2c2c 50%,#000000 51%,#111111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%); /* IE10+ */
background: linear-gradient(to bottom,  #4c4c4c 0%,#595959 12%,#666666 25%,#474747 39%,#2c2c2c 50%,#000000 51%,#111111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%); /* W3C */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */
}

#footer,
#header{
.gradient;
.boxModel;

width: 100%;
display:block;
position: relative;
border: 1px solid #000;
}

.defaultSmallBlockContent{
position:relative; 
width: 40%; 
height: 200px; 
margin-top: 20px;
margin-bottom: 20px;
border: 1px solid #ccc; 
padding: 10px;
}



brenFunctions.js


var viewportWidth = 0;
var viewportHeight = 0;

var portrait = 0;
var landscape = 0;
var previousOrientation = 0;

var minMove = 50; //minimum swipe movement to trigger event

var minSize = 480;

var headerFooterHeight = 43; //header + footer height

var iScrolls = new Array(); //array of iscrolls
var scrollInit = 0; //flag for scroll bar initilization
var sideBarOpen = 0; //flag for if side bar is open

var firstRun = 1;
//function to refresh each iscroller on change
function refreshiScrolls(){
if( !firstRun ){ //flag for when body will be visible
for( var s = 0; s < iScrolls.length; s++){
iScrolls[s].refresh();
}
}
}

//function called after screen rotation, used for applying necessary classes and resetting sizes
function updateScreen(){
viewportWidth = $(window).width();
viewportHeight  = $(window).height();

if( viewportWidth < minSize ){
$('body').addClass('smallView');
}else{
$('body').removeClass('smallView');
}

if( window.orientation != undefined){
if( window.orientation == 0 || window.orientation == 180){
portrait = 1; landscape = 0;
$('body').addClass('portrait'); $('body').removeClass('landscape');
}else{ //90 || -90
portrait = 0; landscape = 1;
$('body').removeClass('portrait'); $('body').addClass('landscape');
}
}

$('#displayRegion').css({'width' : viewportWidth + 'px', 'height' : viewportHeight - (headerFooterHeight * 2) + 'px'});

console.log( 'width ' +  viewportWidth + 'px ' + 'height ' + viewportHeight + 'px');
console.log( 'landscape : ' + landscape + ' portrait : ' + portrait );

refreshiScrolls();
}

//function to open the 'options' side bar
function animateSideBar(){

var slideDuration = 50;

if( sideBarOpen ){

$('#swipeRegionLeft').css({'display' : 'none', 'width' : '0%'});

if( ! viewportWidth < minSize){
$('#iScrollWrapper').animate({
left: '0%',
width: '100%'
 }, slideDuration, function() {
// Animation complete.
refreshiScrolls();
 });
 }
 
 sideBarOpen = 0;
 
 $('#displayContent').removeClass('optionsOpen');
 
}else{

sideBarOpen = 1;
$('#displayContent').addClass('optionsOpen');

$('#swipeRegionLeft').css('display', 'block');

if( viewportWidth < minSize){
$('#swipeRegionLeft').animate({
width: '100%'
 }, slideDuration, function() {
// Animation complete.
refreshiScrolls();
 });
}else{
$('#swipeRegionLeft').animate({
width: '300px'
 }, slideDuration, function() {
// Animation complete.
refreshiScrolls();
 });
 

$('#iScrollWrapper').animate({
width:(viewportWidth - 300) + 'px',
left: '300px'
 }, slideDuration, function() {
refreshiScrolls();
 });
}
}
}

//just for triggering the rotation event
var checkOrientation = function(){
if(window.orientation !== previousOrientation){
previousOrientation = window.orientation;
updateScreen();
}
};

function loaded() 
{
$(function(){
$('.iScrollWrapper').each(function(index){ //get collection of iScrolls
if( $(this).hasClass('inneriScroll') ){
iScrolls[index] =  new iScroll( $(this).attr('id').toString() ,{
onBeforeScrollMove: function (e) { 
e.preventDefault(); 
e.stopPropagation();
}
});
}else{
iScrolls[index] = new iScroll(  $(this).attr('id').toString()  );
}

if( index = $('.iScrollWrapper').length -1){
scrollInit = 1;
}
});

updateScreen();

//Add Swipe Handlers for the various regions of the screen
$("#iScrollWrapper").touchwipe({
wipeLeft: function() { animateSideBar() },
wipeRight: function() { animateSideBar() },
min_move_x: minMove,
preventDefaultEvents: false
});

$('#swipeRegionLeft').touchwipe({
wipeLeft: function() { animateSideBar() },
min_move_x: minMove,
preventDefaultEvents: false
});

$("#footer").touchwipe({
wipeUp: function() { alert("down"); },
wipeDown: function() { alert("up"); },  
preventDefaultEvents: false
});

/*fix for internal links on iOS*/

$.expr[':'].external = function(obj){ return !obj.href.match(/^mailto\:/) && (obj.hostname != location.hostname) && (obj.hostname != ''); };

$('a').not('a:external').each(function(){ //grab all the local links
if( typeof $(this).attr('href') !== 'undefined' && $(this).attr('href') !== false){ //check that there is a href
if( $(this).attr('href') != "#"){ //check the href is not an anchor
$(this).addClass('internalAppLink');
}
}
});

/*Check if IOS*/
$('a.internalAppLink').click(function(event){
event.preventDefault();
window.location.href = $(this).attr('href');
});

if( scrollInit == 1 ){
$('body').css('display', 'block');
firstRun = 0;
refreshiScrolls(); //body must be displayed to work
}

});
}//end loaded

document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);
//Use this for high compatibility (iDevice + Android)
document.addEventListener('DOMContentLoaded', function () { setTimeout(loaded, 200); }, false);

//Use this for iDevice only
//document.addEventListener('DOMContentLoaded', loaded, false);

//Use this if nothing else works
//window.addEventListener('load', setTimeout(function () { loaded(); }, 200), false);

window.addEventListener("resize", updateScreen, false);
window.addEventListener("orientationchange", checkOrientation, false);
if(/Android/i.test(navigator.userAgent) ){ //Android Doesnt seem to always pick up on it, so force a check. This can affect battery performance only use if people experience issues
// setInterval(checkOrientation, 1000); 
}


*Note swipe right to trigger side menu


Here are the files. mobileApp v1.zip


Hope it helps someone - I'll extend on this further and post updates!

No comments:

Post a Comment