Preface
Goal: Trigger animation on scroll using intersection observer.
Instead of using ready made animation, we can be brave enough to make our own custom animation. Such as this two cases.
- Javascript only, no stylesheet.
- Animation in stylesheet.
1: Numerator
Javascript only, no stylesheet.
Original Source
I have seen jquery numerator, and I want the native one. Luckily I found this one.
I use this source code, with a simple modification, to suit my need.
HTML Head
Add this javascript in the HTML head.
<script src="js-75/numerator.js"></script>
HTML: Bulma: Level
I’m using layout level from Bulma.
With additional dataset for counter number, that could be read from javascript.
<nav class="level">
<div class="level-item has-text-centered">
<div>
<p class="heading">Code</p>
<p class="title numerator"
data-number="8"
>0</p>
</div>
</div>
<div ...>
<div ...>
<div ...>
<div ...>
</nav>
Javascript Skeleton
Consider adapt the javascript above:
document.addEventListener(
"DOMContentLoaded", function(event) {
const ...
let counterObserver =
new IntersectionObserver( (entries) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0) {
const ...
const animate = () => {...}
animate();
}
});
});
countersToObserve.forEach((element) => {
counterObserver.observe(element);
});
});
I know this is a long script.
Javascript: Variable Initialization
const countersToObserve = document
.querySelectorAll('.numerator');
const stepfactor = 20;
const timeout = Math.ceil(1000/stepfactor);
Javascript: Scroll Event
If the element is happened to be displayed, we animate from zero to counter number.
if (entry.intersectionRatio > 0) {
const counter = entry.target;
const maxnum = +counter.dataset.number;
// reset on scroll
counter.innerText = 0;
const animate = () => {...}
animate();
}
Javascript: Animate Counter
And finally the numerator animation itself
const animate = () => {
const current = +counter.innerText;
const append = (maxnum-current) / stepfactor;
if (current < maxnum) {
newcurrent = Math.ceil(current + append);
counter.innerText = newcurrent;
setTimeout(animate, timeout);
} else {
counter.innerText = maxnum;
}
}
It is home made. And I open for better enhancement.
Preview
Enjoy the result.
2: Progressbar
Animation in stylesheet.
HTML Head
Add both, stylesheet and javascript in the HTML head.
<link rel="stylesheet" type="text/css" href="css-76/progressbar.css">
<script src="js-76/progressbar.js"></script>
HTML: Custom Progress Bar
Franken Component
I don’t feel like bulma progressbar suit my need, so I grab other html code from w3school, but with modification. And I also grab the stylesheet, from somewhere in the internet. But I wrote the javascript myself. I know it is rather a frankenstein, but it works, at least for me.
<div id="container__bar"
class="has-text-left"
data-count="114">
<div class="my__progressbar"
data-count="8">
<div class="my__progressbar_text"
>Code: <span class="my__progressbar_perc"
></span></div>
<div class="my__inner_bar"></div>
</div>
<div ...>
<div ...>
<div ...>
<div ...>
</div>
Note that I also set the total count. Sum of all element, on the first place.
Stylesheet: Progress Bar Animation
The keyframes is very simple.
@keyframes my__progress__frames {
from { width: 0%; }
to { width: 100%; }
}
I’m using material color gradient for background.
.my__inner_bar {
background-color: #BBDEFB;
background-image: linear-gradient(
45deg, #2196F3 10px,
transparent 20px, transparent 25%,
#64B5F6 50%, #90CAF9 75%,
transparent 75%, transparent);
}
The animation is
.my__inner_bar {
animation-duration: 5s;
animation-fill-mode: both;
animation-name: my__progress__frames;
animation-iteration-count: 1;
animation-timing-function: ease;
animation-direction: normal;
}
And additional cosmetics.
.my__progressbar_text {
white-space: nowrap;
}
.my__inner_bar {
height: 10px;
border-radius: 10px;
}
Javascript Skeleton
We need to make custom javascript:
document.addEventListener(
"DOMContentLoaded", function(event) {
...
let containerBarObserver =
new IntersectionObserver( (entries) => {
entries.forEach((entry) => {
...
});
});
containerBarObserver.observe(containerBar);
});
I know this is script is longer.
Javascript: Variable Initialization
const progressbars = document
.querySelectorAll(".my__progressbar");
const myBars = document
.querySelectorAll(".my__inner_bar");
const containerBar = document
.getElementById("container__bar");
const total = containerBar.dataset.count;
Javascript: Scroll Event
If the element is happened to be displayed,
we reanimate each progressbar.
The point is just toggling,
the presence of each my__inner_bar
class,
as the page scrolled.
new IntersectionObserver( (entries) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0) {
// reset on scroll
myBars.forEach((element) => {
element.classList.add('my__inner_bar');
});
progressbars.forEach((element) => {...});
} else {
myBars.forEach((element) => {
element.classList.remove('my__inner_bar');
});
}
});
Javascript: Animate Counter
And finally the numerator animation itself.
progressbars.forEach((element) => {
const count = element.dataset.count;
const width = Math.round((count/total)*100);
element.style.width = width + "%";
const percText = element
.getElementsByClassName("my__progressbar_perc")[0]
percText.innerText = width + "%";
});
Again, it is home made. And I open for better enhancement.
Preview
Enjoy the result.
Conclusion
What do you think?
Thank you for reading.
Farewell. We shall meet again.