Update
I have made some major modification, which give more control over touch events. You can now set minimum/maximum values of touch duration, distance, and threshold for both X and Y axis.
Moreover, images now are preloaded for smoother transition between images.
I have made this rather complicated code based on touch events touchstart
and touchend
, horizontally and vertically. The code catches touch events and then interpret them into swipe up, right, down and left.
Images are exchanged with .animate()
according to swipe's direction. Swipe up and left, load next images in array; down and right load previous ones.
It is somehow lengthy, and have too much room of enhancement. For instance, you can calculate time elapsed between both events touchstart and touchend to ensure that the touch was sufficient enough to trigger custom swipes.
I'll go through code's main parts for more explanation.
HTML
<div class="wrapper">
<div class="inner">
<!-- images go here -->
</div>
</div>
CSS
Wrapper - height and width should be adjusted to your need:
.wrapper {
overflow: hidden;
position: relative;
height: 200px;
width: 400px;
margin: 0 auto;
}
Inner wrapper - To append images to:
.inner {
height: 200px;
width: auto;
position: absolute;
left: 0;
white-space: nowrap;
}
Transition wrappers - Used for images transition in and out:
.holder, .in, .hidden {
position: absolute;
}
Hide preloaded images:
.hidden {
display: none;
}
JS
Variables and defaults:
var total = images.length - 1, /* images total number */
current = 0, /* image's index */
startX = '', /* touchstart X coordinate */
startY = '', /* touchstart Y coordinate */
endX = '', /* touchend X coordinate */
endY = ''; /* touchend Y coordinate */
swipeDuration = 1000, /* max touch duration */
swipeDistanceX = 50, /* X-axis min touch distance */
swipeDistanceY = 50, /* Y-axis min touch distance */
thresholdX = 30, /* X-axis max touch displacement */
thresholdY = 30; /* Y-axis max touch displacement */
Preload images:
Wrap each one in holder
and then append them to inner
div, on pageinit
event or any other jQM page events.
$(document).on("pageinit", "#gallery", function () {
$.each(images, function (i, src) {
$("<div class='holder hidden'><img src=" + src + " /></div>").appendTo(".inner");
});
$(".inner .holder:first-child").toggleClass("visible hidden");
});
Touch events interpretation - bind Touch events to inner
div:
Touch duration and distance are added to comparison.
$(document).on("touchstart", ".inner", function (e, ui) {
startX = e.originalEvent.touches[0].pageX;
startY = e.originalEvent.touches[0].pageY;
start = new Date().getTime(); /* touch start */
}).on("touchmove", ".inner", function (e, ui) {
/* prevent page from scrolling */
e.preventDefault();
}).on("touchend", ".inner", function (e, ui) {
endX = e.originalEvent.changedTouches[0].pageX;
endY = e.originalEvent.changedTouches[0].pageY;
end = new Date().getTime(); /* touch end */
if ((end - start) < swipeDuration) {
if (startX > endX && Math.abs(startY - endY) <= thresholdY && Math.abs(startX - endX) >= swipeDistanceX) {
showImg(current, "left");
} else if (startX < endX && Math.abs(startY - endY) <= thresholdY && Math.abs(startX - endX) >= swipeDistanceX) {
showImg(current, "right");
} else if (startY > endY && Math.abs(startX - endX) <= thresholdX && Math.abs(startY - endY) >= swipeDistanceY) {
showImg(current, "up");
} else if (startY < endY && Math.abs(startX - endX) <= thresholdX && Math.abs(startY - endY) >= swipeDistanceY) {
showImg(current, "down");
}
}
});
Transition showImg(image index, swipe type)
function:
Added opacity to animation.
function showImg(index, type) {
if (type == "left") {
current = index;
if (current >= 0 && current < total) {
current++;
var distance = $(".visible").width();
$(".inner .holder").eq(current).css({
left: distance
}).toggleClass("in hidden");
$(".visible").animate({
left: "-" + distance + "px",
opacity: 0
}, 600, function () {
$(this).toggleClass("visible hidden").css({
top: "auto",
left: "auto"
});
});
$(".in").animate({
left: 0,
opacity: 1
}, 500, function () {
$(this).toggleClass("in visible");
});
}
}
if (type == "up") {
current = index;
if (current >= 0 && current < total) {
current++;
var distance = $(".visible").height();
$(".inner .holder").eq(current).css({
top: distance + "px"
}).toggleClass("in hidden");
$(".visible").animate({
top: "-" + distance + "px",
opacity: 0
}, 600, function () {
$(this).toggleClass("visible hidden").css({
top: "auto",
left: "auto"
});
});
$(".in").animate({
top: 0,
opacity: 1
}, 500, function () {
$(this).toggleClass("in visible");
});
}
}
if (type == "right") {
current = index;
if (current > 0 && current <= total) {
current--;
var distance = $(".visible").width();
$(".inner .holder").eq(current).css({
left: "-" + distance + "px"
}).toggleClass("in hidden");
$(".visible").animate({
left: distance + "px",
opacity: 0
}, 600, function () {
$(this).toggleClass("visible hidden").css({
top: "auto",
left: "auto"
});
});
$(".in").animate({
left: 0,
opacity: 1
}, 500, function () {
$(this).toggleClass("in visible");
});
}
}
if (type == "down") {
current = index;
if (current > 0 && current <= total) {
current--;
var distance = $(".holder").height();
$(".inner .holder").eq(current).css({
top: "-" + distance + "px"
}).toggleClass("in hidden");
$(".visible").animate({
top: distance + "px",
opacity: 0
}, 600, function () {
$(this).toggleClass("visible hidden").css({
top: "auto",
left: "auto"
});
});
$(".in").animate({
top: 0,
opacity: 1
}, 500, function () {
$(this).toggleClass("in visible");
});
}
}
}
Demo (1)
(1) Tested on iPad 2 and iPhone 5 - v7.0.4