<div class="mol-counter">
<div class="counter-item">
<div class="number">
<span class="count-number">86</span>
</div>
<div class="text">medewerkers</div>
</div>
<span class="atm-slash " style="width: 1px; height: 30px;"></span>
<div class="counter-item">
<div class="number">
+
<span class="count-number">245</span>
</div>
<div class="text">tevreden klanten</div>
</div>
<span class="atm-slash " style="width: 1px; height: 30px;"></span>
<div class="counter-item">
<div class="number">
<span class="count-number">100</span>
%
</div>
<div class="text">aandacht voor jou</div>
</div>
<span class="atm-slash " style="width: 1px; height: 30px;"></span>
</div>
<div class="mol-counter">
{{#each items}}
<div class="counter-item">
{{#if number}}
<div class="number">
{{#if character-before}}{{character-before}}{{/if}}
<span class="count-number">{{number}}</span>
{{#if character-after}}{{character-after}}{{/if}}
</div>
{{/if}}
{{#if text}}
<div class="text">{{text}}</div>
{{/if}}
</div>
{{render '@slash'}}
{{/each}}
</div>
{
"items": [
{
"text": "medewerkers",
"number": "86"
},
{
"text": "tevreden klanten",
"number": "245",
"character-before": "+"
},
{
"text": "aandacht voor jou",
"number": "100",
"character-after": "%"
}
]
}
.mol-counter {
@apply flex justify-between items-center;
.counter-item {
@apply relative;
@apply text-center;
.number {
@apply text-h2;
@apply font-medium;
@apply text-black;
@apply whitespace-nowrap;
}
.text {
@apply px-4;
}
}
.atm-slash {
@apply block;
}
.atm-slash:nth-child(4),
.counter-item:nth-child(5) {
@apply hidden md:block;
}
.atm-slash:nth-child(6) {
@apply hidden;
}
}
const COUNTER_ANIMATION_LENGTH = 4000;
const COUNTER_SELECTOR = ".mol-counter .count-number";
let counterElements;
window.addEventListener("load", () => {
const observer = new IntersectionObserver(function (entries) {
if (entries.some(entry => entry.intersectionRatio >= 0.9 && entry.target.dataset.started === undefined)) {
entries.forEach(el => {
if (el.intersectionRatio >= 0.9 && el.target.dataset.started === undefined) {
el.target.dataset.started = true;
}
});
window.requestAnimationFrame(drawFrame);
}
}, { threshold: 0.9 });
counterElements = document.querySelectorAll(COUNTER_SELECTOR);
counterElements.forEach(el => observer.observe(el));
}, false);
function drawFrame(timestamp) {
let runAgain = false;
for (let element of counterElements) {
if (element.dataset.started === undefined) continue;
let start = element.dataset.start;
let previousTimeStamp = element.dataset.previousTimeStamp;
if (start === undefined) {
start = timestamp;
element.dataset.start = timestamp;
element.dataset.maxCount = element.innerHTML;
runAgain = true;
}
let maxCount = parseInt(element.dataset.maxCount);
const elapsed = timestamp - start;
if (previousTimeStamp !== timestamp) {
const absoluteProgress = Math.min(elapsed / COUNTER_ANIMATION_LENGTH, 1);
const easedProgress = easeOutQuint(absoluteProgress);
element.innerHTML = Math.round(maxCount * easedProgress);
if (elapsed <= COUNTER_ANIMATION_LENGTH) {
runAgain = true;
}
}
if (elapsed < COUNTER_ANIMATION_LENGTH) {
element.dataset.previousTimeStamp = timestamp;
}
}
if (runAgain) {
window.requestAnimationFrame(drawFrame);
}
}
// https://easings.net
function easeOutQuint(x) {
return x === 1 ? 1 : 1 - Math.pow(1 - x, 5);
}
No notes defined.