<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Document</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui">
</head>
<body>
<div class="scrolling">
<p>Крути вниз!</p>
<div class="number" data-max="30"></div>
<div class="number" data-max="450"></div>
<div class="number" data-max="7500"></div>
<div class="number" data-max="1000000"></div>
</div>
<style>
html {
text-align: center;
--angle: 5deg;
background: linear-gradient(calc(180deg + var(--angle)), transparent 47%, #f5f5f5 50%, transparent 0) 0 0,
linear-gradient(calc(180deg - var(--angle)), transparent 47%, #f5f5f5 50%, transparent 0) 100% 0;
background-size: 50% 8em;
background-repeat: repeat-y;
}
body {
margin: 0;
}
.scrolling {
overflow: auto;
height: 100vh;
}
.number {
font: 3em sans-serif;
margin: 25em auto;
}
.number::after {
background: rebeccapurple;
color: white;
border-radius: 50%;
padding: 2em;
counter-reset: number var(--value);
content: counter(number);
}
</style>
<script>
{
let inTheView = Symbol.for("inTheView");
let multiplier = Symbol.for("multiplier");
// Создадим новое событие, которое выстреливает, когда счётчик становится видимым
for(let element of document.querySelectorAll(".scrolling")) {
element.addEventListener("scroll", event => {
for(let numberElement of element.querySelectorAll(".number")) {
let y = (element.scrollTop - numberElement.offsetTop + element.offsetHeight) / (element.offsetHeight);
let isInTheView = y > 0 && y < 1;
if(numberElement[inTheView] !== isInTheView) {
numberElement[inTheView] = isInTheView;
if(isInTheView)
numberElement.dispatchEvent(new Event("number:visible", { bubbles: true }));
}
}
});
}
// обработка нового события
addEventListener("number:visible", event => {
loop.call(event.target);
console.log(event.target[inTheView]);
});
// анимация цифр запускается, когда срабатывает новое событие
function loop() {
if(this[multiplier] === 1) {
this[multiplier] = null;
return;
};
if(typeof this[multiplier] !== "number") {
this[multiplier] = 0;
this["dateStart"] = performance.now();
}
this[multiplier] = Math.min((performance.now() - this.dateStart) / 750, 1);
this.style.setProperty("--value", (this[multiplier] * this.getAttribute("data-max") | 0));
requestAnimationFrame(loop.bind(this));
}
};
</script>
</body>
</html>