I want to make a full page scroll effect like this site or this site (demo here).
It seems that divs are moving in the different speed or kind of some acceleration so that the later div may scroll first and the div before may scroll later, and makes the later div becomes like a mask that will hide the before div for a little while.
I tried to use scrollIntoView with smooth behavior like this:
const content = document.querySelectorAll('section');
let index = 0;
document.addEventListener('wheel', event => {
var delta = event.wheelDelta;
if (delta < 0) {
index++;
content.forEach((section, i) => {
if (i === index) {
toggleText(i, 'show');
section.scrollIntoView({behavior: "smooth"});
}
})
} else {
index--;
content.forEach((section, i) => {
if (i === index) {
toggleText(i, 'show');
section.scrollIntoView({behavior: "smooth"});
}
})
}
})
Now when scroll event fires, each section moves in the same speed, and the later section cannot hide the before section for a little while.
How to implement this "mask" like effect?
Easiest way to do this would just to add delays after clicking next or prev
var delay = 2500; //2.5 second
setTimeout(function() {
//your code here
}, delay);
Your code will execute in 2.5 seconds.
Related
I have this function that translates the element based on the scroll, but I'm using it on several elements, some at the beginning, others at the very end of the page. The problem is that those bottom elements start having their translates movment at the very first scroll, so when the users reaches the end of the page, those elements have been overly moved.
So I tried using the IntersectionObserver to trigger the translate action only when the element is visible. It worked, but the translate calculation still takes the total page scroll to calculate the translate value, so the bottom page elements goes way beyound anyway.
How can I make this scrollY calculation be based only at the scroll made above the element section?
Here's the script:
<script>
function stickyModule() {
let el = document.querySelectorAll('.<?$=elements?>');
el.forEach(function (module) {
function moduleAnimation() {
console.log('scroll event');
}
new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
console.log('entry', entry.isIntersecting)
if (entry.isIntersecting) {
window.addEventListener('scroll', moduleAnimation, true);
let value = window.scrollY;
document.querySelector('.<?=$element?>').style.transform = `translatey(` + value * <?=$value?> + `px)`;
} else {
window.removeEventListener('scroll', moduleAnimation, true);
console.log('else');
}
});
}).observe(module);
})
}
document.addEventListener('DOMContentLoaded', stickyModule);
</script>
I've tried changing let value = window.scrollY; to entry.scrollY; or entries.scrollY, but no value is returned.
Context
I am working on a one-page website where the fixed navigation's class changes as it scrolls through the different sections in order to match the section's background color. To achieve this effect, I used and modified the 2nd solution listed here.
Issue
While it works great most of the time, the navigation code breaks when I resize the browser (or leave the page and click back). More specifically, the navigation's background color changes too early or too late and no longer matches the section's background.
I'm guessing that this happens because the section's height are calculated on page load. Ideally, they would be recalculated on every scroll - but I am a novice and that's just a guess. Any help to solve this issue would be appreciated.
JavaScript
FYI: there are four sections in the websites (Hero, Work, About, Contact). Navigation's bg color should be transparent in Hero, white in Work and Contact, and teal in About.
var afterhero = $('#hero-section').offset().top + $('#hero-section').height();
var afterwork = afterhero + $('#work-section').height();
var afterabout = afterwebsites + $('#about-section').height();
$(window).on('scroll', function() {
stop = Math.round($(window).scrollTop());
if (stop > afterabout) {
$('header').removeClass('teal');
$('header').addClass('white');
} else if (stop > afterwork) {
$('header').addClass('teal');
} else if (stop > afterhero) {
$('header').removeClass('teal');
$('header').addClass('white');
} else {
$('header').removeClass('teal');
$('header').removeClass('white');
}
});
Just try adding all your size variables into your scroll event handler:
$(window).on('scroll', function() {
var afterhero = $('#hero-section').offset().top + $('#hero-section').height();
var afterwork = afterhero + $('#work-section').height();
var afterabout = afterwebsites + $('#about-section').height();
stop = Math.round($(window).scrollTop());
if (stop > afterabout) {
$('header').removeClass('teal');
$('header').addClass('white');
} else if (stop > afterwork) {
$('header').addClass('teal');
} else if (stop > afterhero) {
$('header').removeClass('teal');
$('header').addClass('white');
} else {
$('header').removeClass('teal');
$('header').removeClass('white');
}
});
Now afterhero, afterwork and afterabout should all be recalculated on a page scroll.
The general idea to the site i am designing is to scroll through a set of menu items horizontally and incrementally underneath a static div that will magnify(increase dimensions and pt size) the contents of a menu items. I don't really need help with the magnify portion because i think it's as simple as adding a mag class to any of the menuItem divs that go underneath the static div. I have been messing with this for a few weeks and the code I have for incrementally scrolling, so far, is this:
$(document).ready(function () {
currentScrollPos = $('#scrollableDiv').scrollTop(120); //sets default scroll pos
/*The incrementScroll function is passed arguments currentScrollPos and UserScroll which are variables that i have initiated earlier in the program, and then initiates a for loop.
-The first statement sets up the variables: nextScrollPos as equal to the currentScrollPos(which by default is 120px) plus 240px(the distance to next menuItem), prevScrollPos as equal to the currentScrollPos(which by default is 120px) minus 240px(the distance to next menuItem).
-The second Statement checks to see if the user has scrolled using var userScroll
-The third statement sets: var CurrentScroll equal to the new scroll position and var userScroll to false*/
function incrementScroll(currentScrollPos, userScroll) {
for (var nextScrollPos = parseInt(currentScrollPos + 240, 10),
prevScrollPos = parseInt(currentScrollPos - 240, 10); //end first statement
userScroll == 'true'; console.log('dude'), //end second statement and begining of third
currentScrollPos = scrollTop(), userScroll = 'false') {
if (scrollTop() < currentScrollPos) {
$('#scrollableDiv').animate({
scrollTop: (parseInt(prevScrollPos, 10))
}, 200);
console.log('scrolln up')
} else if (scrollTop() > currentScrollPos) {
$('#scrollableDiv').animate({
scrollTop: (parseInt(nextScrollPos, 10))
}, 200);
console.log('scrolln down')//fire when
}
}
}
$('#scrollableDiv').scroll(function () {
userScroll = 'true';
_.debounce(incrementScroll, 200); //controls the amount of times the incrementScroll function is called
console.log('straight scrolln')
});
});
I have found a variety of solutions that are nigh close: such as a plugin that snaps to the next or previous div horizontally demo, another solution that also snaps and is based on setTimeout demo, but nothing that nails incrementally scrolling through divs. I also found a way to control the rate at which a user may scroll through the menuItems using debounce which is included in the above code.
The console.logs inside the loop do not fire when I demo the code in jsfiddle which leads me to believe the problem lies within the loop. I'm a noob though so it could be in syntax or anywhere else in the code for that matter. Also in the second demo, i have provided the css for the horizontal static div, but the moment I put it in my html it keeps the js from working.
I would like to write the code instead of using a plugin and any help would be appreciated! Also, thank you ahead of time!
Try this fiddle. Menu container height is 960px to show 4 menu items. "Zoom" div is positioned absolutely at top. When you scroll mouse over this div, menu items shifts to top/bottom. I had to add additional div to bottom to be able to scroll to last 3 menu items. JS code:
jQuery(document).ready(function($){
var current = 0;
var menu = $('.menu-container').scrollTop(0);
var items = menu.find('.menu-item');
var zoom = $('.zoom');
function isVerticalScroll(event){
var e = event.originalEvent;
if (e.axis && e.axis === e.HORIZONTAL_AXIS)
return false;
if (e.wheelDeltaX)
return false;
return true;
}
function handleMouseScroll(event){
if(isVerticalScroll(event)){
var delta = event.originalEvent.wheelDelta * -1 || event.originalEvent.detail;
current += (delta > 0 ? 1 : -1);
if(current < 0)
current = 0;
if(current >= items.length){
current = items.length - 1;
}
menu.stop().animate({
"scrollTop": current * 240
}, 300);
items.removeClass('current').eq(current).addClass('current');
event && event.preventDefault();
return false;
}
}
zoom.on({
"MozMousePixelScroll": handleMouseScroll,
"mousewheel": handleMouseScroll
});
});
Hope it will help.
Is there a way to get elements which is:
Inside a div with overflow: scroll
Is in viewport
Just like the following picture, where active div (5,6,7,8,9) is orange, and the others is green (1-4 and >10) :
I just want the mousewheel event to add "active" class to div 5,6,7,8,9 (currently in viewport). View my JSFiddle
$('.wrapper').bind('mousewheel', function (e) {
//addClass 'active' here
});
You could do something like this. I would have re-factored it, but only to show the concept.
Firstly I would attach this to scroll event and not mousewheel. There are those among us that likes to use keyboard for scrolling, and you also have the case of dragging the scrollbar. ;) You also have the case of touch devices.
Note that with this I have set overflow:auto; on wrapper, thus no bottom scroll-bar.
With bottom scrollbar you would either have to live with it becoming tagged as in-view a tad to early, or tumble into the world of doing a cross-browser calculating of IE's clientHeight. But the code should hopefully be OK as a starter.
»»Fiddle««
function isView(wrp, elm)
{
var wrpH = $(wrp).height(),
elmH = $(elm).height(),
elmT = $(elm).offset().top;
return elmT >= 0 &&
elmT + elmH < wrpH;
}
$('.wrapper').bind('scroll', function (e) {
$('div.box').each(function(i, e) {
if (isView(".wrapper", this)) {
$(this).addClass('active');
} else {
$(this).removeClass('active');
}
});
});
Note that you should likely refactor in such a way that .wrapper height is only retrieved once per invocation, or if it is static, at page load etc.
Update; a modified version of isView(). Taking position of container into account. This time looking at dolphins in the pool.
»»Fiddle««
function isView(pool, dolphin) {
var poolT = pool.offset().top,
poolH = pool.height(),
dolpH = dolphin.height(),
dolpT = dolphin.offset().top - poolT;
return dolpT >= 0 && dolpT + dolpH <= poolH;
}
I'm currently using a combination of smooth scroll and IDs/anchor tags to scroll to content on my site. The code below is getting the ID of the next 'section' in the DOM, and adding it's ID as the 'view next section' href, so once it's clicked, it'll scroll to the top of that div. Then, it iterates through, updating the href with the next ID each time etc until the last section is seen and it scrolls back to the top. Pretty straightforward.
The only problem is that the 'sections' are fullscreen images, so as it's scrolling to the top of the next section, if you resize the browser, the top position of that section (where we scrolled to) has moved, and means the position is lost.
I've created a JSFiddle. You can see this happening after you click the arrow to visit the next section then resize the window: http://jsfiddle.net/WFQ9t/3/
I'm wanting to keep this top position fixed at all times so even if you resize the browser, the scroll position is updated to reflect this.
Thanks in advance,
R
var firstSectionID = $('body .each-section').eq(1).attr('id');
$('.next-section').attr('href', '#' + firstSectionID);
var i = 1;
$('.next-section').click(function() {
var nextSectionID = $('body .each-section').eq(i).attr('id');
i++;
$('.next-section').attr('href', '#' + nextSectionID);
var numberOfSections = $('body .each-section').length;
var lastSectionID = $('body .each-section').eq(numberOfSections).attr('id');
if ($('.next-section').attr('href') == '#' + lastSectionID ) {
$('.next-section').attr('href', '#introduction');
i = 1;
}
});
Ok, Please check out this fiddle: http://jsfiddle.net/WFQ9t/9/
The few things I did were:
Made some global variables to handle the screen number (which screen you're on and also the initial window height. You will use this when the screen loads and also when you click on the .next-session arrow.
var initWinHeight = $(window).height();
var numSection = 0;
Then I tossed those variables into your resizeContent() function
resizeContent(initWinHeight, numSection)
so that it will work on load and resize
I made the body move around where it needs to, to accomodate for the movement of the divs (I still don't understand what divs are moving when the regular animation happens).
$('body').css({
top: (((windowHeight - initWinHeight)*numSection)*-1) + "px"
});
Then in your click function, I add 1 to the section number, reset the initial window height and then also reset the body to top:0. The normal animation you have already puts the next section at the top of the page.
numSection++;
initWinHeight = $(window).height();
$('body').css({top:"0px"}, 1000);
Finally, I reset the numSections counter when you reach the last page (You might have to make this 0 instead of 1)
numSection = 0;
The fiddle has all of this in the correct places, these are just the steps I took to change the code.
Here is a solution that i found, but I dont use anchor links at this point, i use classes
Here is my HTML code:
<section class="section">
Section 1
</section>
<section class="section">
Section 2
</section>
<section class="section">
Section 3
</section>
<section class="section">
Section 4
</section>
And here is my jQuery/Javascript code,
I actually used a preety simple way:
$('.section').first().addClass('active');
/* handle the mousewheel event together with
DOMMouseScroll to work on cross browser */
$(document).on('mousewheel DOMMouseScroll', function (e) {
e.preventDefault();//prevent the default mousewheel scrolling
var active = $('.section.active');
//get the delta to determine the mousewheel scrol UP and DOWN
var delta = e.originalEvent.detail < 0 || e.originalEvent.wheelDelta > 0 ? 1 : -1;
//if the delta value is negative, the user is scrolling down
if (delta < 0) {
next = active.next();
//check if the next section exist and animate the anchoring
if (next.hasClass('section')) {
var timer = setTimeout(function () {
$('body, html').animate({
scrollTop: next.offset().top
}, 800);
next.addClass('active')
.siblings().removeClass('active');
clearTimeout(timer);
}, 200);
}
} else {
prev = active.prev();
if (prev.length) {
var timer = setTimeout(function () {
$('body, html').animate({
scrollTop: prev.offset().top
}, 800);
prev.addClass('active')
.siblings().removeClass('active');
clearTimeout(timer);
}, 200);
}
}
});
/*THE SIMPLE SOLUTION*/
$(window).resize(function(){
var active = $('.section.active')
$('body, html').animate({
scrollTop: active.offset().top
}, 10);
});