I'm trying to understand how to animate more elemets at the same time in jQuery.
Let's say I have a simplified code like this:
var $targetElement = $('#target-element');
var offset = $targetElement.offset();
var width = $targetElement.outerWidth();
var height = $targetElement.outerHeight();
var bodyWidth = $('body').width();
var bodyHeight = $('body').height();
$topHelper.animate({
height: offset.top
});
$rightHelper.animate({
top: offset.top,
width: bodyWidth - offset.left - width,
height: height
});
$bottomHelper.animate({
height: bodyHeight - offset.top - height
});
$leftHelper.animate({
top: offset.top,
width: offset.left,
height: height
});
Where the targetElement is an element somwhere in the body and all those helpers are black semitransparent absolutely positioned layers (right on the body) which should get to sorround the targetElement's bounding box.
The problem is that during the animation I can see tiny (1px - 3px) gaps or overlaps between the animated layers. It's on the edges where the layers shoud be always next to each other. I think it's because I animate 4 elements and every element has its own 'fx' queue. So there are 4 timers and the 'frame refresh' is not at the same time for all of them.
Is it true or am I totally wrong? How can I synchronize animation of more elements?
EDIT From comments below:
Well, I don't understand how to use css animations here. I think they are declarative and I need to animate to a dynamically computed positions.
requestAnimationFrame looks promising, unfortunatelly I need to support IE9.
I would really appreciate an answer regarding to the jQuery animations. More specifically, are my assumptions about 4 timers right? If yes, is it possible to animate more elements in one 'queue' - that is recompute all 4 element positions in every 'frame repaint' together?
Related
I tried to experiment with parallax and started from scratch to understand the core parts of this magic. To give you an example that I like to use as inspiration, you can see it at this link here at the "Photos" section.
Latest code is down the page with related information. To get an overall look of the question see the rest of the details.
Core parts I already know are the scrollTop() of the $window and the offsetTop of the element are important to apply the parallax effect on a DOM element as well as a factor for how sensitive the effect should be respond to the scroll speed. The end result should be some formule that will calculate the translateY or translate3d coordinates in pixels or percentage.
I read on the internet that the CSS property translate is faster than, for example, top from position: absolute, and my preference would be also to use translate in combination with TweenMax GSAP. So the movement of the parallax will be very smooth. But if only the css property translate is enough that's fine too. I saw some examples that where using TweenMax, so that's why I use it for now.
JS
I have code the basic things:
var win = $(window);
var el = $('#daily .entry').find('figure');
win.scroll(function() {
var scrollTop = win.scrollTop();
var parallaxFactor = 5;
el.each(function() {
var image = $(this).find('img');
var offsetTop = $(this).offset().top;
// This is the part where I am talking about.
// Here should be the magic happen...
});
});
So I've code above code, but it doesn't do anything, of course. See CodePen from above code here. It will only console log scrollTop and offsetTop. As mentioned before, I only know the core parts like scrollTop and offsetTop to apply the parallax effect. Then there should be some area created where the parallax effect will be triggered and happen, so calculations will be only done for elements within the viewport in order to keep the performance good. After that there should be some math done, but doesn't know exactly what or how to achieve this. Only after I have a final number, I could use it within for example TweenMax from Greensock like so:
TweenMax
TweenMax.to(image, 0.1, {
yPercent: offsetPercent + '%',
ease: Linear.easeNone
});
Parallax formula
If I look around to get the formula down I came to something like this (founded on the internet):
var viewportOffset = scrollTop - offsetTop + win.height();
var offsetPercent = ((viewportOffset / win.height() * 100) - 100) / parallaxFactor;
if (viewportOffset >= 0 && viewportOffset <= win.height() * 2) {
TweenMax.to(image, 0.1, {
yPercent: offsetPercent + '%',
ease: Linear.easeNone
});
}
But if I am honest, I doesn't know what this does exactly, or why it should/could be this way. I would like to know this, so I can understand the whole process of making parallax happen. The functions of scrollTop(), offsetTop and $(window).height() are clear for me, but what the trick behind the formula is, is the part that I doesn't understand.
Updates
Update 1
#Scott has notified that the inspiration site uses a plugin called scrollmagic.io, but I am very curious about how I can create a parallax by myself without the use of a plugin. How it works and how to achieve it. With emphasis on the formula, why I should it do this or that way and what exactly will be calculated, because I don't understand it and really wanna know this, so that I can use this knowledge in the future when applying a parallax effect.
Update 2
I tried to figure out what the following code snippet exactly does. I talking about this one:
var viewportOffset = scrollTop - offsetTop + win.height();
After some good debug sessions I think that I've the clue. So scrollTop is the amount of pixels that you've scrolled down the page and that are hidden from the view. offsetTop is the start position of the element within the DOM and $(window).height is the viewport height - the part that is visible in the browser -.
This is what I think that this formula does:
Set the zero point to the point where the element starts. For example, when scrollTop is equal to 0 and the element starts at 240px from the top, then the formula is: 0 minus 240 is -240. So the current scroll position is below zero point. After scrolling 240px down, the formula will output 0 because of course 240 minus 240 is 0 (zero). Am I right?
But the part that I doesn't understand yet is why + win.height.
If we go back to above formula (at Update 2) and scrollTop is zero then the $(window).height is the space from 240px till the bottom of the viewport. When scrolling down, the amount of pixel will grow on scroll, that makes no sense to me. If someone can explain what could have been the purpose of this would be fine. 'm very curious. The second part of the formula to calculate the parallax offsetPercent I still don't understand. In general the calculation of the parallax strength on scroll.
Update 3 / 4
Advised by #Edisoni, I walked the last few days by the videos of Travis Neilson and I have become a lot wiser on the basic functionalities of parallax. A must for everyone who wants to dig in parallax. I've used the new knowledge about parallax to get my above script work:
var root = this;
var win = $(window);
var offset = 0;
var elements = $('#daily .entry figure');
if (win.width() >= 768) {
win.scroll(function() {
// Get current scroll position
var scrollPos = win.scrollTop();
console.log(scrollPos);
elements.each(function(i) {
var elem = $(this);
var triggerElement = elem.offset().top;
var elemHeight = elem.height();
var animElem = elem.find('img');
if (scrollPos > triggerElement - (elemHeight / 2) && scrollPos < triggerElement + elemHeight + (elemHeight / 2)) {
// Do the magic
TweenMax.to(animElem, 0.1, {
yPercent: -(scrollPos - elemHeight / 2) / 100,
ease: Linear.easeNone
});
} else {
return false;
}
});
});
}
However, the script works only for a certain part of the elements. The problem is that it only works for the first two elements. I have a suspicion that the "error" is located in particularly after the AND && sign in the if statement, but can't get the error solved. http://codepen.io/anon/pen/XKwBAB
When the elements, that work on the trigger are animated, they will be jumping some pixels to the bottom, don't know how to fix this to.
The jumping to: 1.135%, after the trigger is fired. So it doesn't start at 0%. I already checked if I should add the CSS property translate to the CSS and set the type of number to %, but this doesn't work for me.
-webkit-transform: translateY(0%);
-moz-transform: translateY(0%);
-o-transform: translateY(0%);
transform: translateY(0%);
Should I use the TweenMax .fromTo() function instead of using the .to() function so I can set the start position as well or is my thought about this wrong and has a different cause?
Something like this:
TweenMax.fromTo(animElem, 0.1, {
yPercent: triggerElement,
z: 1
}, {
yPercent: -(scrollPos - elemHeight / 2) / 100,
ease: Linear.easeNone
});
Beside that I trying to recreate the effect of the site that I would like to use as inspiration source without the use of the scrollMagic plugin, but I don't really know how this works, with the use of two different objects that are animated.
At last, if someone thinks the code can be better formatted, don't hesitate, I would like to hear your suggestions
My actual questions are for update 2 and 3/4:
How to calculate the parallax y coordinates to get "the magic" done?
Am I right about update 2, that the zero point will be reset to offsetTop of each element?
Why my code only works for the first two elements and why they jumping some pixels down if the inline style of translate will be added to the animated element? See update 3/4 for all info.
Parallax is actually quite simple in principle. Just make the parallax element scroll slower than the rest of the content. That being said, a parallax implementation can be as simple as dividing the scroll distance by a factor:
var parallaxFactor = 3;
window.addEventListener("scroll", function(e){
el.style.top = (document.body.scrollTop / parallaxFactor) + "px";
// This is the magic. This positions the element's y-cord based off of the page scroll
}, false);
CODEPEN
This is an extremely simple demonstration of the parallax effect. Other more thorough implementations may handle values as percentages, or attempt to smooth the animation with TweenMax. This however, is the magic you're looking for.
Live long and prosper.
Update:
This example only works for elements at the top of a screen. If this were for a more general purpose, you would want to store the default y-position of the element, then something along the lines of defaultYCord + (document.body.scrollTop / parallaxFactor).
Update 2:
A very good visualization for parallax comes from Keith Clark who made a pure css parallax scroller: http://keithclark.co.uk/articles/pure-css-parallax-websites/demo3/. If you click debug in the upper left, it gives you a nice 3d-view of the magic.
This is not an answer how to build a parallax in JS. But it shows some basics, which will often be forgotten, if your too much into the code.
Basics:
Order your graphical objects in z-layers. As higher z is, as nearer
it is to observer in front of the screen.
As higher your object is in the z-axis as faster it should move on something that appears, f.e. your scrolling
Now you get a 3-D-Effect where objects nearer to you move faster to your actions as objects more far away.
Your question
How to calculate the parallax y coordinates to get "the magic" done?
The y-position depends on your z-index. If it is far away a.k.a the z-index is low, delta-y is small. If it is near too you, delta-y is big.
Please consider the z-index is not necessarily used as Style-property, it's more like it looks like.
I would add an attribute like data-z to every parallaxing layer.
function parallaxingY(el){
//el is a parallaxing element with attribute data-z
return $(el).data('z')*window.scrollTop;
}
the suggested CSS-Solution is nice and should be preferred. There the "magic" - the "z-index" - is made by the css-style "scale".
Although we can achieve fantastic animations through various Javascript libraries such as jQuery. I am wondering what's the technique behind the animation?
I can think of using CSS to format the page element.
But how can we place an element on arbitrary position of the page? I mean, not by lines. Is it true that we can think of the client area within the browser window as the Paint canvas?
I am totally new to frontend Web development, I hope I made myself clear. And thank you for answering this junior question.
The jQuery way - and the only cross-browser way - to animate is to set some CSS properties, wait a little, update those properties, wait a little, update those properties...
e.style.position = "absolute";
time_start = Date.now();
time_end = time_start + 10000;
(function tick(){
now = Date.now() - time_start;
if(now > time_end) now = time_end;
e.style.top = now * speed + top_start;
if(now < time_end) setTimeout(tick, 13);
}();
The CSS properties you are interested in are:
position: absolute lets you position the element to an arbitrary location.
display: block or display: inline-block lets an element to have a width and height
top, left, bottom, right define the element position if its position is absolute or relative. left takes precedence over right and top takes precedence over bottom.
width and height define the element's size.
opacity can be animated to fade an element in or out.
padding, border-width, margin and their respective components can all be animated.
You can also animate colors: border-color, color, background.
I'm trying to create a website with main content area and a sidebar, something like here on Stack Overflow. The goal is that when you scroll down, the sidebar stays visible.
I have seen two approaches to this:
position:fixed;
JavaScript manipulation with the DOM
Approach no. 1, as far as I know, will have a problem when the viewport is smaller than the sidebar contents so I guess that can't be used reliably and JavaScript scripts that I have seen are usually animated or generally "slow" (you can see that there is redrawing going on after each scroll).
Can someone point out a JavScript library / CSS approach that would not suffer from the aforementioned issues?
Edit: an example would be this page but with the sidebar sticking to the top without an animation and correctly handling the situation when the sidebar is higher than content / viewport.
I don't like heavy JS solutions, so important thing to ask is - preferred compatibility. In IE8+ it is possible instead of
var $window = $(window),
$sidebar = $(sidebar);
$window.on('resize', function(){
$sidebar.height($window.innerHeight());
});
$window.resize();
do something like this (pure CSS solution):
#sidebar {
position: fixed;
left: 0; /* or right */
top: 0;
bottom: 0;
overflow: auto;
}
When you have top&bottom / left&right value at the same time, box will be stretched. (JSFiddle demo)
Got it. It is Javascript based, but I'm sure that's nothing heavy and even IE8 should solve it pretty fine.
var top = $('#sidebar').offset().top;
var height = $('#sidebar').height();
var winHeight = $(window).height();
var gap = 10;
$(window).scroll(function(event) {
var scrollTop = $(this).scrollTop();
// sidebar reached the (end - viewport height)
if (scrollTop + winHeight >= top + height + gap) {
// if so, fix the sidebar and make sure that offset().top will not give us results which would cancel the fixation
$('#sidebar').addClass('fixed').css('top', winHeight - height - gap + 'px');
} else {
// otherwise remove it
$('#sidebar').removeClass('fixed').css('top', '0px');
}
});
demo
You could catch client window's height and giving it to your sidebar like this :
var sidebarHeight = $(window).innerHeight();
$('#sidebar').css('height',sidebarHeight);
With the proper CSS for the sidebar :
#sidebar {
position: fixed;
right: 0;
top: 0;
width: 200px;
overflow: hidden;
}
Here is a working JSFiddle.
You could also watch for window resizing to avoid a mess on resize :) Here is the way to go with jQuery
Good luck
Not sure if you have this figured out but I have created a contained sticky sidebar jQuery plugin. It's really simple and allows you to invoke with just one line of jQuery. Take a look here: http://mojotech.github.com/stickymojo/
It starts by position: fixed; then uses javascript to handle any resizes, scrolls and even allows you to specify a footer element that it should not intersect. By combining these approaches you will get a smooth looking fixed element. Plus, we made it easy for you.
Code and demo here: http://closure-library.googlecode.com/svn/docs/class_goog_ui_ScrollFloater.html
I would like to create an interface with 3 columns, each having mixed content (text, image, and video), and would like to have them scroll vertically with different speeds at the same time. Is there a relatively simple way of accomplishing this with html, css, and/or javascript?
PS. I know about the parallax scrolling, but the implementations I came across seem to be mostly about using images as background to create a dimensional illusion.
Something like: http://jsfiddle.net/KVWuS/.
$.fn.makeScroll = function(speed) {
var elem = this,
i = 0;
setInterval(function() {
elem.scrollTop(i++); // increment scroll top
}, speed); // run every 'speed' ms (so lower is faster)
};
You can enable it like:
$('div:eq(0)').makeScroll(75); // moderate speed
You want to actually scroll the content?
$('.column').animate({
scrollTop: $('.column').height() - $(window).height()
}, 1000);
This will scroll your column down in 1 second. Adjust the speed per column.
EDIT:
I was assuming your columns were the height of the window. If not, you'll have to adjust the scrollTop.
I think a simple workaround would be to create 3 div Elements with the following Attributes:
overflow: hidden;
width: x px (fixed width)
height: x px (fixed height)
top: 0px;
left: x px;
Then you have to capture the onscroll event and set the top-Attribute.
E.g.
div1: top: -100px
div2: top: -300px
div3: top: -500px
I hope my description is clear.. :)
That should work
I'm trying to use this script here: http://css-tricks.com/scrollfollow-sidebar/ to make a simple div that follows the window as the user scrolls. I changed it from 0 to topPadding and changed topPadding to topPadding*2 to get the right top offset.
Unfortunately this has the side effect of the object making the page a little longer and allowing the user to scroll infinitely. This bug is also actually in the original code without my larger toppadding changes if you make the window small enough.
I also tried another plugin for jquery, but it didn't work at all and this gives me what I need, so how can I fix it?
Why not just use CSS.
#theNonMovingDiv {position:absolute; position: fixed; top: Npx; right:Mpx}
position:fixed; doesn't work in ie6, but including the position:absolute; will give you a rough approximation.
I've knocked together this quick amendment, which limits based on the document height. I'm not certain that jQuery is giving an accurate height, hence a safety barrier of 100px. Even if the height isn't quite right, any extra scrolling will be limited and certainly not infinite.
<script type="text/javascript">
var documentHeight = 0;
var topPadding = 15;
$(function() {
var offset = $("#sidebar").offset();
documentHeight = $(document).height();
$(window).scroll(function() {
var sideBarHeight = $("#sidebar").height();
if ($(window).scrollTop() > offset.top) {
var newPosition = ($(window).scrollTop() - offset.top) + topPadding;
var maxPosition = documentHeight - (sideBarHeight + 100);
if (newPosition > maxPosition) {
newPosition = maxPosition;
}
$("#sidebar").stop().animate({
marginTop: newPosition
});
} else {
$("#sidebar").stop().animate({
marginTop: 0
});
};
});
});
</script>
I can duplicate your bug in my browser (Firefox 3.5).
The problem is that the code doesn't look to see if the bottom of the sidebar falls off the end of the document.
Your best bet is to use the .height() method to check. You can get the height of the sidebar (as presented in the example) as $("#sidebar").height(), and the height of the whole document as $(document).height().
What exactly the behavior should be -- up to you. It involves an extra if, to make sure all your pixels line up right, but design questions, like how the sidebar should align against the bottom, are going to be a matter of personal taste.