Animation Progress Bar - javascript

I'm working on a progress bar animation which needs to animate from 0 to whatever percentage when the progress bar becomes visible within browser's viewport. Animation should always happen when element is scrolled into the view which means that scrolling it outside has to reset animation to start.
Here's my non-working code:
var $animation_elements = $('.progressAnimation');
var $window = $(window);
function check_if_in_view() {
var window_height = $window.height();
var window_top_position = $window.scrollTop();
var window_bottom_position = (window_top_position + window_height);
$.each($animation_elements, function() {
var $element = $(this);
var element_height = $element.outerHeight();
var element_top_position = $element.offset().top;
var element_bottom_position = (element_top_position + element_height);
//check to see if this current container is within viewport
if ((element_bottom_position >= window_top_position) &&
(element_top_position <= window_bottom_position)) {
$element.animate({
"width": (600 * $($element).data("percent")) / 100
}, 3000);
} else {
$element.animate({
"width": "0"
}, 1000)
}
});
}
$window.on('scroll resize', check_if_in_view);
$window.trigger('scroll');
body{
height:4000px;
margin-top:800px;
}
.myContainer{
width:1000px;
margin:50px auto;
}
.myContainer .progressBackground{
width:600px;
height:40px;
margin:0 auto 40px;
background-color:#eaeaea;
}
.myContainer .progressAnimation{
width:0;
height:100%;
background-color:#00f36d;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="myContainer">
<div class="progressBackground">
<div class="progressAnimation" data-percent="80">
</div>
</div>
<div class="progressBackground">
<div class="progressAnimation" data-percent="60">
</div>
</div>
</div>
Note: run code snippet in fullscreen.

Animating using Javascript within scroll/resize event isn't really wise. Without throttling the event it is wiser to do something very simple.
I haven't delved into your code and why it doesn't work, but I've devised an example based on your code, but I'm doing animation using CSS (offloading animation off of browser process), and simply changing elements' state when it's different from what it should be. This means that I'm shrinking progress bar to 0 only when element goes off screen (and not every single time a scroll/resize event fires which is what you're doing) and animating progress bar when it comes on screen only when it's been off screen.
This is the code:
var $animation_elements = $('.progressAnimation');
$(window).on('scroll resize', function(){
var viewportHeight = document.documentElement.clientHeight;
$animation_elements.each(function() {
var $el = $(this);
var position = this.getBoundingClientRect();
if (position.top > viewportHeight || position.bottom < 0) {
this.inView && $el.css({ width: 0 });
this.inView = false;
} else {
!this.inView && $el.css({ width: 6 * $el.data("percent") });
this.inView = true;
}
});
});
As you can see I've also used as much vanilla Javascript as possible to make event handler as fast as possible.
And here is a working JSFiddle.

Related

Progress bar in a div

I have a progress bar on my body. When I click on a button a div appear, we can scoll on this div and I would liked to have also a progress bar for this one. I took the Jquery code of the progress bar
MY JSFIDDLE
window.onscroll = function() {
myFunction()
};
function myFunction() {
var winScroll = document.body.scrollTop || document.documentElement.scrollTop;
var height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
var scrolled = (winScroll / height) * 100;
document.getElementById("myBar").style.height = scrolled + "%";
}
$('button').click(function() {
if ($(this).hasClass("clicked")) {
$(this).text("Open ↓");
} else {
$(this).text("Close ↑");
}
$('.blue-container').toggleClass('In');
$('body').toggleClass('hideOverflow');
$(this).toggleClass("clicked");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="progress-container">
<div class="progress-bar" id="myBar"></div>
</div>
<button> Open ↓</button>
<div class='blue-container'>
<div class='blue'>
<p>Hello ! Scroll down. I would like to have a progress bar for this div, like the body.</p>
</div>
</div>
You need to create same progress setter function like you've set on window, except this one should use blue container scrollable element for scroll position measurement.
Remember that after your blue box is closed, you don't need setting progress from this box anymore, so you should unbind setter function ($scroller.off('scroll', progressSetter)). It will be bound again after next opening of blue box.
window.onscroll = function() {myFunction()};
function myFunction() {
var winScroll = document.body.scrollTop || document.documentElement.scrollTop;
var height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
var scrolled = (winScroll / height) * 100;
document.getElementById("myBar").style.height = scrolled + "%";
}
$('button').on('click', function() {
var $container = $('.blue-container'),
$scroller = $container.find('.blue'),
$btn = $(this),
$bar = $('#myBar'),
progressSetter = function () {
var height = $scroller[0].scrollHeight - $scroller.outerHeight();
$bar.css({
height: $scroller.scrollTop() / height * 100 + '%'
});
};
if ($btn.hasClass("clicked")) {
$btn.text("Open ↓");
$scroller.off('scroll', progressSetter)
} else {
$btn.text("Close ↑");
$scroller.on('scroll', progressSetter)
}
$container.toggleClass('In');
$('body').toggleClass('hideOverflow');
$btn.toggleClass("clicked");
});
https://jsfiddle.net/uo34ru7d/76/
As I can see, you used example code from tutorial. Try to figure out how this scrolling bound functions work (and event binding itself) and you will be able to create one function for both window and your blue container, make that your homework ;)

Get the element which is the most visible on the screen

I would like to get the one element which is the most visible on the screen (takes up the most space). I have added an example picture below to understand my question a bit more.
The two black borders are the sides of a screen. As you can see, the green box (div2) is the most visible on the screen - I would like to know how I can get that element. The most visible element should not have to be fully visible.
I have done a quick (it wasn't THAT quick) seach but to no avail, if I have missed it - my apologies.
TLDR:
Inspired by this question and the necessity for similar functionality in my own projects, I've written a module/jQuery plugin based on the code below. If you're not interested in the 'how', just download that or install with your favourite package manager.
Original Answer:
The answer provided by exabyssus works well in most cases, apart from when neither of an element's top or bottom is visible e.g when the element height is greater than the window height.
Here's an updated version which takes that scenario into account and uses getBoundingClientRect which is supported right the way down to IE8:
// Usage: var $element = getMostVisible($('.elements' ));
function getMostVisible($elements) {
var element,
viewportHeight = $(window).height(),
max = 0;
$elements.each(function() {
var visiblePx = getVisibleHeightPx($(this), viewportHeight);
if (visiblePx > max) {
max = visiblePx;
element = this;
}
});
return $elements.filter(element);
}
function getVisibleHeightPx($element, viewportHeight) {
var rect = $element.get(0).getBoundingClientRect(),
height = rect.bottom - rect.top,
visible = {
top: rect.top >= 0 && rect.top < viewportHeight,
bottom: rect.bottom > 0 && rect.bottom < viewportHeight
},
visiblePx = 0;
if (visible.top && visible.bottom) {
// Whole element is visible
visiblePx = height;
} else if (visible.top) {
visiblePx = viewportHeight - rect.top;
} else if (visible.bottom) {
visiblePx = rect.bottom;
} else if (height > viewportHeight && rect.top < 0) {
var absTop = Math.abs(rect.top);
if (absTop < height) {
// Part of the element is visible
visiblePx = height - absTop;
}
}
return visiblePx;
}
This returns the most visible element based on pixels rather than as a percentage of the height of the element, which was ideal for my use-case. It could easily be modified to return a percentage if desired.
You could also use this as a jQuery plugin so you can get the most visible element with $('.elements').mostVisible() rather than passing the elements to the function. To do that, you'd just need to include this with the two functions above:
$.fn.mostVisible = function() {
return getMostVisible(this);
};
With that in place you can chain your method calls rather than having to save the element into a variable:
$('.elements').mostVisible().addClass('most-visible').html('I am most visible!');
Here's all of that wrapped up in a little demo you can try out right here on SO:
(function($) {
'use strict';
$(function() {
$(window).on('scroll', function() {
$('.the-divs div').html('').removeClass('most-visible').mostVisible().addClass('most-visible').html('I am most visible!');
});
});
function getMostVisible($elements) {
var element,
viewportHeight = $(window).height(),
max = 0;
$elements.each(function() {
var visiblePx = getVisibleHeightPx($(this), viewportHeight);
if (visiblePx > max) {
max = visiblePx;
element = this;
}
});
return $elements.filter(element);
}
function getVisibleHeightPx($element, viewportHeight) {
var rect = $element.get(0).getBoundingClientRect(),
height = rect.bottom - rect.top,
visible = {
top: rect.top >= 0 && rect.top < viewportHeight,
bottom: rect.bottom > 0 && rect.bottom < viewportHeight
},
visiblePx = 0;
if (visible.top && visible.bottom) {
// Whole element is visible
visiblePx = height;
} else if (visible.top) {
visiblePx = viewportHeight - rect.top;
} else if (visible.bottom) {
visiblePx = rect.bottom;
} else if (height > viewportHeight && rect.top < 0) {
var absTop = Math.abs(rect.top);
if (absTop < height) {
// Part of the element is visible
visiblePx = height - absTop;
}
}
return visiblePx;
}
$.fn.mostVisible = function() {
return getMostVisible(this);
}
})(jQuery);
.top {
height: 900px;
background-color: #999
}
.middle {
height: 200px;
background-color: #eee
}
.bottom {
height: 600px;
background-color: #666
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="the-divs">
<div class="top"></div>
<div class="middle"></div>
<div class="bottom"></div>
</div>
Yes, this question is too broad. But I was interested on solving it.
Here is crude example on how to accomplish it.
I tried to explain what's going on with comments. It surely can be done better, but I hope it helps.
// init on page ready
$(function() {
// check on each scroll event
$(window).scroll(function(){
// elements to be tested
var _elements = $('.ele');
// get most visible element (result)
var ele = findMostVisible(_elements);
});
});
function findMostVisible(_elements) {
// find window top and bottom position.
var wtop = $(window).scrollTop();
var wbottom = wtop + $(window).height();
var max = 0; // use to store value for testing
var maxEle = false; // use to store most visible element
// find percentage visible of each element
_elements.each(function(){
// get top and bottom position of the current element
var top = $(this).offset().top;
var bottom = top + $(this).height();
// get percentage of the current element
var cur = eleVisible(top, bottom, wtop, wbottom);
// if current element is more visible than previous, change maxEle and test value, max
if(cur > max) {
max = cur;
maxEle = $(this);
}
});
return maxEle;
}
// find visible percentage
function eleVisible(top, bottom, wtop, wbottom) {
var wheight = wbottom - wtop;
// both bottom and top is vissible, so 100%
if(top > wtop && top < wbottom && bottom > wtop && bottom < wbottom)
{
return 100;
}
// only top is visible
if(top > wtop && top < wbottom)
{
return 100 + (wtop - top) / wheight * 100;
}
// only bottom is visible
if(bottom > wtop && bottom < wbottom)
{
return 100 + (bottom - wbottom) / wheight * 100;
}
// element is not visible
return 0;
}
Working example - https://jsfiddle.net/exabyssus/6o30sL24/
<style>
.block{
padding: 20px;
border:2px solid #000;
height: 200px;
overflow:hidden;
}
.green{
border: 1px solid green;
height: 150px;
margin:20px 0px;
}
.red{
border: 1px solid red;
height: 100px;
}
</style>
<div class="block">
<div class="example green"></div>
<div class="example red"></div>
</div>
var divs = $('.example');
var obj = {};
var heights = [];
$.each(divs,function (key, val)
{
heights.push($(val).outerHeight());
obj[$(val).outerHeight()] = $(val);
});
var max = Math.max.apply(null, heights);
console.log(obj[max]);

Problems positioning element based on browser width

I have a fixed position div that I want to have position left: 0 when the browser window is smaller than 1200px, but no left positioning when larger than 1200px. I'm using this code
var $window = $(window);
function checkWidth() {
var windowsize = $window.width();
document.write(windowsize);
if (windowsize < 1200) {
document.getElementById("DBCIbox").style.left = "0";
} else {50
document.getElementById("DBCIbox").style.left = "500px";
}
}
checkWidth();
$(window).resize(checkWidth);
but it doesnt seem to be doing anything. I'm setting the left=500px as simply a test to get the javascript working, then I'll worry about where I want to position it.
I've done some googleing and from what I can tell this should work, what am I missing?
If theres a way to clear the left positioning I'd like to know that as well.
var $window = $(window);
That line is cacheing that instance of window. You need to re-evaluate $(window) everytime you call this code to get updated width and height values
Updated code below
function checkWidth() {
var $window = $(window);
var windowsize = $window.width();
document.write(windowsize);
if (windowsize < 1200) {
document.getElementById("DBCIbox").style.left = "0";
} else {50
document.getElementById("DBCIbox").style.left = "500px";
}
}
checkWidth();
$(window).resize(checkWidth);
#Jnatalzia had it right, I needed to make a few tweaks, and the comments wouldn't let me post the code, but credit goes to #Jnatalzia for the answer.
function checkWidth() {
var $window = $(window);
var windowsize = $window.width();
var position;
if (windowsize < 1390) {
document.getElementById("DBCIbox").style.marginLeft = 0 + "px";
} else if (windowsize >= 1390) {
position = windowsize - 1060;
position = position / 2;
position = position - 175;
document.getElementById("DBCIbox").style.marginLeft = position + "px";
}
}
$("document").ready(function(){
$(window).load(function(){
checkWidth();
});
$(window).resize(function(){
checkWidth();
});
});

y position issue for background images via css and javascript

I have implemented a parallax scrolling effect based on a tutorial I found. The effect works great. However, when I specify the background images, I am unable to control the y (vertical) axis. This is causing problems because I'm trying to set locations on multiple layered images.
Any thoughts on what's causing the problem?
Here is one external script:
$(document).ready(function(){
$('#nav').localScroll(800);
//.parallax(xPosition, speedFactor, outerHeight) options:
//xPosition - Horizontal position of the element
//inertia - speed to move relative to vertical scroll. Example: 0.1 is one tenth the speed of scrolling, 2 is twice the speed of scrolling
//outerHeight (true/false) - Whether or not jQuery should use it's outerHeight option to determine when a section is in the viewport
$('#mainimagewrapper').parallax("50%", 1.3);
$('#secondaryimagewrapper').parallax("50%", 0.5);
$('.image2').parallax("50%", -0.1);
$('#aboutwrapper').parallax("50%", 1.7);
$('.image4').parallax("50%", 1.5);
})
This is another external script:
(function( $ ){
var $window = $(window);
var windowHeight = $window.height();
$window.resize(function () {
windowHeight = $window.height();
});
$.fn.parallax = function(xpos, speedFactor, outerHeight) {
var $this = $(this);
var getHeight;
var firstTop;
var paddingTop = 0;
//get the starting position of each element to have parallax applied to it
$this.each(function(){
firstTop = $this.offset().top;
});
if (outerHeight) {
getHeight = function(jqo) {
return jqo.outerHeight(true);
};
} else {
getHeight = function(jqo) {
return jqo.height();
};
}
// setup defaults if arguments aren't specified
if (arguments.length < 1 || xpos === null) xpos = "50%";
if (arguments.length < 2 || speedFactor === null) speedFactor = 0.1;
if (arguments.length < 3 || outerHeight === null) outerHeight = true;
// function to be called whenever the window is scrolled or resized
function update(){
var pos = $window.scrollTop();
$this.each(function(){
var $element = $(this);
var top = $element.offset().top;
var height = getHeight($element);
// Check if totally above or totally below viewport
if (top + height < pos || top > pos + windowHeight) {
return;
}
$this.css('backgroundPosition', xpos + " " + Math.round((firstTop - pos) * speedFactor) + "px");
});
}
$window.bind('scroll', update).resize(update);
update();
};
})(jQuery);
Here is the CSS for one section:
#aboutwrapper {
background-image: url(../images/polaroid.png);
background-position: 50% 0;
background-repeat: no-repeat;
background-attachment: fixed;
color: white;
height: 500px;
width: 100%;
margin: 0 auto;
padding: 0;
}
#aboutwrapper .image4 {
background: url(../images/polaroid2.png) 50% 0 no-repeat fixed;
height: 500px;
width: 100%;
margin: 0 auto;
padding: 0;
}
.image3{
margin: 0 auto;
min-width: 970px;
overflow: auto;
width: 970px;
}
Both of these are being called to achieve the parallax scrolling. I really just want to more specifically control the background image locations. I've tried messing with the CSS background position and I've messed with the first javascript snippet as well. No luck.
just a quick shot, have you tried actually placing the images, either in a div or just using the img src tag to actually move the element rather than manipulating the y axis of a background image?

Jquery follow scroll

I have a sort of sidebar on my website, which has to scroll down together with the user so that it is always in the view.
The code I'm using now is actually working fine however there is one problem. On smaller screens the sidebar scrolls before your at the sidebar thus making it impossible to see it all even if you scroll.
So what I want is the sidebar to scroll with the bottom instead of it being pushed down with the top so that when you reach the end of the sidebar it starts to scroll.
This is the code that I'm currently using.
var documentHeight = 0;
var topPadding = 10;
$(function() {
var offset = $("#mainright").offset();
documentHeight = $(document).height();
$(window).scroll(function() {
var sideBarHeight = $("#mainright").height();
if ($(window).scrollTop() > offset.top) {
var newPosition = ($(window).scrollTop() - offset.top) + topPadding;
var maxPosition = documentHeight - (sideBarHeight);
if (newPosition > maxPosition) {
newPosition = maxPosition;
}
$("#mainright").stop().animate({
marginTop: newPosition
});
} else {
$("#mainright").stop().animate({
marginTop: 0
});
};
});
});
I guess the "best practice" for accomplishing a task like this is to use dynamically changing css position from absolute to fixed and vice versa. A basic example could look like:
$(function(){
var $box = $('.box'),
offset = $box.offset(),
doc_h = $(document).height();
$(window).scroll(function(){
if($(window).scrollTop() > offset.top) {
if(!$box.hasClass('fix'))
$box.toggleClass('normal fix');
}
else{
if(!$box.hasClass('normal'))
$box.toggleClass('normal fix');
}
});
});​
Example in action: http://www.jsfiddle.net/YjC6y/14/
$(function() {
var top = 50;
$(window).scroll(function() {
$('#box').stop().animate({ top: $(window).scrollTop() + top}, 1000);
});
});
Try the example : http://jsbin.com/omiyi3
I think you can instead make the sidebar responsive by throwing your function into one of these:
if (responsive_viewport >= 768) {}
This makes it so that the function will only load if the viewport is bigger than or equal to 768px.

Categories

Resources