Thursday, October 6, 2011

Adding Swipe Support

I spent some time trying to figure out how to add mobile swipe support without using jQuery Mobile, because the swipe is all I really needed. I don't own an iPhone/iPad/iAnything, so it took a bit of back-and-forth testing - thanks to all the folks over at CSS-Tricks.com - to get this to finally work.



Here is the complete code:

var maxTime = 1000, // allow movement if < 1000 ms (1 sec)
    maxDistance = 50,  // swipe movement of 50 pixels triggers the swipe

    target = $('#box'),
    startX = 0,
    startTime = 0,
    touch = "ontouchend" in document,
    startEvent = (touch) ? 'touchstart' : 'mousedown',
    moveEvent = (touch) ? 'touchmove' : 'mousemove',
    endEvent = (touch) ? 'touchend' : 'mouseup';

target
    .bind(startEvent, function(e){
        // prevent image drag (Firefox)
        e.preventDefault();
        startTime = e.timeStamp;
        startX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX;
    })
    .bind(endEvent, function(e){
        startTime = 0;
        startX = 0;
    })
    .bind(moveEvent, function(e){
        e.preventDefault();
        var currentX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX,
            currentDistance = (startX === 0) ? 0 : Math.abs(currentX - startX),
            // allow if movement < 1 sec
            currentTime = e.timeStamp;
        if (startTime !== 0 && currentTime - startTime < maxTime && currentDistance > maxDistance) {
            if (currentX < startX) {
                // swipe left code here
                target.find('h1').html('Swipe Left').fadeIn();
                setTimeout(function(){
                    target.find('h1').fadeOut();
                }, 1000);
            }
            if (currentX > startX) {
                // swipe right code here
                target.find('h1').html('Swipe Right').fadeIn();
                setTimeout(function(){
                    target.find('h1').fadeOut();
                }, 1000);
            }
            startTime = 0;
            startX = 0;
        }
    });


Now, lets break down what each line does. First, some definitions:

var maxTime = 1000, // allow movement if < 1000 ms (1 sec)
    maxDistance = 50,  // swipe movement of 50 pixels triggers the swipe

    target = $('#box'),
    startX = 0,
    startTime = 0,

The "maxTime" variable is a set to 1000 milliseconds. It is used to compare the touch start and touch end times, and if less than one second (1000 ms) it is considered to be a valid swipe, otherwise it is ignored. Basically, we don't like them slow swipers! Hehe, ok, actually, it to determine if the event ends up being a touch or a swipe.

"maxDistance" is the number of pixels from the touch start point that the mouse or a finger moved to determine if a swipe or touch is occurring.

The "target" variable, is the target element to add swipe support to. For the demo, I added an H1 tag inside of this element to add a swipe message. The "startX" and "startTime" variables are set to zero because the "touchmove" or "mousemove" event looks to see if they are set with the "startX" position and start time. If zero, it ignores the move.

In the next part we figure out of the device has touch support and set up the event names appropriately:

touch = "ontouchend" in document,
startEvent = (touch) ? 'touchstart' : 'mousedown',
moveEvent = (touch) ? 'touchmove' : 'mousemove',
endEvent = (touch) ? 'touchend' : 'mouseup';

The "touch" varible is true if the "ontouchend" event exists, which means touch is supported. All that line is doing is looking to see if that object exists.

The next variables, "startEvent", "moveEvent" and "endEvent" contain the necessary event names for start, move and end, respectively. Which the next lines of code will now bind to:
target
    .bind(startEvent, function(e){
        // prevent image drag (Firefox)
        e.preventDefault();
        startTime = e.timeStamp;
        startX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX;
    })
The "touchstart" or "mousedown" event code contains the following:

"e.preventDefault()" prevents the image drag function in Firefox which actually makes you think you're dragging the image instead of swiping. Now we define "startTime" and "startX" as the start time and position of the swipe. The "startX" needs to get the position of the touch from the touches variable, but only the first one. We don't want to swipe if there are multiple touches (gestures) but it shouldn't work anyway since we're not binding to the gestures event.
.bind(endEvent, function(e){
    startTime = 0;
    startX = 0;
})
Now the "touchend" or "mouseup" event code only clears the start time and position variables. The reason for this, which was a fun and painful lesson, is that these events don't fire when your mouse/finger leaves the element or goes off the screen. So the actual swipe calculations are done in the move event functions.

.bind(moveEvent, function(e){
    e.preventDefault();
    var currentX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX,
        currentDistance = (startX === 0) ? 0 : Math.abs(currentX - startX),
        // allow if movement < 1 sec
        currentTime = e.timeStamp;
    if (startTime !== 0 && currentTime - startTime < maxTime && currentDistance > maxDistance) {
        if (currentX < startX) {
            // swipe left code here
            target.find('h1').html('Swipe Left').fadeIn();
            setTimeout(function(){
                target.find('h1').fadeOut();
            }, 1000);
        }
        if (currentX > startX) {
            // swipe right code here
            target.find('h1').html('Swipe Right').fadeIn();
            setTimeout(function(){
                target.find('h1').fadeOut();
            }, 1000);
        }
        startTime = 0;
        startX = 0;
    }
});

The "touchmove" or "mousemove" events is where the main portion of this swipe code exists. Again, we want to prevent the default functionality of the move/drag. The "currentX" variable is set with the current x position of the finger/mouse. Again, the x position is obtained from the touches variable, if available. The difference "currentDistance" between the starting x and current x position is then determined. We need the absolute value of this number (ignore the negative sign if it is there) to see if it is more than the "maxDistance" variable. Also here is another trick to make sure the move event is occurring after a start event, check to make sure the starting x position isn't zero. If it is, set the "currentDistance" variable to zero. We also need determine the "currentTime" current time of the move event.

Now, we check to make sure the move event occurred after a start event. This can happen is the start event occurs outside of the element or window. So, first we make sure the "startTime" isn't zero, then we check that the time difference from start to current time is less than one second. And finally, we check that the current distance is more than the max distance setting. If it is, then we have a swipe!

YAY finally a swipe! Now to just figure out if it was going to the right or left. We do this by comparing the current position "currentX" to the starting position "startX"; if the current position is less than the start, then it's a swipe to the left and if it is greater, then the swipe was to the right.

Last but not least, we set the start time and position back to zero to prevent multiple firing of the swipe event.

I hope everything had a clear enough explanation. Please feel free to leave a comment or email me (gmail with wowmotty as the user) with questions or if you need further clarification.

Thursday, September 22, 2011

Print a Paragraph

By default, browsers will print the content of an entire page/web page or selected text. With some simple scripting you can make it easier for your users to get the browser to print the contents of a particular block or paragraph. The trick is to copy these contents into a new popup window, then print the contents of that window. I put together a very simple demo, which I should turn into a plugin, of how to do this:

Sunday, September 11, 2011

AnythingSlider Themes

I put together ten more themes for AnythingSlider! YAY!


  • All theme files are meant to be used independently from the plugin's "anythingslider.css" file. So you only need to include one of these theme files.
  • There is an additional stylesheet named "wrappers.css" which only targets the wrapper around the slider. It is independent of the theme files and meant to be used by extracting out any specific wrapper style you would like to use.
  • The first default theme is meant to be used as the base to make your own theme, but without using images or css3.
  • The second default theme is also meant to be used as a base for your own theme, but includes images (no css3 though)
  • The rest of the themes are free to use.
If you would like to contribute a theme, either fork the repository, add your theme then send me a pull request or just email me the files - my gmail account, user name is wowmotty.

Thanks and enjoy!