I'm trying to create a smooth scrolling effect using javascript library react. I don't want to use JQuery.
I want to archive when user clicks on a link that he goes to appropriate section of the page.
My handleScroll function is as follows :
handleScroll(ref, offsets, event){
event.preventDefault();
let offset = offsets[ref]; // this is where it needs to scroll for example 900
let activeLinks = {};
activeLinks[ref] = "active";
let start = new Date().getTime();
let time = 4000;
let offsetTo = this.state.offsetTo; //offsetTo is 0 on page load
let timer = setInterval(function() {
let step = Math.min(1,(new Date().getTime()-start)/time);
offsetTo = offsetTo + 40;
if(offsetTo >= offset) {
return;
}
window.scrollTo(0, offsetTo);
if( step == 1) clearInterval(timer);
},10);
this.setState({activeLinks: activeLinks}, () => timer);
}
This works pretty fine but only if I'm top of the page. When I click on the link it goes to the appropriate section with an animation.
But when I then click on some other link it works, but it starts from the top and not from the place where I am.
I need to find a way to set the offsetTo to the appropriate value, and then depending on it to go to the top or to the bottom.
Any idea how can I do that?
https://github.com/fisshy/react-scroll should do the trick.
see the examples, it's pretty straight forward.
Related
So I have worked hard to get this code correct thus far. Basically my click event makes my shapes DECAY gradually start being affected. It works perfectly as I wanted. But my question is when I let go of holding down my mouse or finger it automatically jumps back to the original frame. Can I please get some help on how to make it gradually go back (or gradually end) like how it starts? that way its a fluid animation from start to finish.
Here's my Click event code
decaybackup = config.shader.decay;
world.resize()
});
let interval='';
let myshape = document.getElementById('shapeId');
myshape.addEventListener('pointerdown', function(event) {
interval = setInterval(()=>{
config.shader.decay += .001;
},1)
});
myshape.addEventListener('pointerup', function(event) {
config.shader.decay = decaybackup;
clearInterval(interval)
interval = '';
});
also here is a read only link to my site if you need a visual of what im talking about and can also see any code I have added...
enter link description here
THANK YOU!!!
Use setInterval() to decrement decay back to decaybackup similar to the way you increment it during pointerdown.
let undecayInterval;
myshape.addEventListener('pointerup', function(event) {
config.shader.decay = decaybackup;
clearInterval(interval);
clearInterval(undecayInterval);
undecayInterval = setInterval(() => {
config.shader.decay -= 0.001;
if (config.share.decay <= decaybackup) {
clearInterval(undecayInterval);
}, 1);
}
});
I'm trying to add a simple counter in the bottom of my app like this one:
And it is very simple atm, 80 is my array.length that is being populated through my axios request.
<div>{people.length.toLocaleString()}</div>
And as I scroll down the page, using react-infinite-scroll, the number goes up and up and this is just fine. What I'm trying to do is subtract the number as the user goes back up the page.
Is this something harder than I'm thinking? If so, don't give me the full answer, just give me the path to follow. Thanks.
This is what I'm trying to accomplish: https://mkorostoff.github.io/hundred-thousand-faces/
you can do by using scroll event with window.innerHeight and the element bottom height to check whether its available inside the display window.
You can try like this using onscroll event which is available in library itself.
let counter = 0;
[listofElement].find(ele => {
var conditionHeight = window.innerHeight;
var cordinat = ele.getBoundingClientRect().top;
counter++;
return conditionHeight < cordinat;
});
You can check here with sample working part.
Looking at the source of the page you've linked, the code uses this function to get the size of the page:
function getScrollPercent() {
var face_width = document.getElementById('first').clientWidth;
var face_height = document.getElementById('first').clientHeight;
var body = document.documentElement || document.body;
var faces_per_row = Math.floor(main.clientWidth / face_width);
var total_height = total / faces_per_row * face_height;
var scroll_percent = (body.scrollTop - main.offsetTop + body.clientHeight) / total_height;
var count = Math.floor(scroll_percent * total);
var chunked_count = count - (count % faces_per_row);
if (chunked_count > 0) {
counter.classList = "fixed";
}
else {
counter.classList = "";
}
return (chunked_count > 0) ? chunked_count : 0;
}
The essential bit is var scroll_percent = (body.scrollTop - main.offsetTop + body.clientHeight) / total_height;. Basically, if you can calculate your total height (assuming that isn't infinite), then you can use body.clientHeight, +/- offsets, divided by totalHeight to figure out how far down the page you are. Call this from an event listener on scroll, and you should be good to go.
Incidentally, if this is the infinite scroll library you're talking about using, it's no longer maintained in favor of react-infinite-scroller.
using react-infinite-scroll, you can't back axios request results or remove generated doms.
The solution is calculating width and height of every doms and calculate offset.
Check how many doms are above the scrollReact and so so.
I have a comment box and I am using following JS to set scroll to bottom and for it scroll down when new message is posted.
window.setInterval(function() {
var elem = document.getElementById('Commentbox');
elem.scrollTop = elem.scrollHeight;
}, 500);
It kind of works, when a new message is posted it scrolls down, but when I scroll up to look at old messages it scrolls me back down. Is there a way to prevent from that from happening?
I wouldn't use an interval function to scroll down, cause you scroll every 500 millis with your implementation. I think you have a function, that adds new messages and is called on incoming messages:
function addMessage() {
// here you add the new message to DOM
// ...
// then you can scroll down once to show the new messages
var elem = document.getElementById('Commentbox');
elem.scrollTop = elem.scrollHeight;
}
If you post the code how you add your new messages, i can help you better.
Because the correct solution was not working for me, I did something like this:
$('.your-div-class').scrollTop($('.your-div-class').height()*$('.your-div-class').height());
How I came up with the idea?
First I found the height of the div I want to auto-scroll by writing in the console of the browser:
console.log($('.your-div-class').height());.
Then I found the scrollTop value:
console.log($('.your-div-class').scrollTop());.
Then I put this in the console:
$('.your-div-class').scrollTop($('.your-div-class').height()*$('.your-div-class').height());,
and found out that it only takes me half way down and realized,
$('.your-div-class').scrollTop($('.your-div-class').height()*$('.your-div-class').height());
would take me scrolled enough downwards.
If it doesn't work for you. You can use:
$('.your-div-class').scrollTop($('.your-div-class').height()*1000);.
This should work for you just fine.
Set the interval as a variable and then clear the interval when the scrolling down is completed, using window.clearInterval().
var timer = window.setInterval(function() {
var elem = document.getElementById('Commentbox');
elem.scrollTop = elem.scrollHeight;
window.clearInterval(timer);
}, 500);
#Wowsk has a good answer, but your follow up question sounds like you are looking for a slight difference in functionality. I think this should get you there, but I've not tested it yet.
var lastScrollTop = 0;
var elem = document.getElementById('Commentbox');
var timer = window.setInterval(function() {
elem.scrollTop = elem.scrollHeight;
lastScrollTop = elem.scrollTop
}, 500);
elem.addEventListener("scroll", function(){
if(lastScrollTop < elem.scrollTop){
window.clearInterval(timer);
}
}, false);
I just went ahead and tested it before posting, hope it helps - refer to the fiddle: https://jsfiddle.net/condorhauck/ak1L12pd/1/
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.
I have 9 links in my header and 10 divs in the body. The first div is the main page, the other 9 divs have diffrent content in them. What i want to do is when people mouseover the 9 links it shows the 1 of the 9 divs. But if the user stops using the mouse it needs to return to the first div after 5 minutes.
I hope somebody can help me to set this up.
How about something like this?
// call showPage('something') to switch to a different section
var currPage = 'main';
function showPage(id) {
if (currPage !== null) {
document.getElementById(currPage).style.display = 'none';
}
currPage = id;
document.getElementById(currPage).style.display = 'block';
}
var lastMove = new Date().getTime();
document.onmousemove = function() {
lastMove = new Date().getTime();
}
setInterval(function() {
var now = new Date().getTime();
if (now - lastMove > 300000) {
showPage('main');
}
}, 5000);
We keep a global lastMove variable that gets updated with the current timestamp every time the mouse moves.
Then we have a function that's called every 5 seconds that can do something if it's been 5 minutes since the last time the mouse moved.
This is an alternate solution that involves setting up a new timer every time the mouse moves.
function goToMain() {
// todo: switch back to Main
}
var timer = null;
document.onmousemove = function {
if (timer !== null) clearTimeout(timer);
timer = setTimeout(goToMain, 300000);
}
This is slightly cleaner code than my first solution, but it's doing more processing on every mouse move, so it may not be as efficient.
You can make all the divs with absolute position and just play with style.visibility switching it from "hidden" to "visible". As for 5 minutes and back to div 1 - you can make timer function and onMouseMove just reset counter.
<header>
<script type='text/javascript'>
var maxTime = 300;//sec=5min
var offTime = 0;
document.documentElement.onmousemove = function(){offTime=0};
function isTimeToReturn(){
offTime++;
if(offTime<=maxTime){
setTimeout('isTimeToReturn',1000);
}else{
//change your present div to hidden and first to visible
}
}
setTomeout('isTimeToReturn',1000);
</script>
</header>