Hi I want to bind the the CSS opacity of two divs with how much that element is scrolled.
For example say I have two divs:
<div class="red" style="background:red"></div>
<div class="blue" style="background:blue"></div>
As the red div comes into the viewport, its opacity goes from 0 to 100 - depending on the amount scrolled.
Likewise as the blue div comes into viewport its opacity goes from 100 to 0, depending on the amount scrolled.
I found this Jquery/Javascript Opacity animation with scroll -
var fadeStart=100 // 100px scroll or less will equiv to 1 opacity
,fadeUntil=200 // 200px scroll or more will equiv to 0 opacity
,fading = $('#fading')
;
$(window).bind('scroll', function(){
var offset = $(document).scrollTop()
,opacity=0
;
if( offset<=fadeStart ){
opacity=1;
}else if( offset<=fadeUntil ){
opacity=1-offset/fadeUntil;
}
fading.css('opacity',opacity).html(opacity);
});
however the scroll amount is bound the document height, rather than the div itself.
Here is a jsfiddle to work from http://jsfiddle.net/RPmw9/
Thanks in advance.
Depends on when you want it fully opaque or not, but this could be a start:
»»Fiddle«« (Multiple element class version - set individually)
»»Fiddle«« (Single element class version - if only one element per class)
function fader() {
var r = $('.red'), // The .red DIV, as variable so we do not have to look
// it up multiple times.
b = $('.blue'), // Same for blue.
wh = $(window).height(), // Height of window (visible part).
dt = $(document).scrollTop(), // Pixels document is scrolled down.
/* red offset top is a semi static values which say how many pixels it
* is from the top of the document.
* "Red Offset Top" - "Document Scroll Top" gives us how many pixels
* the red DIV is from top of visible window.
* "Window Height" - that value gives us pixels the red DIV is from top
* normalized to start from 0 when top of DIV is at bottom of window.
* */
redView = wh - (r.offset().top - dt),
// Same for blue DIV
blueView = wh - (b.offset().top - dt),
// Variable to save opacity value.
op;
/* If redView is bigger then 0 it means the DIV has top border above bottom
* of window.
*/
if (redView > 0) {
/* Opacity goes from 0 - 1 so we divide 1 by window height and
* multiplies it with pixels red DIV is from bottom.
* In addition we add the height of the red DIV to window height, such
* that we set opacity until it is out of view (Bottom border is at top
* of window, and not only until it has top border at top of window.)
*/
op = 1 / (wh + r.height()) * redView;
/* If value of calulation is less or equal to one, it is in visible
* view and we set the opacity accordingly.
*/
if (op <= 1)
r.css('opacity', op);
}
if (blueView > 0) {
op = 1 - 1 / (wh + b.height()) * blueView;
if (op >= 0)
b.css('opacity', op);
}
// Add this line for a possible help in understanding:
console.log(
'Window Height:', wh,
'Doc Scroll Top', dt,
'Red offset top:', r.offset().top,
'Red offs.top - Doc Scroll Top', r.offset().top - dt,
'View:', wh - (r.offset().top - dt)
);
}
// Attach scroll event to the function fader()
$(document).bind('scroll', fader);
OK. Added some comments. Might not feel it is the best explanation. A better way to understand it is perhaps to have a look at the values so I also added a console.log() line inside the fader() function. Open your console and view the values as you scroll. Fiddle with logging. Also note this performance difference. style is considerably faster.
Version two:
This set full opacity when element has top at top of window, (not bottom of element). Note that we could use the Math.min() in the above function as well, to omit the op variable and if (op <= 1) and if (op >= 0) statement, but not at least a quick benchmark on jsperf revealed the if version to perform slightly better. If you have many elements that you style one should use the if version.
»»Fiddle««
function fader() {
var r = $('.red'),
b = $('.blue'),
wh = $(window).height(),
dt = $(document).scrollTop(),
redView = wh - (r.offset().top - dt),
blueView = wh - (b.offset().top - dt);
if (redView > 0) {
// Math.min() returns the lowest of given values. Here we do not want
// values above 1.
$('.red').css('opacity', Math.min(1 / wh * redView, 1));
}
if (blueView > 0) {
$('.blue').css('opacity', 1 - Math.min(1 / wh * blueView, 1));
}
}
// Event on scroll
$(document).bind('scroll', fader);
Related
Please help me out here, if bottom of div or full div is visible then i want to scroll to down of next div. Here the code i have tried out,
Mathematically,
var top = $("#myDiv").offset().top;
//top = 1863
var divHeight = $("#myDiv").height();
//divHeight = 571
var total = top + divHeight;
//total = 2434
if($('#myDiv').css('height',total).visible(true))
{
alert('hi');
// I need to alert only if the full div is visible not some part of div
}
else
{
//if height of myDiv is larger window height (my screen height 640 pixels)
}
If all this part of html(from top to divHeight) or bottom of page(here total value) is visible then i need to scroll to next div.
Please note :- the code inside if conditional statement is not correct, i think you got some idea from that.
Given element the jQuery object you want to check, element is fully visible if it is shown and all of the 4 sides of its layout box fall within the window viewport.
Caution: the following solution assumes that there are no element with scrollable overflow between element and the document root, otherwise the calculation becomes way more complicated.
function isFullyVisible(element) {
var offset = element.offset();
var scrollTop = $(document).scrollTop();
var scrollLeft = $(document).scrollLeft();
return element.is(":visible") && // shown
offset.top >= scrollTop && // top
offset.left >= scrollLeft && // left
offset.top + element.outerHeight() <= scrollTop + $(window).height() && // bottom
offset.left + element.outerWidth() <= scrollLeft + $(window).width(); // right
}
If you don't care of sides, you can keep only the corresponding sub-expression.
This question already has answers here:
Cross-Browser Method to Determine Vertical Scroll Percentage in Javascript
(14 answers)
Closed 2 years ago.
The problem:
What would be the mathematical formula to calculate (regardless of the
scrollHeight of the document) how far the bottom of the scrollbar is from it's total bottom (which would be the end of the page). So, for example, when the scrollbar is at the top, I would say the distance in percentages of the bottom of it, from the bottom of the document, would be 0%, and when it's totally scrolled all the way (vertically), it would be 100%.
My goal:
My goal is to calculate how many pixels there are between the bottom and a specific position which is, let's say 3%, relative to the viewport, above that bottom. Again, the document height should mean nothing. 3% are 3% if it's relative to the viewport.
Known variables:
var P = 3 // in %
var totalHeight = document.documentElement.scrollHeight;
var viewportHeight = document.documentElement.clientHeight;
Returns a number between 0 to 100 relative to scroll position:
document.onscroll = function(){
var pos = getVerticalScrollPercentage(document.body)
document.body.innerHTML = "<span>" + Math.round(pos) + "%<span>"
}
function getVerticalScrollPercentage( elm ){
var p = elm.parentNode
return (elm.scrollTop || p.scrollTop) / (p.scrollHeight - p.clientHeight ) * 100
}
body{ height:2000px }
span{ position:fixed; font:5em Arial; color:salmon; }
● Difference between scrollHeight & clientHeight
When you scroll to the bottom, the final position value is equal to the height of your document minus the height of one screen (viewport). So if you compute:
scrollPositionRelative = scrollPosition / (documentHeight - viewportHeight);
The values will be in the range 0-1 as expected.
Here's the function used in the example given at the end.
function getScrollPosition () {
var viewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); // Viewport height (px)
var scrollPosition = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; // Current scroll position (px)
var documentHeight = $(document).height(); // Document height (px)
var scrollPositionRelative = scrollPosition / (documentHeight - viewportHeight); // The document height is reduced by the height of the viewport so that we reach 100% at the bottom
return {
documentHeight: documentHeight,
relative: scrollPositionRelative,
absolute: scrollPositionRelative * documentHeight // Yields an "average" pixel position
};
}
See it in action: http://jsbin.com/tawana/1/
This isn't so much a jQuery question as it is an overall conceptual question.
In my example I can populate a container with divs that have a top value set in a nonlinear fashion.
The top value of each one is calculated based on a formula that takes into account the top position of the one to its left as well as the height of the container (line 33 of fiddle).
//this formula sets the top value for each new child added to the container
//height is 100% of its parent which is 20% of the body
//newOne:last is the most recently added child and will have an initial top value of 10%
parseInt($(this).next().css('top'), 10) / $('#queue').height()) * 75 + (parseInt($('.newOne:last').css('top'), 10) * 2) + '%'
I more of less stumbled upon this by chance and it seems to work 'ok', but if an optimization is obvious to you, please point it out :)
What I'm having trouble coming up with is an elegant formula for how to adjust the children smoothly during a drag event. I'm thinking the top value needs to be adjusted based on some manipulation of the left offset, but after hours of experimenting, I haven't found anything that keeps the original position intact when I start dragging and continues adjusting the values smoothly during my drag. The children should gradually approach a minimum top value of 10% as I drag left (child with left offset of 0 will have a top value of 10%), and gradually move away from that top value back toward their initial position as I drag right.
$('#queue').draggable({
axis: "x",
scroll: false,
drag: function(){
//adjust values of each child
$('.newOne').each(function(){
var percentLeft = $(this).offset().left / $('footer').width() * 100
var thisLeft = parseInt($(this).css('left'), 10) / $(window).width() * 100;
var thisTop = parseInt($(this).css('top'), 10) / $('#queue').height() * 100;
if (percentLeft >= 0){
//top value of each one gradually decreases...
//as it gets closer to an offset value of 0 and minimum top value of 10%
//non-linear attempt but not even close
//$(this).css('top', $(this).css('top', 10 + (thisTop - 10 / thisLeft) + '%'));
//linear step
$(this).css({'top': 8 + (percentLeft/2) + '%'});
}
});
}
});
http://jsfiddle.net/5RRCS/17/
P.S. I know I'm asking a lot here, but hopefully someone is up to the challenge :)
Update:
Stumbled onto exp method and did something like this:
adjustTop = function(offset){
return 100 * (1.0-Math.min(0.98,(0.83 + ( 0.17/ (Math.exp(0.007*offset))) )) ) + '%';
};
$(this).css('top', adjustTop($(this).offset().left) );
Here's a version that I believe does what you are looking for.
The first thing I did was to refactor the top calculation so that both the initialization and the drag handlers would get the same results.
Rather than calculate the positions of the child divs based on their offset to the document, I changed the logic to use position relative to their container.
I also remove z-index as the child divs already being added the parent with the correct stacking order - the left most child is the last element in the container.
Calculating the height of each child depended on whether #queue's current position was to the left or right of its origin.
I also change the iteration logic to behave the same to simplify calculating the current elements starting offset:
$($('.newOne').get().reverse()).each(function (index) {
$(this).css({
'background': 'rgba(255,255,255,.80)',
'top': calcTop($(this), index)
});
});
Code for positioning the child elements:
function calcTop($ele, index) {
var elePositionLeft = $ele.position().left;
var queuePositionLeft = $('#queue').position().left;
var footerWidth = $('footer').width();
var queueHeight = $('#queue').height();
var distanceToTravel = queuePositionLeft < 0 ? elePositionLeft : footerWidth - elePositionLeft;
var percentTraveled = Math.abs(queuePositionLeft) / distanceToTravel;
var thisPercentLeft = (elePositionLeft + queuePositionLeft) / footerWidth;
var queuePercentLeft = queuePositionLeft / footerWidth;
var newTop;
var myStartOffset = (index + 1) * startOffset;
var topTravel = queuePositionLeft < 0 ? -myStartOffset + startOffset : (queueHeight - startOffset);
var linear = false;
if (linear) {
newTop = myStartOffset + (topTravel * percentTraveled);
newTop = newTop > startOffset ? Math.round(newTop) : startOffset;
return newTop;
} else {
if (queuePositionLeft >= 0) {
newTop = myStartOffset + (topTravel * thisPercentLeft * percentTraveled);
newTop = newTop > startOffset ? Math.round(newTop) : startOffset;
} else {
newTop = myStartOffset + (topTravel * (1+thisPercentLeft) * percentTraveled);
newTop = newTop < startOffset ? startOffset : Math.round(newTop);
}
return newTop;
}
}
There was also a minor bug in the reset function - it wasn't setting childCount back to zero:
$('#reset').click(function () {
$('#queue').empty().css('left', 0);
childCount = 0;
});
Demo Fiddle
I said “JavaScript”. I'm trying to make my own JavaScript custom scrollbar. It's almost successful. The problem is that I can't get an accurate multiplier for the scrolling speed. This is my code:
var elem = document.getElementById('scroll-area'),
track = elem.children[1],
thumb = track.children[0],
height = parseInt(elem.offsetHeight, 10),
cntHeight = parseInt(elem.children[0].offsetHeight, 10),
trcHeight = parseInt(track.offsetHeight, 10),
distance = cntHeight - height,
mean = 50, // For multiplier (go faster or slower)
current = 0;
elem.children[0].style.top = current + "px"; // Set default `top` value as `0` for initiation
thumb.style.height = Math.round(trcHeight * height / cntHeight) + 'px'; // Set the scrollbar thumb hight
var doScroll = function (e) {
// cross-browser wheel delta
e = window.event || e;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
// (1 = scroll-up, -1 = scroll-down)
// Always check the scroll distance, make sure that the scroll distance value will not
// increased more than the content height and/or less than zero
if ((delta == -1 && current * mean >= -distance) || (delta == 1 && current * mean < 0)) {
current = current + delta;
}
// Move element up or down by updating the `top` value
elem.children[0].style.top = (current * mean) + 'px';
thumb.style.top = 0 - Math.round(trcHeight * (current * mean) / cntHeight) + 'px';
e.preventDefault();
};
if (elem.addEventListener) {
elem.addEventListener("mousewheel", doScroll, false);
elem.addEventListener("DOMMouseScroll", doScroll, false);
} else {
elem.attachEvent("onmousewheel", doScroll);
}
And the markup:
<div id="scroll-area">
<div><!-- CONTENT --></div>
<span class="scrollbar-track"><span class="scrollbar-thumb"></span></span>
</div>
My problem is on mean = 50. When you scroll the container until the bottom of content, the red line that I made in the demo page should stop right at the bottom of the container, not higher than that.
Anyone have an idea for the accurate result?
PS: I also want to add a function that will enable user to scroll the content by dragging the scrollbar thumb. But I think I want to focus on this issue first. Thanks for your help.
DEMO: http://jsfiddle.net/tovic/2B8Ye/
You could have done this much much easier.
Instead of calculating the top property for the container you can set its scrollTop property, and you guess what - you don't have to check if the content moves upper than 0 or lower than height because scrollTop property can not be set to a value smaller than 0 or greater than container's height!
Here's your modified fiddle
Notice that I had to wrap everything with additional <div id="everything">...</div> so the scrollbar won't be scrolled together with the #scroll-area div.
Also I assume using clientHeight instead of offsetHeight for .scroll-content div to include padding in the cntHeight variable.
More info on the scrollTop property you can find in this article.
I'm looking to change the opacity on an object (and have the transition be animated) based on a users scroll.
example(http://davegamache.com/)
I've searched everywhere
like here, but it ends up pointing me to the waypoints plugin (http://stackoverflow.com/questions/6316757/opacity-based-on-scroll-position)
I've implemented the [waypoints][1] plugin and have the object fading once it's higher than 100px. [Using the offet attribute] but would like to basically control the opacity of an object and have the animation be visible like the above example.
I've searched all over- this is my last resort.
Any help is greatly appreciated.
working exemple with starting and ending point here:
http://jsfiddle.net/z7E9u/1/
I copy paste basic code here
var fadeStart=100 // 100px scroll or less will equiv to 1 opacity
,fadeUntil=200 // 200px scroll or more will equiv to 0 opacity
,fading = $('#fading')
;
$(window).bind('scroll', function(){
var offset = $(document).scrollTop()
,opacity=0
;
if( offset<=fadeStart ){
opacity=1;
}else if( offset<=fadeUntil ){
opacity=1-offset/fadeUntil;
}
fading.css('opacity',opacity).html(opacity);
});
Here's a working example: http://jsfiddle.net/meEf4/
And the code:
var target = $('div');
var targetHeight = target.outerHeight();
$(document).scroll(function(e){
var scrollPercent = (targetHeight - window.scrollY) / targetHeight;
if(scrollPercent >= 0){
target.css('opacity', scrollPercent);
}
});
All we do is grab the current scroll position of the window, figure out what percentage of the element in question is now off-screen, and set its opacity with that percentage.
As I have lower than 50 reputation I cannot reply to Lonut's question, how to do the reverse. Here is my code if you would like the reverse, quite handy for navigation bars.
$(window).scroll(function () {
var offset = $(document).scrollTop()
var opacity = 0;
if (offset <= 0) {
opacity = 0;
} else if (offset > 0 & offset <= 200) {
opacity = (offset - 1) / 200;
}
else {
opacity = 1;
}
$('.black-background').css('opacity', opacity).html(opacity);
});
I looked at the source code of that site.
it uses: $(document).scrollTop();
to determine the scroll height, and $(window).scroll(function(){}) to bind an event listener to scrolling.
try this:
$(window).scroll(function(){
var fromtop = $(document).scrollTop(); // pixels from top of screen
$('#fademeout').css({opacity: 100-fromtop}); // use a better formula for better fading
});
I know I am a bit late to the party, but here's my approach:
$(window).scroll(function(){
var st = $(window).scrollTop();
var range = 300 // finetune this to the desired effect
$('.yourelement').css("opacity", 1- st / range); // animate your element
});
I like this solution
var fadeStart=100 // 100px scroll or less will equiv to 1 opacity
,fadeUntil=200 // 200px scroll or more will equiv to 0 opacity
,fading = $('#fading')
;
$(window).bind('scroll', function(){
var offset = $(document).scrollTop()
,opacity=0
;
if( offset<=fadeStart ){
opacity=1;
}else if( offset<=fadeUntil ){
opacity=1-offset/fadeUntil;
}
fading.css('opacity',opacity).html(opacity);
});
How could you use the mouse scrolling for the fading ONLY until eg 0.2 opacity is reached and then scroll the page too? The solutions i found so far disable the mouse scrolling function completely