How to make a slider scrollable on touch screens? - javascript

I have a site here:
www.completefaq.com
The main page contains a slider, which I built on my own. It changes the slides automatically after 8 secs. and on a click on forward or backward button. But, the problem is that I want it to scroll even when one tries to just slide it on a touchscreen.
Any help is appreciated. I can only use CSS, JavaScript, HTML and PHP, because other APIs such as JQuery, tend to slowdown the website.

Since you don't want a jQuery plugin, it's going to be non trivial due to the differences between the various touch platforms, and in the end you would probably end up in reinventing the wheel trying to get the abstraction you need. Above all, your effort will clearly depend on the level of accuracy you want to achieve, so it's advisable that you don't try to make the images respond to every minimal touch event.
I believe your best bet is to use Tocca.js, a very promising, standalone script which is "super lightweight" (only 1kB) and aims to normalize touch events among existing devices.
Hammer.js is more accurate, but could be a bit heavier in your case.
QuoJS is also good but it's not focused only on touch events.
You may find this and this SO question interesting. Finally, you can always take inspiration from the several existing touch-enabled javascript image sliders.

Here is a very lightweight script to start with.
Put this script somewhere below your div#featured, or execute it on dom ready.
You may want ajust the minimum swipe time (200ms) and minimum swipe distance (50px):
var featured = document.getElementById("featured");
if( "ontouchstart" in window ) {
var touchStart = function(evt) {
var startTime = (new Date()).getTime();
var startX = evt.changedTouches[0].pageX;
var touchEnd = function(evt) {
document.removeEventListener("touchend", touchEnd);
var diffX = evt.changedTouches[0].pageX - startX;
var elapsed = (new Date()).getTime() - startTime;
console.log( "Swiped " + diffX + " pixels in " + elapsed + " milliseconds" );
if( elapsed < 200 && Math.abs(diffX) > 50 ) {
( diffX < 0 ) ? slideright() : slideleft();
}
}
document.addEventListener("touchend", touchEnd);
};
featured.addEventListener("touchstart", touchStart);
}
I understand that you want to keep your website light, but if you wan't more advanced gesture recognition that this simple swipe, you will have to go with some kind of framework.

Related

Very simple parallax code has performance issues

For a parallax-effect, I created a simple script in native Javascript, but it seems to fail somewhere I can't see. That's why I already added the requestAnimationFrame-functionality, but it doesn't seem to really help.
My relevant code is as follows:
var $parallax, vh;
$(document).ready(function() {
$parallax = $('.parallax');
vh = $(window).height();
$parallax.parallaxInit();
});
$(window).resize(function() {
vh = $(window).height();
$parallax.parallaxInit();
});
$.fn.parallaxInit = function() {
var _ = this;
_.find('.parallax-bg')
.css('height', vh + (vh * .8) );
}
//call function on scroll
$(window).scroll(function() {
window.requestAnimationFrame(parallax);
});
var parallaxElements = document.getElementsByClassName('parallax'),
parallaxLength = parallaxElements.length;
var el, scrollTop, elOffset, i;
function parallax(){
for( i = 0; i < parallaxLength; i++ ) {
el = parallaxElements[i];
elOffset = el.getBoundingClientRect().top;
// only change if the element is in viewport - save resources
if( elOffset < vh && elOffset + el.offsetHeight > 0) {
el.getElementsByClassName('parallax-bg')[0].style.top = -(elOffset * .8) + 'px';
}
}
}
I think it's weird that this script by Hendry Sadrak runs better than my script (on my phone) while that is not really optimised, as far as I can tell.
Update: I checked if getBoundingClientRect might be slower in some freak of Javascript, but it's about 78% faster: https://jsperf.com/parallax-test
So here is the downlow on JS animations on mobile devices. Dont rely on them.
The reason is that mobile devices have a battery and the software is designed to minimize battery load. One of the tricks that manufacturers use (Apple does this on all their mobile devices) is temporarily pause script execution while scrolling. This is particularly noticeable with doing something like parallax. What you are seeing is the code execution - then you scroll, it pauses execution, you stop scrolling and the animation unpauses and catches up. But that is not all. iOS uses realtime prioritization of the UI thread - which means, your scrolling takes priority over all other actions while scrolling - which will amplify this lag.
Use CSS animation whenever possible if you need smooth animation on mobile devices. The impact is seen less on Android as the prioritization is handled differently, but some lag will likely be noticeable.
Red more here: https://plus.google.com/100838276097451809262/posts/VDkV9XaJRGS
I fixed it! I used transform: translate3d instead, which works with the GPU instead of the CPU. Which makes it much smoother, even on mobile.
http://codepen.io/AartdenBraber/pen/WpaxZg?editors=0010
Creating new jQuery objects is pretty expensive, so ideally you want to store them in a variable if they are used more than once by your script. (A new jQuery object is created every time you call $(window)).
So adding var $window = $(window); at the top of your script and using that instead of calling $(window) again should help a lot.

run js on pinch in; run different js on pinch out

I'm trying to develop an interaction I designed where an html element is y-scaled ( scaled in height ) during a pinch so that the height of the element becomes the distance between the two fingers involved in the gesture.
For now, I'm trying to listen for a pinch in and fire an alert and then listen for a pinch out and fire a separate alert with jQuery. Do you know how to do this? Perhaps without a plugin?
I've attempted to do this with two plugins. More on the first attempt, later.
What's wrong with the second attempt is that it says a pinch in is a pinch out and even a tap or click is a pinch out.
My second attempt is in this JS Fiddle: http://jsfiddle.net/hnabM/1/
Here is code used in the second attempt:
//pinchZoom is the distance between the two fingers involved in the pinch
var pinchIn = null;
var dist = null;
$('.pinch').swipe({
pinchIn:function(event, direction, distance, duration, fingerCount, pinchZoom){
dist = null;
},
pinchOut:function(event, direction, distance, duration, fingerCount, pinchZoom){
dist = null;
},
pinchStatus:function(event, phase, direction, distance, duration, fingerCount, pinchZoom){
if(dist != null){
if(dist > pinchZoom){
pinchIn = true;
}
else{
pinchIn = false;
}
dist = pinchZoom;
if(pinchIn === true){
console.log("pinching in");
}
else{
console.log("pinching out");
}
}
else{
dist = pinchZoom;
}
},
fingers:2,
pinchThreshold:0
});
Here is what I did the first attempt. I got it working, but when I combined it with my site, it suddenly didn't work.
The first time I attempted this, I did it with the jQuery version of hammer.js ( http://eightmedia.github.io/hammer.js/ )-- a somewhat famous library for listening for multi-touch gestures. I successfully accomplished it separately apart from the site that I need it to work in. Here it is working on the class .polaroid ( the images ), try pinching in on them and they y-scale down and pinching out on them fires an alert: http://goo.gl/oOQDH0. However, when I apply the same exact js in the site, the site I need it to work in, it suddenly doesn't work. All the dependencies are there. No errors. Also scrolling is somewhat inhibited ( tough to do on touch devices ) for some-reason. It is same exact code applied to the .dataCard class ( the white rectangles under the header ), but failing to work: http://goo.gl/dagKxT
I would greatly, greatly appreciate any and all help in getting this interaction developed and working.
Thanks in Advance!
Not to dismiss or discourage you from doing so but is there any particular reason why you are writing this yourself?
In any case take a look at hammer.js
https://github.com/EightMedia/hammer.js
Which supports/emulates a lot of the common touch gestures out there. Including pinch to zoom. It's there on their example pages:
https://github.com/EightMedia/hammer.js/blob/master/examples/pinchzoom.html
You've probably done your research and seen lots of libraries like this but Hammer's code base is really well laid out and easy to figure out.
If you're going to give this a go from scratch you will probably end up detecting a gesture at the start and locking it down with a common flag (so other gestures don't kick in) then resetting the flag when the gesture completes.

scrolling on mobile devices

This question is more of an advice research, I do hope that it will be helpful for others and it won't closed, as I'm not quite sure where to ask for advice on this matter.
I've been developing for mobile for the past 6 months and I had the occasion to deal with all kinds of situations and bugs on various devices.
The most troubling was the scrolling issue, when it comes to scrolling in multiple areas of the website. On three projects that I have been working on I've been building a navigation that behaves the same way that the native iOS Facebook app has, or the Google website on mobile, etc. And for each one I came up with different solutions.
But a few days ago I have just released a new JavaScript library, drawerjs, that can be used to generate such navigation (so called off canvas concept). The difference between the other libs and this one is that is library agnostic, and it acts on touch behavior (the same way that the Facebook app behaves) not just open / close on click.
One of the things that I have left to implement is a solution for scrolling inside the menu and the navigation without affecting one another (most of the time when you scroll in such way, the content tends to scroll together with you menu or after you have reached the end of the menu scrolling).
I have two solutions in mind:
One approach would be to use the same principle I'm using for dragging the content and showing the navigation, on touchmove I prevent the default scrolling on document / content and I start translating the contents with the same amount you scroll. And with the same resistant behavior as a touch slider would have (when you exceed the boundaries and let go, the contents would translate back so it doesn't exceed the boundary anymore, or on swipe with the same behavior).
A second approach would be using the native overflow-scrolling that iOS has and would offer the same feel as described in the first approach. The downside of this would be that only iOS devices would have the nice resistant feature, but it would be, supposedly, less of a hassle the the first approach.
So I'm not quite sure which approach I should take, or if there any better solutions for that. I'm also trying to keep in mind that some users would like to hide the url bar, so scrolling on the body / html would have to be kept (on the y axis).
You could do touchmove . But as far as I understand, you want something like this?
http://jsfiddle.net/2DwyH/
using
var menu = $('#menu')
menu.on('mousewheel', function(e, d) {
if((this.scrollTop === (menu[0].scrollHeight - menu.height()) && d < 0) || (this.scrollTop === 0 && d > 0)) {
e.preventDefault();
}
});
Using this plugin from Brandon Aaron - github : https://github.com/brandonaaron/jquery-mousewheel
And it should work with Android: What DOM events are available to WebKit on Android?
Some more info here: Prevent scrolling of parent element?
Also without using the plugin above , using only jQuery you could do this like it says on the link above - answer from Troy Alford
$('.Scrollable').on('DOMMouseScroll mousewheel', function(ev) {
var $this = $(this),
scrollTop = this.scrollTop,
scrollHeight = this.scrollHeight,
height = $this.height(),
delta = (ev.type == 'DOMMouseScroll' ?
ev.originalEvent.detail * -40 :
ev.originalEvent.wheelDelta),
up = delta > 0;
var prevent = function() {
ev.stopPropagation();
ev.preventDefault();
ev.returnValue = false;
return false;
}
if (!up && -delta > scrollHeight - height - scrollTop) {
// Scrolling down, but this will take us past the bottom.
$this.scrollTop(scrollHeight);
return prevent();
} else if (up && delta > scrollTop) {
// Scrolling up, but this will take us past the top.
$this.scrollTop(0);
return prevent();
}
});
The JS Fiddle he mentions: http://jsfiddle.net/TroyAlford/4wrxq/1/
Why not just provide a fixed height to your widget (min and max will also do). Then define like these -
height: x px;
overflow-y: auto;
This way till the focus is inside the widget, it'll only overflow the widget, once outside the page will scroll without affecting widget content at all.

Scroll a page with touch events

I have a website page and I've added to the body of the page touch events.
More exactly for swipe right and swipe left. Since the event listener is added to the body of the page and I have added event.preventDefault(); i can't scroll the page any more.
How can i scroll the page in the browser ?
P.S. The code is pure javascript / library agnostic.
Edit #1. This site viewed in mobile seems to do it http://swipejs.com/ . It slides the tabs right to left and back as well as scroll the website. I just can't seen in the code how :(
Use iscroll plugin. it's help to you.
see example : http://cubiq.org/dropbox/iscroll4/examples/simple/
Unfortunately there is no easy answer. The best way is to build smart gesture recognizers. Or use something like this (for Safari Mobile):
http://mud.mitplw.com/JSGestureRecognizer/#single-gesture
You will notice that when you are touching a gesture recognizer, there is no scrolling. However, you could make the callback of a recognizer scroll the page.
Wondering why it only says it supports Safari mobile? That's because Safari mobile has its own set of touch events. However, you can use it as a start and try to add support for other platforms.
I have the same problem that swiping without "preventDefault()". Because I want to achieve a pulltorefresh's effect, I can only prevent the pulldown event but not pullup. The code like this:
function touchBindMove(evt){
//evt.preventDefault();
try{
var deviceHeight = window.innerHeight;
var touch = evt.touches[0]; //获取第一个触点
var x = Number(touch.pageX); //页面触点X坐标
var y = Number(touch.pageY); //页面触点Y坐标
//记录触点初始位置
if((y - offsetStart) > 0 && document.body.scrollTop == 0){
evt.preventDefault();
var page = document.getElementsByClassName('tweet-page')[0];
var rate = 0;
end = x;
offsetEnd = y;
rate = (offsetEnd - offsetStart) / (2 * deviceHeight);
//tool.print(rate);
easing.pullMotion(page, rate);
}
}catch(e){
alert(e.message);
}
}
"y - offsetStart" judges whether the event is pulldown and "document.body.scrollTop == 0" judges the scrollbar is in the middle or not.
Maybe it can help you a little bit.

Fake scroll bars in browser

I want to provide the user with the experience of scrolling through content, but I would like to load the content dynamically so the content in their viewing area is what they would expect, but there is no data above or below what they are looking at. For performance reasons, I don't want that data loaded. So when they scroll down new data gets loaded into their view, and data previously in their view is discarded. Likewise when scrolling up. The scroll bar should represent their location within the entire content though, so using "infinite scrolling" or "lazy loading" does not look like what I need.
My solution may be that I need to re-architect things. As of now, my project is a hex-viewer that allows you to drop a binary file onto it. I create html elements for every byte. This causes performance issues when you end up with a 1MB file (1,000,000+ DOM elements). One solution would be to not use DOM elements/byte but I think this will make other features harder, so I'd like to just not display as many DOM elements at once.
Make a div, set overflow to scroll or auto. As user scrolls you can change the content of the div.
You could look at yahoo mail (the JavaScript based one) to see how they do it (they add rows with email as you scroll).
You don't necessarily need custom scroll bars.
You could look for some code here for custom scroll bars:
http://www.java2s.com/Code/JavaScript/GUI-Components/Scrolltextwithcustomscollbar.htm
or here:
http://www.dyn-web.com/code/scroll/
I'm looking for an answer to this question as well so I'll share where I'm at with it.
I have a large amount of content I want to display vertically and have the user scroll through it. I can load it all into the DOM and scroll normally but that initial creation phase is horribly slow and scrolling can awfully slow also. Also, I will dynamically add to it as I stream more data in.
So I want the same thing which is to be able to dynamically populate and update a non-scrolling area with content. I want to make it seem as if the user is scrolling through that content and have a model (which has lots of data) that is kept off the DOM until it would be seen.
I figure I'll use a queue concept for managing the visible DOM elements. I'd store queueHeadIndex and queueTailIndex to remember what off-DOM elements are shown in the DOM. When the user scrolls down, I'd work out what whether the head of queue falls off the screen and if it does update queueHeadIndex and remove it's DOM element. Secondly I'd then work out whether I need to update queueTailIndex and add a new element to the DOM. For the elements currently in the DOM I'd need to move them (not sure if they need animation here or not yet).
UPDATE:
I've found this which seems to have some promise http://jsfiddle.net/GdsEa/
My current thinking is that there are two parts to the problem.
Firstly, I think I want to disable scrolling and have some sort of virtual scrolling. I've just started looking at http://www.everyday3d.com/blog/index.php/2014/08/18/smooth-scrolling-with-virtualscroll/ for this. This would capture all the events and enable me to programmatically adjust what's currently visible etc. but the browser wouldn't actually be scrolling anything. This seems to provide mouse wheel driven scrolling.
Secondly, I think I need to display a scroll bar. I've had a look at http://codepen.io/chriscoyier/pen/gzBsA and I'm searching around more for something that looks more native. I just want it to visually display where the scroll is and allow the user to adjust the scroll position by dragging the scroller.
Stackoverflow is insisting I paste code so here is some code from that codepen link above
var elem = document.getElementById('scroll-area'),
track = elem.children[1],
thumb = track.children[0],
height = parseInt(elem.offsetHeight, 10),
cntHeight = parseInt(elem.children[0].offsetHeight, 10),
trcHeight = parseInt(track.offsetHeight, 10),
distance = cntHeight - height,
mean = 50, // For multiplier (go faster or slower)
current = 0;
elem.children[0].style.top = current + "px";
thumb.style.height = Math.round(trcHeight * height / cntHeight) + 'px';
var doScroll = function (e) {
// cross-browser wheel delta
e = window.event || e;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
// (1 = scroll-up, -1 = scroll-down)
if ((delta == -1 && current * mean >= -distance) || (delta == 1 && current * mean < 0)) {
current = current + delta;
}
// Move element up or down by updating the `top` value
elem.children[0].style.top = (current * mean) + 'px';
thumb.style.top = 0 - Math.round(trcHeight * (current * mean) / cntHeight) + 'px';
e.preventDefault();
};
if (elem.addEventListener) {
elem.addEventListener("mousewheel", doScroll, false);
elem.addEventListener("DOMMouseScroll", doScroll, false);
} else {
elem.attachEvent("onmousewheel", doScroll);
}
I imagine I'll have one class that listens to scroll events by either the virtual scroll method or the ui and then updates the ui scroller and the ui of the content I'm managing.
Anyway, I'll update this if I find anything more useful.
I think avoiding using DOM elements/byte is going to be the easier solution for me than creating a fake scrolling experience.
UPDATE: I ultimately solved this as explained here: Javascript "infinite" scrolling for finite content?
You're taking about using some serious javascript, specifically AJAX and JSON type elements. There is no easy answer to your questions. You'd need to do a lot of R&D on the subject.

Categories

Resources