I'm working on a parallax effect for a project and while I have all the animation working great the scroll-driven effect often appears to stutter depending on the input device.
I've ended up using Request Animation Frame (RAF) to check the page offset every animation loop instead of using the basic `.scroll() event for performance reasons. However using either, if I log out the offset/scroll position values the events receive they are often very far apart for a scroll wheel/ball versus a track pad. Because the animation is scroll/offset driven this causes stuttering.
For instance a track pad triggers many events with only a few pixels difference between each event's offset value while a scroll wheel/ball triggers fewer events with 5-15 pixels difference (depending on the scroll speed) leading to a less smooth animation.
You can fool around with this yourself on this guys fiddle that goes like this:
$(window).scroll(example);
function example() {
var tempScrollTop = $(window).scrollTop();
console.log("Scroll from Top: " + tempScrollTop.toString());
};
Anyone know a good library or method for smoothing out these scroll events and their offsets? I know it's possible because I've seen parallax sites where the animations are nice and smooth regardless of input but I can't figure out how it's managed. Thanks.
Related
I am using a Muuri grid with draggable items. On desktop, users can use the mousewheel to scroll; however, I had to add an event handler to check if the user holds the item for x time, then they can drag the item. If they immediately move a certain distance, it prevents dragging and programmatically scrolls the view with window.scrollTo.
This works fine on Android and Windows touch screens but the screen jumps around on iPhones. Worse on Safari but still occurs on Chrome. I tried using webkit style atrributes that some articles mention for smoother scrolling, used different scroll behaviors, made sure calculations lead to a top, whole number value (only setting top) of 0 or higher, and the only thing that made a difference was setting a delay. A higher delay (100ms) between scrollTo calls seems to resolve it but isn't smooth to the user.
Content for the move event is simply the method call:
window.scrollTo({top: initialTop + yStart - yEnd})
I'm trying to explore how feasible it is to create an HTML5 interaction where a touch can be conditionally captured by the initial direction of the motion.
The goal is to capture and track the motion of the touch only if it initially moves in a horizontal direction. Combined with a responsive page layout which only scrolls vertically, we should be able to use horizontal swipe motion to do something cool by tracking it.
The problem is in the seeming impossibility (especially on iOS) of performing touch sequence capture conditionally.
In order to track a touch (and by this I mean specifically obtaining the stream of x,y coordinates which represent the position of the finger over time), touchstart has to be preventDefaulted in order to prevent the page from engaging native momentum-scroll. Native momentum-scroll, while in operation, suspends all JS execution (setTimeout, rAF, jQuery animate et al...) and even CSS keyframe/animation/transition execution.
I would really like to know if there's a way to somehow condition the preventDefault() of the original touchstart event. It's completely possible for JS to store that event object, and then not call it until some later time (when it is determined that we are indeed interested in preventing the native scroll from starting).
But this is certain to fail because the default behavior of not running preventDefault() on that event is to engage native-scroll, which then will block JS execution for the entire remaining duration of the scroll. Failing to return the event listener function attached to touchstart does not appear to be an option. It would freeze everything.
The tentative answer, then, without any additional insight, is that all swipes must be captured if we want to be able to capture it at all, and scrolling has to be "faked" via external means à la (iScroll)[http://cubiq.org/iscroll-5-ready-for-beta-test] (personally I would want to explore some sort kind of combination of rAF and window.scrollTo and would be surprised if iScroll 5 does not employ these APIs)
Incidentally the description for the 4th demo there got me really excited, but it turns out that all it does is create a page section that iScrolls horizontally while the rest of the page behaves regularly, which is entirely mundane.
I am using Adobe Edge Animate to create a large portion of my website, but I want to make a mobile version as well, and so obviously instead of onClick events, I want to use onTouch events. I just can't seem to find any good information on how to use touch events as I have never used them before.
I want to make a simple image gallery that plays forward when you swipe to the left, and plays reverse when you swipe to the right.
The big problem here is, how do I tell a touch event to do different things based on the direction of the swipe?
first of all, you're right, you want to use three event handlers: touchstart, touchmove, touchend
one approach you might take is to compare the starting position of the user's finger(s) to the ending position. although the code will vary based on how many fingers you want to detect, i'll outline a basic method you can adapt. in order to detect one finger, enter the following in a "touchstart" event handler:
var xStart = e.originalEvent.touches[0].pageX;
sym.setVariable('xStart', xStart);
that will store the starting x-position of the first finger in a variable and then make it globally accessible by other event handlers.
then maybe do the same thing in a "touchend" event handler. use some logic to compare the two values.
xStart = sym.getVariable('xStart');
var xEnd = e.originalEvent.touches[0].pageX;
if (xEnd > xStart)
{
sym.playReverse();
}
else if (xEnd < xStart)
{
sym.play();
}
then you'll have some stop frames to hold at those images.
that's just an example, but hopefully it'll help you get going.
good luck!
I currently use $(window).bind('scroll', foo); to monitor $(window).scrollTop() and do stuff to create a parallax effect.
In all desktop browsers foo() is called for each pixel the user scrolls, and everything is nice and dandy. In Safari on iOS, the scroll event is only fired AFTER the scrolling is finished.
I added $(window).bind('touchmove', foo); to make sure the function is called during the swipe in iOS, and it got me a little bit further. When user releases finger, the page continues to scroll, but the event stops firing.
Any ideas?
When I saw your question, I was planning to do a polyfill for this (if such does not exist?). Unfortunately I've had very little time.
The idea is to have a setInterval, which is initiated ontouchstart, and it checks whether document.body.scrollTop has changed since last time, eg. for every 20 milliseconds. And if it is, then we manually dispatch the scroll event. Otherwise we clearInterval since apparently there's no more scrolling happening.
This it would be in brief. If you got more time (than I do), then feel free to try with those guidelines.
Edit: Now, reading further, the same idea is seems to be suggested on the other answer. Are you certain that intervals are stopped whilst scrolling on iPad?
I highly recommend using the "Skrollr" javascript library. It is by far the best mobile scrolling animation option that I've found to date and is very easy to get up and running quickly. Create animations and manipulate CSS based on a start and end scroll position. Create as many data scroll positions and animations as you need for most standard CSS properties.
In the following example the background color would animate over the course of a 500 pixel scroll:
<div data-0="background-color:rgb(0,0,255);" data-500="background-color:rgb(255,0,0);">WOOOT</div>
Checkout the repository on Git: https://github.com/Prinzhorn/skrollr
Skrollr Demo Example: http://prinzhorn.github.io/skrollr/
Awesome real world example: http://www.fontwalk.de/03/
Apple's webpage for iPhone 5c uses some parallax scrolling effects that seem to continue with your finger still touching the screen and scrolling. So I guess javascript can't be entirely disabled during scroll. Tumult Hype provides this functionality too.
While this isn't possible out of the box, so to speak, you can do it using iscroll
So, you'd use iScroll for iOS and Android mobile devices / tablets, and use the way you're currently doing it for desktop devices.
iScroll has a bunch of options for callback functions to be called on certain events - such as 'onScrollMove', 'onBeforeScrollEnd', 'onTouchEnd', etc. Unfortunately, there's not an option to execute a callback on "EVERY position change, including changes cause by the momentum of scrolling after the user has stoped touching the screen". But, it's easy enough to add one.
In the iScroll source, around line 127, near the "//Events" comment, add a new variable:
onPosChange: null
Then, around line 308, at the end of the "_pos" function, add this line:
if (this.options.onPosChange) this.options.onPosChange.call();
(that will just call the function passed in the "onPosChange" option, if it exists).
Having done that, the following example code will set up iScroll for the div with id="iscroll_container", and will print the Y-offset to the console each time it changes:
var myScroll;
function loaded() {
myScroll = new iScroll('iscroll_container', { onPosChange: actOnScroll });
}
document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);
//Use this for high compatibility (iDevice + Android)
document.addEventListener('DOMContentLoaded', function () { setTimeout(loaded, 200); }, false);
function actOnScroll(){
console.log('got a scroll move! Vert offset is: ' + myScroll.y);
}
Note, Ben the Bodyguard Parallax Site works on iPad by using iScroll (they use a much older version of iscroll and don't do it exactly the way I described here)
Also, Life of Pi Parallax Site works on iPad really nicely - I can't figure out how they do it, but at least it proves it's possible!
This could be a bug with jQuery itself: http://bugs.jquery.com/ticket/6446
That ticket has been open for a while and pertains to iOS 4, but perhaps you should investigate calculating the scroll position with pure javascript.
As I know there is no javascript execution while scrolling on the most mobile devices.
There are some workarounds (f.e. iscroll) out there.
Iscroll is using css-technique "transform".
But there is no way to execute javascript while scrolling.
I suppose the smoothe scrolling-algorithm is too expensive.
I'm working on writing a custom scrollbar and am catching the mousewheel event. I'm using this to then adjust the scrollTop of the element I want to scroll on.
Is there a standard number of pixels that are scrolled down, or does it vary from system to system?
I'm showing 114px in the latest build of Firefox:
Many mouse drivers let you set the distance scrolled by the mouse wheel, so there is not a standard distance. I'd try your code out for a while and pick a distance that keeps you from scrolling all day but doesn't jump a mile at each scroll. You'll kind of need to "feel" it out. Get friends to give feedback, it helps to let a few hands touch this kind of thing.
I have noted that in Google Chrome - that is 100px per mouse scroll
For Firefox, you have the MozMousePixelScroll event, which reports the number of pixels that should be scrolled in e.detail.
window.addEventListener('MozMousePixelScroll', function(e) {
console.log(e.detail);
});
For many other browsers, you have the mousewheel event that reports e.wheelDeltaY, but they are not in pixels and you will have to guess the amount to be scrolled.
Also see how SproutCore handles scrolling in their own framework (they're writing their own scrolling view too): http://blog.sproutcore.com/scrolling-in-sproutcore/
You'll need to store the current scroll position before the scroll and then when you detect a scroll get the distance travelled me thinks.
We can controll using javascript.
Refer below link. I hope it will help you.
http://deepport.net/archives/javascript-scrolling/
Usually each mouse wheel "tick" corresponds to some configurable (by user) number of pixels.
In Windows for example distance scrolled by each mouse wheel click should be a function of
SystemParametersInfo( SPI_GETWHEELSCROLLLINES, ...) parameter.
Here you can find more info on the subject.