Problem
I created a counter using HTML, CSS and JS (such as satisfied customer numbers, branch numbers, etc.) The counter is also animated but since it's down the page, I'd like to animate it only when it gets to that point on the page. How do I do with the js?
const counters = document.querySelectorAll('.value');
const speed = 400;
counters.forEach( counter => {
const animate = () => {
const value = counter.getAttribute('akhi');
const data = counter.innerText;
const time = value / speed;
if(data < value) {
counter.innerText = Math.ceil(data time);
setTimeout(animate, 1);
}else{
counter.innerText = value;
}
}
animate();
});
.counter-box {
display: block;
background: #f6f6f6;
padding: 40px 20px 37px;
text-align: center
}
.counter-box p {
margin: 5px 0 0;
padding: 0;
color: #909090;
font-size: 18px;
font-weight: 500
}
.counter {
display: block;
font-size: 32px;
font-weight: 700;
color: #666;
line-height: 28px
}
.counter-box.colored {
background: #eab736;
}
.counter-box.colored p,
.counter-box.colored .counter {
color: #fff;
}
<div >
<div >
<div >
<div >
<span akhi="560">0</span>
<p>Countries visited</p>
</div>
</div>
<div >
<div >
<span akhi="3275">0</span>
<p>Registered travellers</p>
</div>
</div>
<div >
<div >
<span id="conta" akhi="289">0</span>
<p>Partners</p>
</div>
</div>
</div>
</div>
What I have tried
i had tried with
const target = document.querySelector('.counter');
observer.observe(target);
but it doesn't seem to work. Many thanks to whoever can help me.
CodePudding user response:
I would recommend, as others have suggested, to use the Intersection Observer API to animate your elements once they appear in the viewport.
The idea is simple, we'll create an observer
that will observe the counters
to animate and we're going to configure it so that it calls the animate
function once a counter
is fully visible in the viewport.
You may learn more about the options that an IntersectionObserver
can accept in order to customize its behavior. Meanwhile, here's a live demo that illustrates how to make the counters
animate once they appear in the screen (the code below has some helpful comments):
const counters = document.querySelectorAll('.value'),
speed = 400,
/**
* create an IntersectionObserver with the specified callback that will be executed for each intersection change for every counter we have.
* You may customize the options (2nd argument) per you requirement
*/
observer = new IntersectionObserver(
entries => entries.forEach(entry => entry.isIntersecting && animate(entry.target)),
{
threshold: 1 // tells the browser that we only need to execute the callback only when an element (counter) is fully visible in the viewport
}
),
// the animate function now accepts a counter (HTML element)
animate = counter => {
const value = counter.dataset.akhi,
data = counter.innerText,
time = value / speed;
if (data < value) {
counter.innerText = Math.ceil(data time);
setTimeout(() => animate(counter), 1);
} else {
counter.innerText = value;
}
};
// attach the counters to the observer
counters.forEach(c => observer.observe(c));
.counter-box {
display: block;
background: #f6f6f6;
padding: 40px 20px 37px;
text-align: center
}
.counter-box p {
margin: 5px 0 0;
padding: 0;
color: #909090;
font-size: 18px;
font-weight: 500
}
.counter {
display: block;
font-size: 32px;
font-weight: 700;
color: #666;
line-height: 28px
}
.counter-box.colored {
background: #eab736;
}
.counter-box.colored p,
.counter-box.colored .counter {
color: #fff;
}
<div >
<div >
<div >
<div >
<!-- it is recommended to use "data-*" attributes to cache data that we might use later. The "data-akhi" contains the number to animate -->
<span data-akhi="560">0</span>
<p>Countries visited</p>
</div>
</div>
<div >
<div >
<span data-akhi="3275">0</span>
<p>Registered travellers</p>
</div>
</div>
<div >
<div >
<span id="conta" data-akhi="289">0</span>
<p>Partners</p>
</div>
</div>
</div>
</div>
CodePudding user response:
As others suggested, you should use Intersection Observer. This is how I'd do: Scrolldown the snippet in order to see the counter animating up once is on the screen.
const counters = document.querySelectorAll('.value');
const speed = 400;
const observer = new IntersectionObserver( items => {
if(items[0].isIntersecting) {
const target = items[0].target;
const animate = () => {
const value = target.getAttribute('akhi');
const data = target.innerText;
const time = value / speed;
if(data < value) {
target.innerText = Math.ceil(data time);
setTimeout(animate, 1);
}else{
target.innerText = value;
}
}
animate();
observer.unobserve(target);
}
})
counters.forEach( counter => observer.observe(counter));
.counter-box {
display: block;
background: #f6f6f6;
padding: 40px 20px 37px;
text-align: center
}
.counter-box p {
margin: 5px 0 0;
padding: 0;
color: #909090;
font-size: 18px;
font-weight: 500
}
.counter {
display: block;
font-size: 32px;
font-weight: 700;
color: #666;
line-height: 28px
}
.counter-box.colored {
background: #eab736;
}
.counter-box.colored p,
.counter-box.colored .counter {
color: #fff;
}
<div style="height: 600px;">
</div>
<div >
<div >
<div >
<div >
<span akhi="560">0</span>
<p>Countries visited</p>
</div>
</div>
<div >
<div >
<span akhi="3275">0</span>
<p>Registered travellers</p>
</div>
</div>
<div >
<div >
<span id="conta" akhi="289">0</span>
<p>Partners</p>
</div>
</div>
</div>
</div>