Activate animation after scrolling beyond a specific point in document - javascript

I made a count-up counter animation with js, but the counter starts count-up animation when I refresh the page (does not matter where I am, or whether I can see it or not).
I want the counter to start working only when the counter div is in the viewpoint (when I scroll down and see it).
HTML
<div class="content bg-warning">
<div class="row m-0 py-5 d-flex justify-content-center">
<div class="counter" data-count="2200" style="color: white; font-size: 32px; font-weight: bold;">0</div>
</div>
</div>
js
<script type="text/javascript">
$('.counter').each(function() {
var $this = $(this),
countTo = $this.attr('data-count');
$({ countNum: $this.text()}).animate({
countNum: countTo
},
{
duration: 8000,
easing:'linear',
step: function() {
$this.text(Math.floor(this.countNum));
},
complete: function() {
$this.text(this.countNum);
//alert('finished');
}
});
});
</script>
How can I solve this?

Use $(window).scroll() to monitor the current scroll position (fires once for each pixel scrolled)
Use $(window).scrollTop() to get the current scroll position*
Use $(element).offset() to get the .top and .left coords of the element*
Use $(window).height() to get the height of the viewport
* calculated from top of document
You want to calculate how far below the bottom of the viewport is the element you are scrolling to. That calculation (simplified) is:
element_position - viewport_height = distance below the fold
Important: Note that window.scroll() fires once for each pixel scrolled. This is great, but it can bog down your app. The usual solution is to implement a debouncer. See:
https://davidwalsh.name/javascript-debounce-function
Here is an example:
var st = $(window).scrollTop();
var fin = calcWhenDivInWindow();
var off = 20; //How far counter div must be above bottom of screen before count begins
$(window).scroll(function(){
st = $(this).scrollTop();
if (st > fin+off) doCount();
});
function doCount(){
$('.counter').each(function() {
var $this = $(this),
countTo = $this.attr('data-count');
$({ countNum: $this.text()}).animate({
countNum: countTo
},
{
duration: 8000,
easing:'linear',
step: function() {
$this.text(Math.floor(this.countNum));
},
complete: function() {
$this.text(this.countNum);
}
});
});
}
function calcWhenDivInWindow(){
var tt = $('.content').offset().top;
var th = $('.content').outerHeight();
var ww = $(window).height();
return tt+th-ww;
}
*{font-family:Arial;font-size:1.2rem;}
.stuff1{height:80vh;width:98vw;background:pink;}
.stuff2{height:50vh;width:98vw;background:lightblue;}
.stuff3{height:50vh;width:98vw;background:palegreen;}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/countup#1.8.2/dist/countUp.min.js">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
<div class="stuff1">Stuff n Nonsense (scroll slowly...)</div>
<div class="stuff2">More Stuff n Nonsense</div>
<div class="content bg-warning">
<div class="row m-0 py-5 d-flex justify-content-center">
<div class="counter" data-count="2200" style="color: white; font-size: 32px; font-weight: bold;">0</div>
</div>
</div>
<div class="stuff3"></div>

Related

jQuery animtion stops when user scrolls

I'm using multiple number elements on my site which are counting up after hitting the visible area of the viewport.
That part works until the user manually scrolls the page. If the user scrolls up or down, the animation stops for a second and repeats when the user don't scroll anymore. It looks very buggy.
If I try to replicate the problem in a fiddle, the same code always works without the "stuttering"?
jQuery(function($) {
$(function($, win) {
$.fn.inViewport = function(cb) {
return this.each(function(i, el) {
function visPx() {
var H = $(this).height(),
r = el.getBoundingClientRect(),
t = r.top,
b = r.bottom;
return cb.call(el, Math.max(0, t > 0 ? H - t : (b < H ? b : H)));
}
visPx();
$(win).on("resize scroll", visPx);
});
};
}(jQuery, window));
$(".fig-number").inViewport(function(px) {
// if px>0 (entered V.port) and
// if prop initNumAnim flag is not yet set = Animate numbers
if (px > 0 && !this.initNumAnim) {
this.initNumAnim = true; // Set flag to true to prevent re-running the same animation
$(this).prop('Counter', 0).animate({
Counter: $(this).text()
}, {
duration: 10000,
step: function(now) {
$(this).text(Math.ceil(now));
}
});
}
});
});
html,
body {
height: 100%;
}
.spacer {
height: 100%;
width: 100%;
display: block;
background: red;
color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="spacer">
scroll down
</div>
<div class="number-box">
<h1 class="fig-number">1000</h1>
<h1 class="fig-number">1500</h1>
</div>
<div class="spacer">
scroll down
</div>
<div class="number-box">
<h1 class="fig-number">2000</h1>
<h1 class="fig-number">2500</h1>
</div>
<div class="spacer">
scroll down
</div>
<div class="number-box">
<h1 class="fig-number">3000</h1>
<h1 class="fig-number">3500</h1>
</div>
The working fiddle (same code): https://jsfiddle.net/JSCray/r7g0vn93/3/

Make function to animate when in view more flexible

I have some code to animate a couple of numbers when scrolled into view. Everything is working fine. However, currently it only works with ONE set of numbers and tied to ONE div (with an id). I'd like to make it become more flexible, making it work for more than 1 sets of numbers.The DIV ids can be counter1, counter2, counter3 etc.
var a = 0;
$(window).scroll(function() {
var oTop = $('#counter1').offset().top - window.innerHeight;
if (a == 0 && $(window).scrollTop() > oTop) {
$('.counter-value').each(function() {
var $this = $(this),
countTo = $this.attr('data-count');
...
Here's the pen:
https://codepen.io/anon/pen/bQWpjJ
Thanks for you help!
Perhaps you could revise your implementation by first iterating over each counter using $('#counter1, #counter2').each( .. ).
Inside of each iteration, you'd effectively re-use your existing code by setting up a window.scroll() handler for that counter instance.
You'd also move var a = 0 inside of the iteration, so that your code tracks the unique scroll offset for this counter instance.
Finally, you'd want to ensure that you select '.counter-value' elements for the current counter instance of the iteration:
$('#counter1, #counter2').each(function() {
var a = 0;
var counter = $(this);
$(window).scroll(function() {
var oTop = counter.offset().top - window.innerHeight;
if (a == 0 && $(window).scrollTop() > oTop) {
$('.counter-value', counter).each(function() {
var $this = $(this),
countTo = $this.attr('data-count');
$({
countNum: $this.text()
}).animate({
countNum: countTo
},
{
duration: 2000,
easing: 'swing',
step: function() {
$this.text(Math.floor(this.countNum));
},
complete: function() {
$this.text(this.countNum);
}
});
});
a = 1;
}
});
})
.spacing {
width:100%;
height: 1280px;
position:relative;
}
.counter {text-align:center}
.counter-value {display:inline-block; padding:20px 40px; margin:0 20px; border:1px solid #ddd; font-family:Arial; font-size:50px; font-weight:bold}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="spacing"></div>
<div id="counter1">
<div class="counter-value" data-count="300">0</div>
<div class="counter-value" data-count="400">100</div>
<div class="counter-value" data-count="1500">200</div>
</div>
<div class="spacing"></div>
<div id="counter2">
<div class="counter-value" data-count="500">100</div>
<div class="counter-value" data-count="600">200</div>
<div class="counter-value" data-count="1700">300</div>
</div>
<div class="spacing"></div>
Here's a working codepen - hope that helps!

One of the sections is randomly crashing on the page

I have a one page site with few sections.. Bootstrap template. One of the sections cause a bug after few refreshes of the site. There are no errors in the console.. nothing and I just don't know how to trace and find the problem.
This is the html part of this section
<section id="portfolio" class="section portfolio padding-large text-center" data-section-name="portfolio">
<div class="container margin-bottom-medium">
<div class="row margin-bottom-medium wow fadeInUp">
<h2> Portfolio </h2>
<div class="line main-bg"></div>
</div>
</div>
<div class="portfolio-wrapper margin-bottom-medium">
<div class="portfolio-item">
<div class="portfolio" style="margin-left:5px;"><a href="#" data-lightbox-gallery="portfolio"><img src="img/portfolio/release02-360x360.jpg" alt="">
<div class="portfolio-overlay hvr-rectangle-out">
<h2 class="margin-bottom-small">
<strong class="white-color bold-text" style="font-size: 20px;">Portfolio</strong>
</h2>
<div class="button">Check item</div>
</div>
</a>
</div>
</div>
</div>
<div class="button light margin-top-medium margin-bottom-medium hvr-grow"><i class="icon-reload"></i> Check All</div>
</section>
This is what I have in main.js from the portfolio section
var container = $('.portfolio-wrapper'); // portfoolio container
container.isotope({
itemSelector: '.portfolio-item',
animationEngine: 'best-available',
animationOptions: {
duration: 200,
queue: false
},
layoutMode: 'fitRows'
});
// Split columns for different size layout
function splitColumns() {
var windowWidth = $(window).width(),
columnNumber = 1; // default column number
if (windowWidth > 1200) {
columnNumber = 4;
} else if (windowWidth > 767) {
columnNumber = 3;
} else if (windowWidth > 600) {
columnNumber = 2;
}
return columnNumber;
}
// Set width for portfolio item
function setColumns() {
var windowWidth = $(window).width(),
columnNumber = splitColumns(),
postWidth = Math.floor(windowWidth / columnNumber);
container.find('.portfolio-item').each(function() {
$(this).css({
width: postWidth + 'px'
});
});
}
// initialize isotope
function initIsotope() {
setColumns();
container.isotope('layout');
}
container.imagesLoaded(function() {
setColumns();
});
$(window).bind('resize', function() {
initIsotope();
});
initIsotope();
Here are two images in normal state and when it's crashed. Normal behavior
Here is when it crashed
I really don't have an idea of what can be the issue since there are no errors at all. I think that may be a js problem but again I'm not sure.
Edit: Here are the JS which are included in footer in this order:
<script src="js/vendor/jquery-1.11.1.js"></script>
<script src="js/vendor/jquery-migrate-1.2.1.min.js"></script>
<script src="js/vendor/bootstrap.js"></script>
<script src="js/wow.min.js"></script>
<script src="js/twitterFetcher_min.js"></script>
<script src="js/jquery.easing.min.js"></script>
<script src="js/appear.js"></script>
<script src="js/jquery.circliful.min.js"></script>
<script src="js/owl.carousel.min.js"></script>
<script src="js/nivo-lightbox.min.js"></script>
<script src="js/isotope.pkgd.min.js"></script>
<script src="js/imagesloaded.pkgd.min.js"></script>
<script src="js/main.js"></script>
<script src="js/scrollify.js"></script>
Edit2: Wrapping like this?
// initialize isotope
function initIsotope() {
setColumns();
container.isotope('layout');
}
container.imagesLoaded(function() {
setColumns();
});
$(window).bind('resize', function() {
initIsotope();
});
//initIsotope();
setTimeout(function(){ initIsotope(); }, 0);
From having a look at similar masonry issues. I would suspect one of these two would solve your issue -
$(window).load(function(){
initIsotope()
});
// or
setTimeout(function(){ initIsotope(); }, 0);
Both these are masking the core issue - you should really be reading the section around unloaded media here - http://isotope.metafizzy.co/v1/docs/help.html
I would suggest your content is not fully loaded sometimes and isotope gets confused. Specifying the dimensions lets isotope render correctly despite the images not loading quickly enough. You need to follow this pattern so that slow connections work. Setting timeouts has its uses, but you should fix the underlying issue.

Ion.RangeSlider, slider width and position on update

I have a carousel which can be moved by Ion.RangeSlider. Slider width depends on number of carousel items.
Carousel itself also can be moved by mouse drag. I want to also update slider position when carousel is dragged so I initialized update for slider.
But when I update slider, it lost style attribute. So i made it to calculate this again. But also slider position appears to be not in the center at expected point, it moves to right.
Maybe it's easier to understand when viewing demo:
JSFIDDLE
Try to drag carousel to the end and you will see what the problem is.
jQuery(document).ready(function($){
var itemsNum = 0;
var owl = $('.owl-carousel');
owl.on('initialized.owl.carousel', function(event) {
var itemCount = event.item.count;
var size = event.page.size;
var dragLength = 100 / (itemCount/size);
$("#range").ionRangeSlider({
type: "single",
min: 1,
max: itemCount - (size - 1),
keyboard: true,
step: 1,
onChange: function (data) {
//owl.trigger('changed.owl.carousel', [???]);
owlTo = (data.from) - 1;
owl.trigger('to.owl.carousel', [owlTo, 500, true]);
}
});
$('.irs-slider.single').css('width', dragLength + "%")
});
//Слайдер
owl.owlCarousel({
loop:false,
margin:10,
nav:false,
dots: false,
slideBy:1,
items: 8,
responsiveClass:true,
responsive:{
0:{
items:2
},
500:{
items:3
},
600:{
items:4
},
700:{
items:5
},
800:{
items:6
},
900:{
items:7
},
1000:{
items:8
}
}
});
owl.on('dragged.owl.carousel', function(event) {
var itemCount = event.item.count;
var size = event.page.size;
var curItem = event.item.index + 1;
var dragLength = 100 / (itemCount/size);
console.log(curItem);
$("#range").data("ionRangeSlider").update({from: curItem});
$('.irs-slider.single').css('width', dragLength + "%");
});
owl.on('resized.owl.carousel', function(event) {
var itemCount = event.item.count;
var size = event.page.size;
var curItem = event.item.index + 1;
var dragLength = 100 / (itemCount/size);
$("#range").data("ionRangeSlider").update({
max: itemCount - (size - 1),
from: curItem
});
$('.irs-slider.single').css('width', dragLength + "%");
});
})
.owl-carousel .owl-item {
background-color: #ccc;
text-align: center;
}
.irs-bar, .irs-bar-edge, .irs-min, .irs-max, .irs-from, .irs-to, .irs-single{opacity: 0;}
.irs-slider.single {
transition: all 0.3s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.0.0-beta.3/assets/owl.theme.default.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.0.0-beta.3/owl.carousel.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.0.0-beta.3/assets/owl.carousel.min.css" rel="stylesheet"/>
<link href="http://ionden.com/a/plugins/ion.rangeSlider/static/css/ion.rangeSlider.skinHTML5.css" rel="stylesheet"/>
<script src="http://ionden.com/a/plugins/ion.rangeSlider/static/js/ion-rangeSlider/ion.rangeSlider.js"></script>
<link href="http://ionden.com/a/plugins/ion.rangeSlider/static/css/ion.rangeSlider.css" rel="stylesheet"/>
<div class="owl-carousel">
<div class="item"><h4>1</h4></div>
<div class="item"><h4>2</h4></div>
<div class="item"><h4>3</h4></div>
<div class="item"><h4>4</h4></div>
<div class="item"><h4>5</h4></div>
<div class="item"><h4>6</h4></div>
<div class="item"><h4>7</h4></div>
<div class="item"><h4>8</h4></div>
<div class="item"><h4>9</h4></div>
<div class="item"><h4>10</h4></div>
<div class="item"><h4>11</h4></div>
<div class="item"><h4>12</h4></div>
</div>
<div style="position: relative; padding: 20px 100px;">
<div><input type="text" id="range" value="" name="range" /></div>
</div>
The script is almost correct.
In the part where you update the ionRangeSlider after you dragged the owl.carousel there is:
var curItem = event.item.index + 1;.
That should be:
var curItem = event.item.index - 1;.
Check this fiddle:
http://jsfiddle.net/broezer/mwams6g2/28/
This only applies to a gallery of 4 items in a row.
update
However I do notice some quirks with the responsiveness.
So i reckon, it has to do something with that part of the script.
After some testing I found that changing values of items & the update function on the drag are related to each other. So be aware of this quirk.
You can set breakpoints in your drag function to change var curItem according to the breakpoints you defined in you owl.owCarousel settings

Scroll to Div inside Wrapper not Body

I am using jQuery code from http://www.dconnell.co.uk/blog/index.php/2012/03/12/scroll-to-any-element-using-jquery/ to scroll to a Div within a Wrapper.
The code provided animates the body (Scrolls the body down onClick) I am trying to animate the scroll inside a div, not body.
My code below:
HTML:
Scroll Down
<div class="Wrapper" style="height:400px; width:600px; overflow:hidden;">
<div id="Div1" style="height:200px; width:600px;"></div>
<div id="Div2" style="height:200px; width:600px;"></div>
<div id="Div3" style="height:200px; width:600px;"></div>
</div>
jQuery
function scrollToElement(selector, time, verticalOffset) {
time = typeof(time) != 'undefined' ? time : 1000;
verticalOffset = typeof(verticalOffset) != 'undefined' ? verticalOffset : 0;
element = $(selector);
offset = element.offset();
offsetTop = offset.top + verticalOffset;
$('html, body').animate({
scrollTop: offsetTop
}, time);
}
$(document).ready(function() {
$('a.ScrollDown').click(function (e) {
e.preventDefault();
scrollToElement('#Div3');
});
});
I know it has something to do with $('html, body').animate({ }) but im not sure how to change it.
Thanks
JSFiddle http://jsfiddle.net/3Cp5w/1/
Animate the wrapper instead of the body:
$('.Wrapper').animate({
scrollTop: offsetTop
}, time);
http://jsfiddle.net/robbyn/qU6F7/1/
Try the following Code
<body>
Scroll Down
<div class="Wrapper" style="height:200px; width:600px; overflow:scroll;">
<div id="Div1" style="height:200px; width:600px;background-color:red;" ></div>
<div id="Div2" style="height:200px; width:600px;background-color:green;" ></div>
<div id="Div3" style="height:200px; width:600px;background-color:yellow;" ></div>
</div>
</body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
function scrollToElement(selector, time, verticalOffset) {
time = typeof(time) != 'undefined' ? time : 1000;
verticalOffset = typeof(verticalOffset) != 'undefined' ? verticalOffset : 0;
element = $(selector);
offset = element.offset();
offsetTop = offset.top + verticalOffset;
$('.Wrapper').animate({
scrollTop: offsetTop
}, time);
}
$(document).ready(function() {
$('a.ScrollDown').click(function (e) {
e.preventDefault();
scrollToElement('#Div3');
});
});
</script>
i think you want div3 to scroll down inside Wrapper
you need to first specify the height of Wrapper to a small number so that scroll is visible
&
as you guessed change html,body to .Wrapper
do ask for help
Add the jQuery updated file reference
<script src="http://code.jquery.com/jquery-1.10.2.js" />

Categories