Javascript - Smooth parallax scrolling with mouse wheel - javascript

I have a page where I'm applying a parallax effect. This is accomplished using translate3d. Now, while this works well, I'm wondering how I can override the default "steps" when scrolling with the mouse wheel?
If I scroll with the scrollbars, everything is fine. But with the mouse wheel, it's all jumpy.
I'm doing this in a pretty straight forward way:
var prefix = Modernizr.prefixed('transform');
$window.on('scroll', function(){
var scroll_top = $window.scrollTop();
if(scroll_top < forside_infographics_offset){
$_('#slider').css(prefix , "translate3d(0,"+(scroll_top/3)+"px,0)");
}
});
Now, I've seen this site where the scrolling is super smooth, also with a mouse wheel with steps on it. I've tried to look at the code, and he's using requestAnimationFrame is seems, but how he accomplish this excact scrolling effect, I'm not sure.
http://cirkateater.no/
Any ideas?

After doing a lot of research, I found a pretty easy solution :)
http://bassta.bg/demos/smooth-page-scroll/
Interestingly enough, I didn't have to alter my existing code at all. This overrides the default scroll behaviour, while leaving the event open for me to use like I would normally do.
EDIT: This is a really bad idea. Never ever hijack and override expected behavior. I guess I was overly fascinated with animations back then and overdid everything. Thankfully we all learn and expand our perceptions of good UX principles :)

Scrolling using the mouse wheel requires special handling. Reason being each mouse wheel scroll doesn't scroll the content by a certain amount of pixels. Each scroll causes your page to jump and then each jump results in the "jumpy" jittery animation as the background image is trying to position itself at these jumps.
Using a library will solve the problem most of the time, but it is also worth understanding what problems it is trying to solve under the hood.
Just for reference sakes, the mouse events are mousewheel and DOMMouseScroll

This plugin for Chrome provides the functionality necessary for this. Someone created a gist with a minified version of it. It's from a pretty old version, but I think that's fine because, as I've checked, the latest version of the plugin adds too much stuff.
A couple things with that gist though:
It checks if the browser is Chrome before initiating.
It initiates automatically.
It uses jQuery.
So I let myself create a version that addresses those points. Just add the script and call SmoothScroll.init() to start.

Edit: While testing I figured out this has a significant bug. While my version behaves (in my opinion) tremendously better than the original code, it unfortunately does not account for scrolling by other means (scroll bar/middle click and drag). Scrolling by one of these methods and then scrolling with the mouse wheel causes it to revert to wherever scroll location you were at when you last scrolled the mousewheel. I'll update when I develop a solution to this.
Kenny's referenced solution was a fine approach, but it's functionality drove me crazy. If you scroll the wheel quickly it wouldn't scroll much faster.
I improved it such that you scroll a given distance per click regardless of mouse wheel spin speed.
The reason his answer did not is because if you scroll the wheel a second time before the first animation is complete the new scroll to height is only the current scroll height plus however much it scrolls per wheel click. (So if scroll time is .5 seconds and you scroll a second time after .25 seconds then it will scroll 1.5 times the wheel scroll distance instead of 2 times that distance)
It's late at night, I hope that makes sense.
Regradless here's my code:
Required libraries
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/latest/plugins/ScrollToPlugin.min.js"></script>
Scroll code
<script>
$(function(){
var $window = $(window)
var $scoll = $('#page-container')
var scrollTime = 0.5
var scrollDistance = 120
var scrollTop = $scoll.scrollTop()
$window.on("mousewheel DOMMouseScroll", function(event){
event.preventDefault()
var delta = event.originalEvent.wheelDelta/120 || -event.originalEvent.detail/3
scrollTop = scrollTop - parseInt(delta*scrollDistance)
scrollTop = Math.max(0, Math.min($scoll[0].scrollHeight, scrollTop))
TweenMax.to($scoll, scrollTime, {
scrollTo : { y: scrollTop, autoKill:true },
ease: Power1.easeOut,
overwrite: 5
})
})
})
</script>

Great question.
The library I use is this one:
https://github.com/cferdinandi/smooth-scroll
Simply include the smoothscroll.js file, and job done.
The mouse-wheel will now smoothly easy down the page, rather than jumping down in chunks of pixels.
It really improves the look of parallax webpages.
Btw, for parallax images, I use this library:
https://github.com/pederan/Parallax-ImageScroll
It's really easy to add to a webpage, just remember to include and initialise this library at the bottom of your webpage, after your images and HTML.
(I didn't realise that this would make a difference, but it absolutely does !)

I know I'm late to the game but I was researching the topic of smooth scrolling today after I stumbled on I Love me Wellness' site.
Analyzing the sites JavaScript I found that they are using a vanillaJS script called Luxy.js which is showcased at Luxy.js on GitHub. I find the effect rather pleasing. I don't think it's a very bad idea if implemented well. And it's very easy to implement.

Related

requestAnimationFrame() does not seem to always repaint my newly applied css transforms

If anybody could help me, I would greatly appreciate it. I am using requestAnimationFrame() to apply a vertical translate on an element with CSS, and I want the vertical translate distance to relate to the vertical scroll position. My issue is that it seems like some frames either do not call my function or do not repaint with the updated CSS when using the scroll wheel (testing with Chrome and Firefox, both up-to-date).
This is a trimmed down version (optimizations removed) to show the root of what I'm trying to do:
let sticky = document.getElementById('top');
function changePos() {
let position = window.scrollY;
sticky.style.transform = `translateY(${position}px)`;
window.requestAnimationFrame(changePos);
}
window.requestAnimationFrame(changePos);
You can see a full working demo on this upload. For some reason clicking and dragging the scrollbar doesn't miss any frames, but using the mouse wheel does. You may have to throttle CPU using dev tools to see the missed frames, or use an older computer or phone.
Thanks so much.

Javascript - Smooth scrolling to element on mobile device

I am implementing a navigation with anchors that trigger some Javascript to scroll to a specified element, testing it on iOS (Iphone 4) for now.
This is not a new topic and I have done a lot of research before I decided to re-open this question, for no avail. However, my setup also differs a bit from the numerous others in that I first trigger a transition on the navigation horizontally and then the smooth-scroll afterwards. I am wondering if the combination might raise that "buggy" behavior. What make me not really believe this is the fact that waiting for the transition to finish does not resolve the problem (neither using a callback function, nor utilizing window.setTimeout).
Using .scrollTop simply make the browser jump to the element. The transition of the navigation however is smooth.
Using .animate, the transition of the navigation is very laggy. When I apply a very slow animation, it "only" laggs until the transition of the navigation has finished (or some milliseconds after that), whereas the last part of the scroll is quite smooth (this gives me some hope).
The .translate3d CSS option worked fine, but the page is "cut" so that I cannot scroll back after the transition has finished. This is more of a solution for full screen pages I guess.
I do not want to know how to implement an anchor that triggers scrolling, I am asking if someone knows a smooth way to implement scrolling (in my case for iOS). Meaning, I realise that I could have written the JS-Function more generic, but this is only test-code for now.
I have not tested this on android as my target device is iOS, but if this is a known issue only for iOS (e.g. it is working on android) I would be happy if you tell me.
JavaScript / jQuery:
$('#my-link').click(function (event) {
scrollTo('element'); /* hand crafted for now */
event.preventDefault();
});
function scrollTo (element) {
navToggle(); /* This toggles classes on various elements, triggering the respective element's transition */
$('html, body').animate({
'scrollTop': $('#'+element).offset().top /* I deleted the callback (on navToggle) and setTimeout code as it did not make a huge difference to the result */
}, 666);
}
HTML:
<!-- typed out by hand, if there are minor errors they are not related to the issue -->
<a href='#element' id='my-link'>Click to scroll</a>
...
<div id='element'>...</div>
Providing that code is actually just part of the etiquette as I dont look for concrete improvement on what I have written. However, this works like a charme on desktop browsers and if it helps some of you, the better. Also check out this question on how to implement anchor scrolling as it delivers great answers for desktop browsers!
It is late (or early) and my brain is a mess, but I hope I made my question clear (If not I will come back to edit it.): Is there a way to make a mobile browser (e.g. iOS' Safari) smooth scroll like a charme? If already solved, please point me in the right direction.
Thank's a lot and happy coding!

Problems with .scroll function in mobile browsers

I am trying to get a div to go from 100% opacity to 0% opacity on scroll.
I made this Fiddle and it works great in a web browser, just as I'd hope. It works in mobile browsers too, but with one horrible downside.
var divs = $('.cover_image');
$(window).on('scroll', function() {
var st = $(this).scrollTop();
divs.css({
'opacity': (1 - st / 40)
});
});
(What is happening in the fiddle is the top div is going to opacity:0 as you scroll, revealing another div below it with the same background-image, but blurred. Creating the impression the same image is blurring the more you scroll)
In a web browser as you scroll the div drops in opacity progressively with a fade like affect which is great.
However in a mobile browser the change of opacity doesn't take effect until you release your finger from the the screen. So there is no progressive change of opacity. It only makes the changes visually as you release your finger from the screen, not as you scroll.
Is there a solution for this? I have tried adding in scrolling touch to my css, but it doesn't make a difference.
-webkit-overflow-scrolling: touch
Scrolling distance on mobile works very different from desktop. Even if you detect each step in the touch event, this is only half the truth. When the user releases, the site will continue to scroll for a bit while deaccelerating. This is called momentum scroll and will in most cases not be picked up by the touch event.
There are to my knowledge no perfect solution to this, since different devices handle scroll and touch very differently. There are however a few options you could look into.
Scrolling libraries
There are libraries to help you solve this problem. For instance one called scrollability that emulates scrolling to work more consistently.
Scrollability adds a good imitation of iOS native scrolling to your
mobile web apps.
Scrollability is a single script, it's small, and it has no external
dependencies. Drop it into your page, add a few CSS classes to
scrollable elements, and scroll away.
Ignore the scroll completely
Don't look at the touch or scroll events. Instead use setInterval or requestAnimationFrame with desired frequency that reports the pages current position (document.documentElement.scrollTop) at all time. Base your animation on this value instead of scroll or touch events. You might want to limit this to touch devices since it's not needed for desktop.
Write your own scroll functionality
Disable scrolling and make your own, without for instance momentum scroll, that is suited for your needs. Note that the scroll event is usually disabled on desktop if you disable scroll, but mousewheel works. I have been down this path and I would not recommend it. Instead you should probably go with the library approach at the top.

JVectorMap Scroll Speed and Full screen Issue

I am using JVectorMap to create a map Page on my Website. However I found that the scrolling zoom speed is much to slow. How do I adjust the scroll speed? There is no documentation for this issue. I found this:
zoomStep: 1.6,
This is however to specify the zoom step for the buttons, not the scrolling.
Another issue that I have found is that I cannot set the height of the Map Container to:
window.innerHeight;
But i can however set the width to:
window.innerWidth;
How can I specify to Height of the Map container to be in relation to the Window size? I've also tried using a % value.
Thank you
this might be quite late answer, even might have been already answered somewhere.
But i got the same problem with mouse scroll speed and found the solution.
It can be fixed in js file in line 2382
zoomStep = Math.pow(1.003, event.deltaY);
You can easily change speed by changing that "1.003" value. More you make it - faster the scroll is.
I didnt fully understand the second problem of yours. But if it is about container height, I just use % values for the height. Make sure your parent has some height value as well.
Re. the scroll speed I agree that it is far too slow in 2.0.1.
I had a look into the js file and found some comments on line 234 - " If this is an older event and the delta is divisable by 120, then we are assuming that the browser is treating this as an older mouse wheel event and that we should divide the deltas by 40 to try and get a more usable deltaFactor. Side note, this actually impacts the reported scroll distance in older browsers and can cause scrolling to be slower than native. Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
On line 113 you will find the adjustOldDeltas set as true. I have set it as false but no change in the scroll speed.
Hopefully this works for you or gives you enough to explore the issue further.
W.

Scroll event on Android Browser not firing. Need workaround

I've created a website with a parallax street scene. See here for an archived version.
It works just fine on all major desktop browsers, and Safari Mobile. It works fine in Mobile Firefox and Chrome for Android Beta also. However the default Android browser has issues with the scroll event. Let me be clear. Scrolling is not the issue. The div scrolls as required. The scroll event doesn't fire. This issue I experience on Honeycomb as well as ICS.
I'm not concerned about other mobile browsers because for mobile screen sizes one usually does not see the parallax scene; mediaqueries and conditional JavaScript loading take care of that. Responsive design and all that jazz.
Basically, I've written a parallise() jQuery plugin that positions each image based on its position and 'depth'. This function is bound to the scroll event.
On Android Browser, this event only fires at the start of the next touch, instead of continuously.
OK, so I thought that perhaps if I bound the function to touchstart, touchmove, and touchend events I would solve my issue. No cigar. Other touch events are also bugged. Applying the suggested workaround causes the events to fire, but as I have to e.preventDefault(), scrolling (the whole point of the exercise) is disabled.
What if I just poll the position of the stage div relative to the window div? Turns out that the position information is only updated at the start of the next touch.
I am at the end of my tether. Any help would be much appreciated.
Even if the touch events worked correctly on the bugged versions of Android, and you were then effectively able to track the native scroll position during a drag, this would be prone to error. For example, it wouldn't account for the momentum animation which happens after the touching has finished.
iOS and Android make sacrifices to improve the performance of scrolling. On both platforms, it's not possible to get the accurate scroll position until the scroll has completed. The scroll event (on the <body>) doesn't fire until the momentum animation is finished. So while your original question is about scroll events on an overflowing <div>, fixing this might not be totally helpful for you anyway.
If you want an animation to update in time with the scroll, then you need to perform the scroll programatically rather than using the browser's native scroll. The best library to do this is iScroll. You can achieve parallax effects very easily as seen in this demo.
If you need more complex effects (the walking character, in your example), you can opt for the "probe" version of iScroll which allows pixel-perfect polling of scroll position in return for reduced performance.
However, there are many downsides to using iScroll:
You may need to change your markup and styling
It is unnecessary overhead for desktop browsers, but due to markup changes may be difficult to use only as a fallback
The scrolling will not feel perfect - on iOS, with its usually excellent scrolling performance - the slight difference in momentum calculation can feel jarring. On Android, the scrolling can become more laggy than usual.
Swipe shim that doesn't need preventdefault on touchstart: https://github.com/TNT-RoX/android-swipe-shim

Categories