Click here to Skip to main content
Click here to Skip to main content

Swipe Clouds for JQuery Mobile Apps & PhoneGap

, 12 Aug 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
HTML5 Tag Canvas for JQuery Mobile, PhoneGap & Cordova

SwipeClouds

Summary

In an effort to create an interface that people might enjoy for my JQuery Mobile Apps I decided to try using html5 "Tag Canvas."  For this project I used as a starting point TagCanvas, a JQuery Plugin, by  Graham      Breach, which was the best Tag Canvas example I found. But It took some tweeking to eliminate jerky movements when swiping on mobile phones in PhoneGap builds but I finally got things working nicely.And I thought I would share it here.  I will be updating the source code in this article from time to time with new  features and demos.  You can download new and updated versions of the source code (free) from my website at  www.SwipeClouds.com.  To view a simulation of Swipe Clouds in the Chrome Web Browser (the simulation just works in Chrome) just Click Here.  www.SwipeClouds.com/demos/index.html.  

Introduction

Since in a mobile app you "swipe" the clouds to move them around I thought "Swipe Clouds" was      descriptive and an appropriate name. There are some basic considerations in making Swipe Clouds work correctly in mobile browsers like re-sizing the Cloud when necessary. To start we setup the canvas as follows: 

   <canvas id='tagcanvas' width='320' height='360' style='background-image:none; cursor:hand;'>
   <canvas>
You can immediately see that we need to do something to automatically adjust the size of this canvas dynamically. The only additional issue I ran into was that if you have a fixed footer you may want to adjust the screen size slightly in the landscape view. There are, of course, a number of ways of doing this in a JQuery Mobile App. In the sample in this article I adjust the size as follows:
    $(window).bind('resize', function(e){ 
      screenAdjust(); 
      $( "#wrapper" ).trigger( "updatelayout" ); 
   });     

   function screenAdjust() { 
      $('#wrapper').panel("close"); 
      var canvas = $("#tagcanvas"); 
      var c = canvas.get(0).getContext("2d"); 
      c.canvas.width = window.innerWidth; 
      var the_height = ($(window).innerHeight() 
                       - $(this).find('[data-role="header"]').height() 
                       - $(this).find('[data-role="footer"]').height() -0); 
      var orientation="portrait"; 
      if(window.orientation == -90 || 
         window.orientation == 90) orientation = "landscape"; 
      if (orientation == "portrait") { 
         the_height = ($(window).innerHeight() - 
         $(this).find('[data-role="header"]').height() 
         - $(this).find('[data-role="footer"]').height() -0); 
      } 
      else { 
          the_height = ($(window).innerHeight() - 
          $(this).find('[data-role="header"]').height() - 0); 
      } 
      c.canvas.height = the_height; 
   }

   $(document).ready(function (      
      screenAdjust(); 
      loadCloud('basicCloud', 'sphere', 1.0);
   });
       
What I found was the best way to deal with orientation changes from portrait to landscape in a JQuery Mobile App is to use the following approach:
   
   $(window).bind('orientationchange', function(e){ 
      var orientation="portrait"; 
      if(window.orientation == -90 || window.orientation == 90) 
         orientation = "landscape"; 
      switch(orientation){ case 'portrait': 
         //$.mobile.fixedToolbars.setTouchToggleEnabled(true); 
         break; 
      case 'landscape': 
         //$.mobile.fixedToolbars.setTouchToggleEnabled(false); break; default: 
         break; 
      }; 
   }); 
             

Mobile Swipe Cloud Options Like Shape & Lock

There are are a lot of option parameters like shape and lock in these clouds which we can dynamically change at any time. The "lock" parameter prevents the cloud from being moved in the direction specified.       For example, if we set lock='x' this prevents the cloud from being moved in the x direction. To change any options you just pass all the relevant options into the "tagcanvas" function using the array method:  

 
   function changeshape(s) { 
      $('#wrapper').panel("close"); 
      var locks = {hcylinder: 'x', vcylinder: 'y', hring: 'x', vring: 'y', sphere: ''}; 
      lock = locks[s] || ''; 
      TagCanvas.initial = (lock == 'x' && [0,0.2]) || (lock == 'y' && [0.2,0]) || [0.2,0.2]; 
      cloudShape = s; 
      // Simply in pass the options to change!
      TagCanvas.Start('tagcanvas', cloudID, { 
         <span class="emphasis">shape: s, 
         lock: lock 
      }); </span >
   }

I created a function, namely, loadCloud, that I call to make changes to an existing Swipe Cloud. This function is shown below: 
   
   function loadCloud(zCloud, zShape, zZoom) { 
      try { 
         cloudID = zCloud; 
         cloudShape = zShape; 
         cloudZoom = zZoom; 
         TagCanvas.shape = cloudShape; 
         TagCanvas.zoom = cloudZoom; 
         TagCanvas.shape = cloudShape; 
         TagCanvas.zoom = cloudZoom; 
         TagCanvas.weightMode = 'both'; 
         TagCanvas.weightFrom = 'data-weight'; 
         var gradient = { 
            0: '#f00',    // red 
            0.33: '#ff0', // yellow 
            0.66: '#0f0', // green 
            1: '#00f'     // blue 
         }; 
         TagCanvas.weightGradient = gradient; TagCanvas.weight = false; 
         if(cloudID == 'wordCloud') { TagCanvas.weight = true; } 
         var selectedColor = '#000';
         document.getElementById('tagcanvas').style.backgroundImage = 'none'; 
         document.getElementById('tagcanvas').style.backgroundRepeat='no-repeat'; 
         document.getElementById('tagcanvas').style.backgroundColor = '#000'; 
         $('#mainPage').css('background', '#000'); 

         oopts = { 
            textFont: 'Impact,Arial Black,sans-serif', 
            textColour: '#fff', 
            textHeight: 20, 
            minBrightness: 0.2, 
            maxSpeed: 0.07, 
            decel: 0.9, 
            depth: 0.99, 
            outlineColour: '#ff0000', 
            outlineThickness: 2, 
            pulsateTo: 0.6, 
            pulsateTime: 0.5, 
            wheelZoom: true, 
            zoomMin: 0.3, 
            zoomMax: 3, 
            zoomStep: 0.05, 
            frontSelect: true, 
            lock: null, 
            tooltip: 'div', 
            tooltipDelay: 100, 
            tooltipClass: 'sc_menu_tooltip', 
            radiusX: 1, 
            radiusY: 1, 
            radiusZ: 1, 
            stretchX: 1, 
            stretchY: 1, 
            offsetX: 0, 
            offsetY: 0, 
            shuffleTags: false, 
            noSelect: false, 
            noMouse: false, 
            imageScale: 1, 
            paused: false, 
            dragControl: true, 
            dragThreshold: 4, 
            initial: [0.1,-0.1], 
            reverse: true, 
            hideTags: false, 
            shadow: '#ccf', 
            shadowBlur: 3, 
            weight: false, 
            outlineMethod: "outline" 
         }; 

         $('#tagcanvas').tagcanvas(oopts, cloudID); 
         var amt = parseFloat(cloudZoom * 100); 
         $("#zoomslider").slider().val(amt.toFixed(0)); 
         var i, et = document.getElementById(cloudID).childNodes; for(i in et) { 
            et[i].nodeName == 'A' && et[i].addEventListener('click', function(e) { 
               e.preventDefault(); 
               var targetUrl = $(this).attr("role"); 
               if(cloudID != 'pumpkinCloud') { 
                  $("#datatest").html(targetUrl); 
                  $('#inlinecontent').simpledialog2( {'hide': {effect: 'fadeOut',
                    duration: 5000} } ); 
               } else { 
                  var ximage = 'img_pumpkins/' + targetUrl + '.png'; 
                 $('#popupPumpkin img').attr('src', ximage); 
                  $('#popupPumpkin').popup("open"); 
               } 
            }); 
         } 
        screenAdjust(); 
      } catch(e) { 
         document.getElementsByTagName('canvas')[0].style.border='0'; 
      } 
   }

Sliding Panel for User Controls

I wanted to give users a way access controls for manipulating properties of the Swipe Clouds and I decided on using a sliding panel to hold the user controls for changing properties of the Clouds. The panel code is shown below.    

   
   <div data-role="panel" id="wrapper" data-display="overlay" data-position="right" 
       class="ui-panel">
       <div id="scroller" style="padding-right:16px;padding-left:2px;">
          <li><a href="#" type="button" data-id="hcylinder">hcylinder</a></li          
          <li><a href="#" type="button" data-id="<span  id="ArticleContent19">vcylinder">vcylinder</a></li>
          <li><a href="#" type="button" data-id="<span  id="ArticleContent20">hring">hring x</a></li>
          <li><a href="#" type="button" data-id="<span  id="ArticleContent22">hring"><span id="ArticleContent21">hring </span>y</a></li>
         <li><a href="#" type="button" data-id="hringnolock">hring</a></li>
          <li><a href="#" type="button" data-id="vringnolock">vring</a></li>
          <li><a href="#" type="button" data-id="<span  id="ArticleContent23">sphere">sphere</a></li>
          <input type="range" name="zoomslider" id="zoomslider" 
          data-id="zoomslider" value="90" min="30" max="150" step="5">
<span id="ArticleContent3">          <li><a href="#" type="button" data-id="close" data-icon="delete">Close</a></li>
   </div></div>
 </span> </span ></span ></span ></span >

I decided to make the panel slide out from the right and have the ability to slide up and down so I could add as many controls as I wanted to add for users. In order to make the controls panel slide vertically in case we have a lot of controls or we are landscape view I added added iscroll.js to ensure smooth scrolling as follows:    

   var myScroll; 
   function loaded() { 
      myScroll = new iScroll('scroller', { scrollbarClass: 'myScrollbar', hScrollbar: 
      false, vScrollbar: true }); 
   }
   document.addEventListener('DOMContentLoaded', loaded, false);

The functions to change the shape and size of the clouds are as follows:   

   
<span id="ArticleContent4"><span id="ArticleContent5"> $(document).delegate('.ui-panel ul li > a', 'click', function(      
   var zid = $(this).data("id"); 
   if(zid == "close") { $('#wrapper').panel("close"); } 
      else { changeshape(zid); }
   });
</span></span>
   function changeshape(s) { 
      $('#wrapper').panel("close"); 
      var locks = {hcylinder: 'x', vcylinder: 'y', hring: 'x', vring: 'y', sphere: ''}; 
      lock = locks[s] || ''; 
      TagCanvas.initial = (lock == 'x' && [0,0.2]) || (lock == 'y' && [0.2,0]) || [0.2,0.2]; 
      cloudShape = s; 
      TagCanvas.Start('tagcanvas',cloudID, { shape: s, lock: lock });
   }

   $( "#zoomslider").on('slidestop', function( event ) { 
      var slider_value=$("#zoomslider").slider().val()/100; 
      loadCloud(cloudID, cloudShape, slider_value);
   });
Click Events in Swipe Clouds

Because the user is swiping the cloud it is likely that the user will accidentally click an images.  In order to handle accidental clicks on images or words in the swipe clouds I decided to popup a message asking the user if they want to go to the link. I used the following approach to handle these clicks.     

 
   var i, et = document.getElementById(ttags).childNodes; 
   for(i in et) { 
      et[i].nodeName == 'A' && et[i].addEventListener('click', function(e) { 
         e.preventDefault(); 
         var targetUrl = $(this).attr("role"); 
         if(ttags != 'pumpkinTags') { 
            $("#datatest").html(targetUrl); 
            $('#inlinecontent').simpledialog2( {'hide': {effect: 'fadeOut', 
            duration: 5000} } ); 
         } 
         else { 
            var ximage = 'img_pumpkins/' + targetUrl + '.png'; 
            $('#popupPumpkin img').attr('src', ximage); 
            $('#popupPumpkin').popup("open"); 
         } 
      }); 
   }

   // Handles when user clicks on the "Ok" button on the popup 
   $("#dok").button().on("click", function () { 
      var path = $("#datatest").html(); 
      $('#dcancel').click(); if (path == "somevalue") { //DO SOMETHING } 
   });

   // This sets the correct dimensions & position for pumpkin image popup
   $( document ).on( "pageinit", function() { 
      $( ".photopopup" ).on({ 
         popupbeforeposition: function() { 
            var maxHeight = $( window ).height() - 60 + "px"; 
            $( ".photopopup img" ).css( "max-height", maxHeight ); 
            $('#popupPumpkin').css( "top", '60px' ); 
         } 
       }); 
    });

   

Points of Interest

To see a simulation of Swipe Clouds in the Chrome Web Browser (it just works in Chrome) just Click Here: 
www.SwipeClouds.com/demos/index.html To create this simulation I used the new mootools simulator that you can download at:
http://moobilejs.com/

Conclusion

There are a lot of ways of customizing these Swipe Clouds and I leave it up to the read to explore their use in JQuery Mobile Apps. Enjoy!
   
   

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

William SerGio
Software Developer (Senior) http://www.SerGioApps.com
United States United States
I love coding and develop desktop apps (C++, C#, Java), websites, and mobile apps (iPhone, Android, Blackberry, iPad, PhoneGap/Cordova).
 
I am launching a new national TV series that features the best mobile apps. Show me your apps!
 
I have written software for Microsoft, MySpace.com, Quicken (Intuit), Mellon Bank, U.S. Army, U.S. Navy, Franklin Templeton, Pepsi, Universal Studios, Ryder Systems, AVID, Media 100, etc.
 
Bill SerGio
http://www.SerGioApps.com
http://www.Software-rus.com

Comments and Discussions

 
GeneralMy vote of 5 PinprofessionalSanthoshobject23-Aug-13 5:15 
GeneralRe: My vote of 5 PinmemberWilliam SerGio23-Aug-13 5:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141030.1 | Last Updated 12 Aug 2013
Article Copyright 2013 by William SerGio
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid