I am using the window.scrollY event listener to get the scroll amount on my page and I set an animatino fram accordingly:
scroll = window.scrollY //inside the event listener
...
function animationLoop() {
...
scrollRatio = scroll/window.innerHeight
animMixer.setTime(scrollRatio) // every page is a "second"
...
}
This works wonderfully on the desktop, and also on iOS on the first scroll forward through the page. But as soon as the page bounces at the end, the scroll value seems to jump around unnecessarily, ie there is a mismatch between the position in the webpage and the animation frame. The timing is off until I scroll back to the very top of the page. After that the downward scrolling works fine again.
What happens to the scrollY value after bounce? I can't figure out where the problem is. I've tried things like pageYOffset but the problem remains. Also the value of the scrollRatio is clamped.
I know that there are some issues with the bouncy behavior of webpages on iOS, but these are negative values at the top of the page.
Thanks for any pointers.
Related
This question is for when using the mouse wheel on an external mouse, not the touchpad.
I'm working on a program that modifies the mouse's behavior.
I'm using window.scrollBy, but window.scroll or window.scrollTo or something else could also work.
I've included the code below for a test page that you could copy and paste.
It works fine for one scroll, but not for multiple scrolls at once.
One scroll of the mouse wheel scrolls smoothly up or down.
But when multiple scrolls are sent quickly, the page actually scrolls slower in a jerky motion.
The problem seems to be that before the initial scroll is finished, the next scroll interrupts it.
For example in the code I typed out below:
If you scroll slowly down 5 times, then quickly up 10 times, the page doesn't scroll back up to the top.
It makes me wonder how it works normally in Windows or Chrome OS or other operating systems.
I've tried to research this already but couldn't find a good explanation of how it normally works.
Normally multiple scrolls at once increase the scrolling smoothly, while the code below does not.
How does this normally work?
What code can be used to make the page scroll properly?
<html><head>
<title>Scroll Test</title>
<script>
window.addEventListener("mousewheel", processEvent, {passive: false});
function processEvent()
{
event.preventDefault();
var scrollAmount = event.deltaY < 0 ? -100 : 100;
window.scrollBy({left:0, top:scrollAmount, behavior:"smooth"});
}
</script>
</head><body align="center">
<b>Test Area:</b>
<p>
</body></html>
<script>
for (var i = 1; i <= 1000; i++) {document.body.innerHTML += i + "<br>";}
</script>
Either throttle the mousewheel/wheel event using something like lodash:
window.addEventListener("mousewheel", _.debounce(processEvent, 500), {passive: false});
Another thing to note, the window scrollBy function may not be as smooth a scroll as the GSAP ScrollTo plugin:
gsap.to(window, {duration: 2, scrollTo: 400});
Some potential solutions depending on if you're cool with external libraries! :)
I have 2 divs (left and right) and i want to scroll the left based on the right.
https://jsfiddle.net/3jdsazhg/2/
This works fine on desktop, but when i change to mobile, it's not smooth anymore...
This can be noticed very easily, by changing
_left.style.top = _content.scrollTop - (_content.scrollTop * ratioLeftRight) + 'px';
to
_left.style.top = _content.scrollTop + 'px';
Where it should act as a fixed positioned div
I would like to know the exact reason why this isn't smooth... I know that it's not the animation. Simple animation on the div is smooth, the issue comes up when it's based on scroll.
How can i make this animation smooth?
It's probably choppy because it's being fired ALOT when being scrolled, in fact i'm pretty sure IOS mobile pauses the javascript execution whilst the user is scrolling.
Instead I'd suggest using an interval, you could tweak the time between each interval to what feels good for your use-case.
Although it may seem intensive that it's firing this logic every X millisecond when using the scroll event you could be firing the event off hundreds of times per second, which is going to be far more intensive and noticeable to a user using a device with limit processing power.
(function () {
var interval = null,
//Currently set at 0.4 seconds, play with the code
//and change this value to see what works best for
//this use-case
time_between_interval = 400;
setInterval(scrollLogic, time_between_interval);
function scrollLogic () {
//The function body of what you're assigning
//to the scroll event.
}
//I have omitted clearing the interval but you would want to do that, perhaps on page change, or something.
//clearInterval(interval);
})();
I finally managed to think out a solution.
From my point of view, i'm guessing the mobile view fires the scroll event less often and because we are scrolling the wrapper, we first scroll the whole page and then scroll back with js the left part and because it's different from the desktop version, this issue becomes visible...
The solution was to change the left side to fixed position, and substract from the top instead of adding to it.
_left.style.top = -(_content.scrollTop * ratioLeftRight) + 'px';
There are a million similar questions but I can't find an answer that works for me.
Here is the situation:
I have an HTML page, and within that page is an element that I am using hammer.js on.
Need to be able to scroll like this:
--->
While also being able to pinch-to-zoom (and subsequently pan on that zoomed element) on the seating chart element above.
The element itself works perfectly. I'm using doubletap, pinch, pinchend, pan, and panend on it.
Now, in the event that the element is totally zoomed out (I'm keeping track of the scale for this reason), I would like the entire page to scroll when using it on a mobile browser (aka the finger will be dragging the page up).
I have tried almost everything under the sun at this point. I can't seem to get it to manually scroll to a specific position (I have tried setting window.scrollTop and using window.scrollTo() with no results).
If someone could point me in the right direction, I'll worship you and your family for the next...say....13 days. Heck, maybe even 14.
TL;DR
- Have we pinch zoomed on the element? If so, handle panning around that element with glee!
- Are we fully zoomed out / pinched out on the element? If so, mobile users should be able to scroll the page like normal!
Thanks
Chris
You may try window.scrollTo to "simulate" normal scroll. Like so:
var currentScroll = 0;
var currentScale = 1; //"fully zoomed out" state
hammer.on("panstart", function (ev) {
currentScroll = window.scrollY;
});
hammer.on("pan", function(ev) {
if (currentScale == 1) {
//abort pan and scroll window instead
window.scrollTo(0, currentScroll + ev.deltaY * -1);
return;
}
//do stuff with pan here...
});
I have a script that monitors scrolling and takes control of the scrolling to animate the page based on certain parameters. To do this, it calls window.scrollTo(0, currentScrollTop); which perfectly interrupts the smooth scrolling in Firefox on Windows. I can then animate the page scroll to the place where I want it.
Unfortunately, this trick doesn't appear to work in browsers in MacOS which results in a broken experience as JavaScript and the browser compete to scroll the window.
Is there a cross-browser way to stop smooth scrolling with JavaScript?
Site using effect in question: http://capitalismis.com
Relevant (simplified) code:
$doc.on('scroll', function(e)
{
$doc.off('scroll');
window.scrollTo(0, $doc.scrollTop());
var aniSpeed = 1500 * Math.abs(scrollTop - selected.top) / windowHeight;
$body
.stop()
.animate({scrollTop: selected.top}, aniSpeed, 'easeOutQuad');
}
);
In short: don't try to override native scrolling. Every OS and device handles things differently and it's impossible to predict the different scenarios. There is "hard scrolling" (most Windows versions), "soft scrolling" (≈Mac OS X 10.6+) and browsers that only fire the onscroll event when the scrolling is completely done (iOS). It's a mess.
Instead of trying to modify the scrolling behavior of the body, I would modify the elements of the page accordingly. Listen to the onscroll-event, and move things around on the web page.
// Capture scroll event
$(window).scroll( function() {
// Get scroll offset from top
var scrollTop = $(window).scrollTop();
// Use it to move elements around on the page (or change backgrounds etc.)
// Here: move .element in the opposite direction of the scroll
$('.element').css({
'-vendor-transform' : 'translate3d(' + (scrollTop*(-1)) + 'px,0,0)'
});
});
I have a content slideshow:
slide container
|--> wrapper
|------> slide1, slide2, etc.
that works as simple as calculating wrapper's position X and slide's position X to determine where to slide the wrapper for the next/previous slide to show up within container's viewport. It's pretty straight forward.
For Firefox and Chrome I am using CSS3 transform and transition-duration to animate the slides. Everything works perfect. Almost.
The problem is only when I click next button very fast. I am using jQuery's
$(selector).position().left
to read the slide's position X (left) and position becomes 0 (instead of expected, say, 300px). I do have a flag isAnimating to prevent users from going too fast but that does not help either. It does prevent from scrolling content too fast but position left may still be 0 as if something else is causing it to fail to determine.
I did a brief search and discovered that if it was image being loaded, some browsers would fail to determine its position until loading is over. However, each slide has an image but inside of it. The slide's CSS seems to have all widths and margins set fine.
The question is why may this be happening based on the scenario I described and possibly what can be improved to determine position X at all times for Firefox, Chrome browsers?
I've decided that if offsetLeft is not reliable for me at all times, I could use width of an element and its index position within container to figure out position X
var newWrapperPos = undefined;
$(lotWrapper).children('div').each(function(index){
if($(this).attr("id") === "slot"+id){
var width = $(this).width();
newWrapperPos = index * width;
return false;
}
});
//now I can shift wrapper to position = newWrapperPos
Sorry I couldn't share the code - it is a bit time consuming to rip off all pieces of functionality involved. But if somebody has a better answer, let me know. For now this works fine for me.