Анимация запускается каждый раз, как только элемент входит в область видимости + только 4 <meter>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.skills { margin: 125vh 0; }
body::before { content: "Крути вниз!"; }
.skill {
width: 100%;
background: #eee;
padding-top: 2em;
display: flex;
flex-flow: wrap;
}
.skill .level {
flex: 1;
text-align: right;
font-size: 125%;
}
.skill meter {
width: 100%;
background: transparent;
}
.skill meter::-webkit-meter-bar {
background: transparent;
border: 0; /* убирает рамку в IE */
}
.skill meter::-moz-meter-bar {
background: currentColor;
}
.skill meter::-webkit-meter-optimum-value {
background: currentColor;
}
</style>
</head>
<body>
<section class="skills">
<meter min="0" max="100" value="98" style="color: #464c5c" title="Оптимизация"></meter>
<meter min="0" max="100" value="100" style="color: #6d747a" title="Кроссплатформенность"></meter>
<meter min="0" max="100" value="99" style="color: #8c8274" title="Удобство"></meter>
<meter min="0" max="100" value="99" style="color: #d2c6b4" title="Дизайн"></meter>
</section>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
jQuery.easing.easeOutQuart = function(x, t, b, c, d) {
return -c * (t /= d) * (t - 2) + b
};
jQuery(".skills meter").each(function(index, { value, title, style }) {
const descr = jQuery(`<span class="title">${title}</span>`);
const level = jQuery(`<span class="level" style="color: ${style.color}"></span>`);
const skill = jQuery(`<section class="skill"></section>`);
skill.append(descr, level);
$(this).replaceWith(skill);
skill.append(this);
document.addEventListener("meter:animate", () => jQuery({ value: 0 }).animate({ value }, {
duration: 1600,
easing: "easeOutQuart",
step: value => {
this.value = value;
level.text(`${value | 0}%`);
}
}));
});
let _isInView = false;
onscroll = () => {
const rect = document.querySelector(".skills").getBoundingClientRect();
const y = rect.bottom / (innerHeight + rect.bottom - rect.top);
const isInView = y > 0 && y < 1;
if(_isInView !== isInView) {
_isInView = isInView;
if(isInView)
document.dispatchEvent(new Event("meter:animate", { bubbles: true }));
}
};
</script>
</body>
</html>