Scroll snapping to a page section - javascript

So I have two sections of content near the top of my page and I’d like for users who have scrolled down to near the top of the second section to get “scroll snapped” to the top of the second one once they have stopped scrolling.
I think it should be possible using jQuery but I haven’t been able to figure it out. Here are my examples:
Without my attempt: http://codepen.io/jifarris/pen/gaVgBp
With my broken attempt: http://codepen.io/jifarris/pen/gaVgQp
Basically I can’t figure out how to make it try scrolling to the spot only once, after scrolling has stopped. It’s kind of just freaking out.
I love how the recently introduced scroll snap points CSS feature handles scroll snapping and I’d almost prefer to use it – for the browsers that support it, at least – but it seems like it only works for items that take up 100% of the viewport height or width, and it seems like it’s for scrolling within an element, not the page itself.
The top section has a fixed height, so this really can be handled with pixel numbers.
And for reference, here’s the heart of the code from my attempt:
$(function() {
$(document).on('scroll', function() {
var top = $(document).scrollTop();
if (top > 255 && top < 455) {
$('html, body').animate({scrollTop: '356'}, 500);
$('body').addClass('hotzone');
} else {
$('body').removeClass('hotzone');
}
});
});

KQI's answer contains most of the steps required to create a well functioning section-scroll for use in your application/webpage.
However, if you'd just want to experiment yourself, developing your script further, the first thing you'll have to do is add a timeout handler. Otherwise your logic, and therefor scrollAnimation, will trigger every single pixel scrolled and create a buggy bouncing effect.
I have provided a working example based on your script here:
http://codepen.io/anon/pen/QjepRZ?editors=001
$(function() {
var timeout;
$(document).on('scroll', function() {
clearTimeout(timeout);
timeout = setTimeout(function() {
var top = $(document).scrollTop();
if (top > 255 && top < 455) {
$('body').animate({
scrollTop: '356'
}, 500);
$('body').addClass('hotzone');
} else {
$('body').removeClass('hotzone');
}
}, 50);
});
});
Good luck!

All right, there are couple of things you gonna have to deal with to get a good result: which are performance, call stack queue, easing.
Performance wise you should drop jQuery animate and use VelocityJs which gives a smoother transition, better frame per second (fps) to avoid screen glitches especially on mobiles.
Call stack: you should wrap whatever logic you have to animate the scrolltop with 'debounce' function, set the delay for let say 500mm and check the scrolling behavior. Just so you know, the 'scroll' listener your using is firing on each pixel change and your script will go crazy and erratic. (It is just gonna be a moment of so many calc at the same time. Debounce will fix that for you)
Easing: make the transition looks cool not just dry snappy movement.
Remember, 'easing' with Velocity starts with 'mina.' i.e.
'Mina.easingFnName'
Finally, your logic could be right, i am in my phone now cannot debug it but try to simplify it and work with a single problem at once, be like i.e.
If ( top > 380 ) // debounce(...)

Related

Run jQuery/Javascript Function w/GSAP only once?

I have a function that scrolls down/snaps to a div after the user starts to scroll (to avoid them having to manually scroll down to where the main content starts). I have used some GSAP to achieve this, however I don't think that will make much difference with the solution to this problem. However, while this function does exactly what I'd like, It won't then allow the user to scroll back up. It just glitches a little bit, as if I'm fighting with the function to scroll back up. I've tried a variety of answers from all the "run only once questions" on here, but none seem to work in this situation.
I want to basically kill the function after the first time it is initiated, or to tweak the function to only work on the first scroll at the top of the page (though I appreciate this would be much more complicated).
Thank you in advance of any help.
JS:
$(window).scroll(function () {
function headScrollToDiv() {
var dHeight = $(window).height();
if (dHeight >= $(this).scrollTop()) {
gsap.to(window, 0.4, { scrollTo: ".buffer" });
}
}
headScrollToDiv();
});

window scroll method flickers in IE

This may come as a huge surprise to some people but I am having an issue with the IE browser when I am using the $(window).scroll method.
My goal:
I would like to have the menu located on the left retain it's position until the scroll reaches > y value. It will then fix itself to the top of the page until the scroll returns to a < y value.
My error:
Everything seems just fine in Chrome and Firefox but when I go to Internet Explorer it would seem the browser is moving #scroller every time the scroll value changes, this is causing a moving/flickering event.
If someone could point me to a resource or give me a workaround for this I would be very grateful!
Here is a fiddle:
http://jsfiddle.net/CampbeII/nLK7j/
Here is a link to the site in dev:
http://squ4reone.com/domains/ottawakaraoke/Squ4reone/responsive/index.php
My script:
$(window).scroll(function () {
var navigation = $(window).scrollTop();
if (navigation > 400) {
$('#scroller').css('top',navigation - 220);
} else {
$('#scroller').css('top',183);
$('#scroller').css('position','relative');
}
});
You might want to take a look at the jQuery Waypoints plugin, it lets you do sticky elements like this and a lot more.
If you want to stick with your current method, like the other answers have indicated you should toggle fixed positioning instead of updating the .top attribute in every scroll event. However, I would also introduce a flag to track whether or not it is currently stuck, this way you are only updating the position and top attributes when it actually make the transition instead of every scroll event. Interacting with the DOM is computationally expensive, this will take a lot of load off of the layout engine and should make things even smoother.
http://jsfiddle.net/WYNcj/6/
$(function () {
var stuck = false,
stickAt = $('#scroller').offset().top;
$(window).scroll(function () {
var scrollTop = $(window).scrollTop();
if (!stuck && scrollTop > stickAt) {
$('#scroller').css('top', 0);
$('#scroller').css('position','fixed');
stuck = true;
} else if (stuck && scrollTop < stickAt) {
$('#scroller').css('top', stickAt);
$('#scroller').css('position','absolute');
stuck = false;
}
});
});
Update
Switching the #scroller from relative to fixed removes it from the normal flow of the page, this can have unintended consequences for the layout as it re-flows without the missing block. If you change #scroller to use an absolute position it will be removed from the normal flow and will no longer cause these side-effects. I've updated the above example and the linked jsfiddle to reflect the changes to the JS/CSS.
I also changed the way that stickAt is calculated as well, it uses .offset() to find the exact position of the top of #scoller instead of relying on the CSS top value.
Instead of setting the top distance at each scroll event, please consider only switching between a fixed position and an absolute or relative position.All browsers will appreciate and Especially IE.
So you still listen to scroll but you now keep a state flag out of the scroll handler and simply evaluate if it has to switch between display types.
That is so much more optimized and IE likes it.
I can get flickers in Chrome as well if I scroll very quickly. Instead of updating the top position on scroll, instead used the fixed position for your element once the page has scrolled below the threshold. Take a look at the updated fiddle: http://jsfiddle.net/nLK7j/2/

Improving Performance on Background Parallax Scrolling

Hello StackOverflow Community,
what I am trying to achieve is a header that can be moved with the mouse.
You klick into the header and drag the mouse and the elements inside the header will move with different speeds.
I achieved the parallaxing part but the performance is not really good. It is partially a bit laggy while dragging the backgrounds.
My question now is: what can be changed in the code to get a performance boost?
That's the part of the code that takes care of parallaxing. On every mousemove a each loop is executed which I think is the reason for the performance beeing so laggy:
var dragging = false;
var clickMouseX;
//Our object for the layers
//each layer has a different scrolling speed
var movingObjects = {
'#header-l1' : {'speed': 1},
'#header-l2' : {'speed': 1.4},
'#header-l3' : {'speed': 1.85},
'#header-l4' : {'speed': 2.2},
};
$('#header-wrapper').mousedown(function(e){
dragging = true;
//Get initial mouse position when clicked
clickMouseX = e.pageX;
$(this).mousemove(function(mme){
//execute only if mousedown
if(dragging){
//iterate through all layers which have to be parallaxed
$.each(movingObjects, function(el, opt){
var element = $(el);
//get difference of initial mouse position and current mouse position
var diff = clickMouseX - mme.pageX;
//scroll-position left speed 1
if(diff < 0) diff = -1;
//scroll position right speed 1
if(diff >= 0) diff = 1;
//get current position of layer
currLeft = parseInt(element.css('left'));
//get current layer width
elWidth = element.width();
//if right border is reached don't scroll further
if(currLeft < -(elWidth - 810)){
element.css('left', -(elWidth - 810));
}
//so do with left border
if(currLeft > 0){
element.css('left', 0);
}
//parallax it! Subtract the scroll position speed multiplied by the speed of the desired
//layer from the current left property
element.css('left', parseInt(element.css('left')) - diff*opt.speed);
});
}
});
/* Cursor */
$(this).css('cursor', 'pointer');
return false;
});
I also put a fiddle up:
http://jsfiddle.net/yWGDz/
Thanks in advance,
Thomas
P.S. maybe someone even finds out why layer two and three have the same scroll speed while having different speeds defined.
I worked at this a bit, and came up with this: http://jsfiddle.net/amqER/2/
This works a lot faster than the original (especially in firefox, where it performs a whole lot better, chrome it's still pretty slow). I also changed up some of the logic in your code, to make it make more sense.
A list of things that I did:
Minify your pngs
2 of your png files were over 2 megs, so I threw them into a png compressor (tinypng) and that reduced the size a lot. This helps with loading time and overall snappiness.
Re-use values as much as possible
In your original code, you wrote to and then subsequently read from the css left property a couple times in your code. Doing this is going to make it a lot slower. Instead, I kept an left property, and would only touch $.css when I absolutely needed to. Likewise for reading each element's width each update.
Also, like I said, I modified your logic to (I think) make more sense, given what you were trying to accomplish. It calculates a new diff each update, and tries to move according to that. Also, it doesn't try to keep moving once one of the images falls off (which yours does if you move all the way to the right, and it looks really weird). You can also look at this: http://jsfiddle.net/amqER/5/, which maybe is more like the control scheme you wanted.
Just some quick performance tips.
Try not to use $(this).mousemove instead save $(this) into a variable and use that.
var th = $(this);
th.mousemove...
Try to avoid using $.each. This is probably the part that's slowing your code down.
You can replace it with a for loop, but I would suggest, in this case, sending in each element one by one.
var parallax = function(img){
};
parallax(img1);
parallax(img2);
instantly-increase-your-jquery-performance
Whilst Xymostech's answer does greatly improve upon the original poster's original code; the performance is hardly improved for me in Chrome.
Whilst inspecting the page FPS, the solution posted here runs at 15FPS for me on a Retina MacBook Pro.
I made a very simple change to the code, altering it to use translate3d properties instead of left. Now, it runs at 55-60 FPS for me. I'd call that a massive performance boost.
If 'show paint rectangles' are turned on in Chrome, you'll see the previously posted solution is continually painting changes to the dom whilst the parallax is in motion. With the translate3d solution, there's simply zero painting done the whole time the parallax is in motion.
http://jsfiddle.net/LG47e/

scrolling effect

I'm trying to do something like this.
http://www.mini.jp/event_campaign/big-point/
What I can't figure out is how to make the animation happen based on the scroll when the scroll hits a specific position. I have similar blocks of content that I only want to animate parts of it based on the scroll and when the block is within the browsers view area when scrolling.
I understand using the scroll event to get the scrollTop position I'm more concerned with how everything else would work.
$(window).bind('scroll',function(e){
var scrolledY = $(window).scrollTop();
});
Anyone can help explain some of this.
Thanks
Just like what MiniGod said in the comment, look in to the source code (animate.js), and you can see that they have recorded all the "scenes" and all other things like alpha and pos for everything.
// scene 1
{
scene:"#scene1",
name:".car",
runStatus:[
{p:10,pos:true,x:275,y:240,alpha:true,opacity:1,scale:true,orgSize:[475,270],scaleSize:1},
{p:180,pos:true,x:275,y:200,alpha:true,opacity:1,scale:true,orgSize:[475,270],scaleSize:1},
{p:270,pos:true,x:275,y:140,alpha:true,opacity:1,scale:true,orgSize:[475,270],scaleSize:1},
{p:500,pos:true,x:275,y:-300,alpha:true,opacity:0,scale:true,orgSize:[475,270],scaleSize:1}
]
}

How to solve element flicker while scrolling with parallax effect in JavaScript?

I am trying to re-create website with parallax effect using JavaScript. That means that I have two or more layers, that are moving different speeds while scrolling.
In my case I'm moving only one layer, the other one remains static:
layer 1 = website text;
layer 2 = element background;
for this I'm using simple source code (with jQuery 1.6.4):
var docwindow = $(window);
function newpos(pos, adjust, ratio){
return ((pos - adjust) * ratio) + "px";
}
function move(){
var pos = docwindow.scrollTop();
element.css({'top' : newpos(pos, 0, 0.5)});
}
$(window).scroll(function(){
move();
});
The Problem:
- All calculations are done right and the effect "works" as expected. But there is some performance glitch under some browsers (Chrome MAC/Windows, Opera MAC, IE, paradoxically not Safari).
What do I see during scrolling?
- While scrolling the background moves in one direction together with scroll, but it seems to occasionally jump few pixels back and then forth, which creates very disturbing effect (not fluid).
Solutions that I tried:
- adding a timer to limit scroll events
- using .animate() method with short duration instead of .css() method.
I've also observed, that the animation is smooth when using .scrollTo method (scrollTo jQuery plugin). So I suspect that there is something wrong with firing scroll events (too fast).
Have you observed the same behavior?
Do you know, how to fix it?
Can you post a better solution?
Thanks for all responses
EDIT #1:
Here you can find jsfiddle demonstration (with timer): http://jsfiddle.net/4h9Ye/1/
I think you should be using scrollTop() instead and change the background position to fixed. The problem is that setting it to absolute will make it move by default when you scroll up or down.
The flicker occurs because the position is updated as part of the default browser scroll and updated again as part of your script. This will render both positions instead of just the one you want. With fixed, the background will never move unless you tell it so.
I've created a demo for you at http://jsfiddle.net/4h9Ye/2/ .

Categories

Resources