Smooth natural motion when page scroll - javascript

I am newbie in JS. Right now i am working on an effect in which i want when page scroll first time then the natural motion animation starts but it's creating a problem because when i scroll the element animation became fast.
Check this more you got the idea.
http://jsfiddle.net/byvLy/

i know that this is a swinging box (figured it out due to the Math.sin())
however, you have to note that scrolling event is fired every few milliseconds during scrolling. in your code, you are calling animate and creating an interval every time the scroll event is fired. that's why your animation is jumpy;
try this instead:
$(function() {
$(window).on('scroll', function() {
swing.start('.cloud1, .cloud2');
});
var swing = (function() {
var animated = false;
function startAnimation(selector) {
if (!animated) {
var banner = $(selector);
var start = 0;
animated = true;
window.setInterval(function() {
banner.css('left', 100 * Math.sin(start) + 80);
start += 0.1;
}, 30);
}
}
return {
start: startAnimation
}
}());
});​

Related

Scroll to a position on scroll event

I want to implement scrolling event function that forwards me to certain position on page like it's done in FullPage.js.
Example: https://alvarotrigo.com/fullPage
I tried adding onscroll event listener, which distinguishes scrolling direction and then executes scrollTo() , but it seems like a bad idea.
How do i implement this properly? Thanks in advance.
What i tried so far:
function throttle(fn, delay) {
let last;
let timer;
return () => {
const now = +new Date;
if (last && now < last + delay) {
clearTimeout(timer);
timer = setTimeout(() => {
last = now;
fn();
}, delay);
} else {
last = now;
fn();
}
};
}
var scrollPos = 0;
function smooth_scroll(){
// detects new state and compares it with the new one
if ((document.body.getBoundingClientRect()).top > scrollPos)
{
window.scrollTo({ top: 0, behavior: 'smooth' })
}
else
{
window.scrollTo({ top: 1000, behavior: 'smooth' })//as an example
}
// saves the new position for iteration.
scrollPos = (document.body.getBoundingClientRect()).top;
}
window.addEventListener('scroll', throttle(smooth_scroll, 1000));
I expect it to work like this: whenever i scroll down it forwards me to bottom (1000, 0), and when i scroll up it gets me to the top. All smoothly.
If you look onto your example website you can see that they are not scrolling a list. They are animating a wrapper and its children:
class: fullpage-wrapper
attribute they change: transform:translate3d(0px, -937px, 0px);
What happens ist that all elements are inside one wrapper which is then moved upwards the height of the box. The smooth effect of this page is provided by a specific easing which can be attached to the CSS animation.
So if you want to get the same "scrolling" effect you need to think about an animation solution. Therefore you can stick to blank CSS-Animations or Framework/Library specific ones.
For further informations about animation, I can try to help you as far as it's not getting a full Animation course :)

Custom buttons to replace browser scrolls -- hold and continuous scroll?

I have custom buttons that replaces the browser scrollbar. The idea is so that scrolling oversize elements in a page wouldn't result to a dozen scroll bar on a page.
See: https://jsfiddle.net/bwgxs6ng/
Since I must show some code sample (according to some SO error message), see this:
$('.right').on('click', function(event) {
var target = $(".image-container");
var current_x = target.scrollLeft();
if( target.length ) {
event.preventDefault();
$(target).animate({
scrollLeft: current_x+100
}, 500);
}
});
It's very simple, basically it takes current scroll position of the parent, and add x to it based on the direction that's clicked.
However, going further, I want it to imitate the hold and continuous scroll, but I'm not sure how to do it.
1) What is the mouse hold event called? (OK, this part is answered, it's called MouseDown as someone point out of the duplicate)
2) What is the continuous scrolling called, and how can I do something that'd imitate the browser's continuous scroll?
You can just call .animate() repeatedly (with easing set to linear, for smooth movement) inside your setInterval() callback. Just arrange for the interval to be equal to the animation duration, so that the next animation starts just when the previous one ends.
Or, better yet, make the interval shorter (say, 50 ms or less) and just call .prop() instead of .animate(), effectively performing your own animation. (This is how jQuery implements animation internally, anyway.)
Anyway, here's how I'd rewrite your code to support smooth continuous scrolling:
var speed_x = 0, speed_y = 0;
var timer = null;
var target = $(".image-container");
function scroll() {
if (speed_x == 0 && speed_y == 0) return;
var current_x = target.scrollLeft();
var current_y = target.scrollTop();
target.prop({
scrollLeft: current_x - speed_x,
scrollTop: current_y - speed_y
});
}
$('.control').on('mouseover mouseout', function (event) {
var $this = $(this);
var speed = (event.type == 'mouseover' ? 10 : 0)
if ($this.hasClass('left')) speed_x = +speed;
if ($this.hasClass('right')) speed_x = -speed;
if ($this.hasClass('up')) speed_y = +speed;
if ($this.hasClass('down')) speed_y = -speed;
}).on( 'mousedown', function () {
scroll();
if (timer !== null) clearInterval(timer);
timer = setInterval(scroll, 50);
return false;
});
$(document).on('mouseup', function () {
if (timer !== null) clearInterval(timer);
timer = null;
});
Note how the animation is started and stopped in the mousedown and mouseup handlers, but the direction of movement is set on mouseover and mouseout. This allows you to change the scrolling direction while holding the mouse down, by dragging the cursor from one edge to another.
(For bonus points, add divs with e.g. class="control up left" in the corners of the scroll area, so that holding the mouse down over those corners will allow you to scroll diagonally. The JS code above already supports it.)
you need to set an interval on mousedown, and clear the interval on mouseup, as done in this fiddle for left and right.
The relevant code change is that we removed the click event and replaced it with
$('.left').on('mousedown', function(event) {
... scroll code ...
interval = setInterval(function(){
... scroll code ...
},500);
})
.on('mouseup',function(){clearInterval(interval);});

Scroll to top of facebook messages

Is there some JavaScript code I can execute to scroll to the top of the messages box on Facebook? So when you click 'see all' and go the main message page, if you scroll up it loads more messages. I want to force the it to keep scrolling up to keep loading messages. I've tried
document.body.scrollTop = document.documentElement.scrollTop = 0;
But that of course only scrolls to the top of the actual page. Any ideas on how to scroll the messages box up?
Select a conversation and try this script (load it through the console):
var autoLoad = {
messagesContainer: document.querySelector('#contentArea [role=main] .uiScrollableAreaContent'),
start: function (speed) {
var messagesContainer = this.messagesContainer,
loadMore = document.querySelector('[role=log] .pam.uiBoxLightblue.uiMorePagerPrimary');
speed = parseInt(speed, 10) || 1000;
clearInterval(this.interval);
this.interval = setInterval(function () {
messagesContainer.style.top = '0px';
loadMore.click();
}, speed);
},
stop: function () {
clearInterval(this.interval);
this.messagesContainer.style.top = '';
}
};
To start it, type:
// Takes a 'speed' parameter, defaults to 1000 milliseconds
autoLoad.start(1200);
And to stop it (necessary for the scrollbar to re-appear):
autoLoad.stop();
Explanation:
After exploring Facebook's DOM, I found some selectors that specifically target the elements that are needed for this to work:
The messages container, which holds the messages
The 'load more' link, that triggers facebook's script in charge of loading more messages.
The messages container scrollable area doesn't use native scrolling, instead, it uses bottom and top to set it's current scroll position.
So, if you want to scroll to the top, you set the container to top: '0', and since this way, the messages auto-load AJAX only triggers once, you need to trigger it manually after every scroll to top. I managed to do this simply by executing click in the link that triggers the AJAX.
I tried to get the most specific classes/selectors that I could find, and the ones that sounded more general, since I don't know if Facebook generates ids/classes dynamically in some way.
I tested this under Firefox and Chrome, explore the code a bit and change it to fit your needs. I hope this works for you as is, otherwise, you can use the DOM explorer to find the appropriate selectors.
I had to tweak the script to use "scrollTop" instead of "style.top".
var autoLoad = {
messagesContainer: document.querySelector('#globalContainer [role=main] .uiScrollableAreaContent'),
start: function (speed) {
var messagesContainer = this.messagesContainer,
loadMore = document.querySelector('#js_d');
speed = parseInt(speed, 10) || 1000;
clearInterval(this.interval);
this.interval = setInterval(function () {
messagesContainer.scrollTop = 0;
loadMore.scrollTop = 0;
/* loadMore.click(); */
}, speed);
},
stop: function () {
clearInterval(this.interval);
this.messagesContainer.style.top = '';
}
};
Nov 2021 #user4698813 code update:
var autoLoad = {
messagesContainer: document.querySelector('[role=main]'),
start: function (speed) {
var messagesContainer = this.messagesContainer,
loadMore = document.querySelector('[data-release-focus-from]');
speed = parseInt(speed, 10) || 1000;
clearInterval(this.interval);
this.interval = setInterval(function () {
messagesContainer.scrollTop = 0;
loadMore.scrollTop = 0;
/* loadMore.click(); */
}, speed);
},
stop: function () {
clearInterval(this.interval);
this.messagesContainer.style.top = '';
}
};

jQuery Animation Triggered By User Interaction Depends on Previous Animation Completion

I have a timeline that can be zoomed by clicking a zoom in or zoom out button. This timeline doesn't all fit on the screen at once, so it is a scrollable div. When the user clicks to zoom, I want the position in the timeline to be the same, so I calculate a new scrollTop for the scrollable div. Here's a simplified version of what I'm doing:
var self = this;
...
this.zoomIn = function() {
var offset = $("#scrollable").scrollTop();
self.increaseZoomLevel(); // Assume this sets the correct zoom level
var newOffset = offset * self.zoomLevel();
$("#scrollable").scrollTop(newOffset);
};
This works fine. Now I'd like to animate the scrolling. This almost works:
var self = this;
...
this.zoomIn = function() {
var offset = $("#scrollable").scrollTop();
self.increaseZoomLevel(); // Assume this sets the correct zoom level
var newOffset = offset * self.zoomLevel();
$("#scrollable").animate({ scrollTop: newOffset });
};
It works if it's clicked once. However, if a second call to zoomIn happens while the animation is still running, the newOffset calculation is wrong because the offset is set to scrollTop() before scrollTop() is correct since the animation is still manipulating it.
I've tried to use jQuery's queue in various ways to make this calculation happen first, and that seems to work sometimes:
var self = this;
...
this.zoomIn = function() {
$("#scrollable").queue(function(next) {
var offset = $("#scrollable").scrollTop();
self.increaseZoomLevel(); // Assume this sets the correct zoom level
var newOffset = offset * self.zoomLevel();
next();
}).animate({ scrollTop: newOffset });
};
I think I'm just not understanding queue properly. How do I keep everything in order even when zoomIn is called repeatedly and rapidly? I want:
zoomIn x 2 clicks
to give me:
calculate 1 -> animate 1 start -> animate 1 finish -> calculate 2 -> animate 2 start -> animate 2 finish
and not
calculate 1 -> animate 1 start -> calculate 2 -> animate 1 finish -> animate 2 start -> animate 2 finish
Because then animate 2 is based on incorrect calculations.
Thanks!
Hm... what about: stop(true,true)? See: http://api.jquery.com/stop/
var self = this;
...
this.zoomIn = function() {
var offset = $("#scrollable").stop(true,true).scrollTop();
self.increaseZoomLevel(); // Assume this sets the correct zoom level
var newOffset = offset * self.zoomLevel();
$("#scrollable").animate({ scrollTop: newOffset });
};
Here's an implementation of #RobinJonsson's comment, which would be my proposed solution too, using a boolean to allow a new zoom action only after the previous animation is complete:
var self = this;
...
this.zooming = false;
this.zoomIn = function() {
if(!self.zooming){
self.zooming = true;
var offset = $("#scrollable").scrollTop();
self.increaseZoomLevel(); // Assume this sets the correct zoom level
var newOffset = offset * self.zoomLevel();
$("#scrollable").animate({ scrollTop: newOffset },function(){
self.zooming = false;
});
}
};
I very much appreciate the answers given. They both would work, but my unwritten requirements included animations that completed entirely as well as no loss of clicks. I know, I should have been more thorough in my question.
Anyway, I believe I have a solution that fits both of those requirements using jQuery queues. There were a couple of things I didn't realize about queues that I learned that got me going in the right direction. The biggest thing is this from the jQuery .animate docs:
When a custom queue name is used the animation does not automatically
start...
This allowed me to have complete control over the queue. I believe this is similar to (or maybe exactly what) #RobinJonsson's comment meant.
var top = 0;
var animating = false;
function calcAndAnimate(top) {
$("#block").queue("other", function() {
// Calculations go here
animating = true;
// This kicks off the next animation
$("#block").dequeue("other");
});
$("#block").animate({
top: top
}, {
duration: 2000,
queue: "other",
complete: function () {
animating = false;
// No need; it looks like animate dequeues for us, which makes sense.
// So the next calculation will be kicked off for us.
//$("#block").dequeue("other");
}
});
}
$("#queueButton").click(function() {
top += 20;
calcAndAnimate(top);
if (!animating) {
// Initial animation, need to kick it off
$("#block").dequeue("other");
}
});
There's a working example with log messages showing the enforced order at http://jsfiddle.net/cygnl7/6h3c2/3/

overflow: hidden; image scrollBy

I had an idea for like a bus window as a fixed frame, about 800px wide, with a parallax city with the content on billboards spaced out so when you scroll between them it allows the parallax to look like bus is moving. The content will be much bigger than the window like a sprite and I'll put forward and back buttons that will scrollBy (x amount, 0). I have a working parallax script and a rough cityscape of 3 layers that all work fine.
I have hit a wall. I am trying to clear a scrollBy animation after it scrolls 1000px. Then you click it again and it goes another 1000px. This is my function.
function scrollForward() {
window.scrollBy(5,0);
scrollLoop = setInterval('scrollForward()',10);
}
So far I can only clear it when it gets to 1000. I tried doing 1000 || 2000 ect but after the first one it goes really fast and won't clear.
Excelsior https://stackoverflow.com/users/66580/majid-fouladpour wrote a great piece of code for someone else with a different question. It wasn't quite right for what the other guy wanted but it is perfect for me.
function scrollForward() {
var scrolledSoFar = 0;
var scrollStep = 75;
var scrollEnd = 1000;
var timerID = setInterval(function() {
window.scrollBy(scrollStep, 0);
scrolledSoFar += scrollStep;
if( scrolledSoFar >= scrollEnd ) clearInterval(timerID);
}, 10);
}
function scrollBack() {
var scrolledSoFar = 0;
var scrollStep = -75;
var scrollEnd = -1000;
var timerID = setInterval(function() {
window.scrollBy(scrollStep, 0);
scrolledSoFar += scrollStep;
if( scrolledSoFar <= scrollEnd ) clearInterval(timerID);
}, 10);
}
Now for step two figuring out how to put this content animation behind a frame.
Not quite sure what your asking here. Perhaps you could provide more relevant code?
I do see a potential issue with your code. You call setInterval('scrollForward()', 10) which will cause scrollForward to be called every 10ms. However, each of those calls to scrollForward will create more intervals to scrollForward creating a sort of explosion of recursion. You probably want to use setTimeout or create your interval outside of this function.
Also, as an aside you can change your code to simply: setInterval(scrollForward, 10). Removing the quotes and the parens makes it a littler easier to read and manager. You can even put complex, lambda functions like:
setInterval(function() {
scrollForward();
// do something else
}, 10);
edit:
So if you know that scrollForward moves the item 10px, and you want it to stop after it moves the item 1000px, then you simply need to stop it has moved that much. I still don't know how your code is actually structured, but it might look something like the following:
(function() {
var moved_by = 0;
var interval = null;
var scrollForward = function() {
// move by 10px
moved_by += 10;
if (moved_by === 1000 && interval !== null) {
clearInterval(interval);
}
};
var interval = setInterval(scrollForward, 10);
})();
If you want to clear it after 1000 or 2000, you simply adjust the if statement accordingly. I hope that helps.

Categories