I have a circle bar animation that should trigger when in view. The effect works on every Browser but in Google Chrome and I can't understand why.
Here an example: https://career.foundationbox.studio
I made a Pen to isolate the problem: https://codepen.io/davide-fanchin/pen/xxydBNY
I suspect is something related to the Javascript code: Any suggestion is welcome.
document.addEventListener('DOMContentLoaded', () => {
const circleProgressBar = document.querySelector('.circle-progress-value');
const circleProgressNumber = document.querySelector('.circle-progress-number');
const checkmarkContainer = document.querySelector('.checkmark-container');
const countUp = (start, end, element, duration) => {
let current = start;
const stepTime = Math.abs(Math.floor(duration / (end - start)));
const timer = setInterval(() => {
if (current <= end) {
element.textContent = current++;
} else {
clearInterval(timer);
}
}, stepTime);
};
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
circleProgressBar.style.transitionDuration = `2000ms`;
circleProgressBar.style.strokeDashoffset = '0';
countUp(0, 50, circleProgressNumber, 2000);
setTimeout(() => {
checkmarkContainer.style.opacity = '1';
}, 2000);
} else {
circleProgressBar.style.strokeDashoffset = '283';
circleProgressNumber.textContent = '0';
checkmarkContainer.style.opacity = '0';
}
});
}, {
threshold: 0.5
});
observer.observe(circleProgressBar);
});
.circle-progress-container {
position: relative;
display: inline-block;
width: 100%;
padding-bottom: 100%;
overflow: visible;
}
.bar-container {
width: 400px;
}
.circle-progress {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: rotate(-90deg);
}
.circle-progress-bg,
.circle-progress-value {
stroke-linecap: round;
}
.circle-progress-bg {
stroke: lightgray;
}
.circle-progress-value {
stroke: url(#circle-gradient-%id%);
stroke-dasharray: 283;
stroke-dashoffset: 283;
transition: stroke-dashoffset 2s;
}
.circle-progress-text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 24px;
font-weight: bold;
text-align: center;
/* Added to center the text */
}
.circle-progress-number-container {
display: flex;
align-items: baseline;
}
.circle-progress-number {
transition: 2s;
}
.circle-progress-label {
font-size: 12px;
font-weight: normal;
margin-top: -10px;
/* Adjust the margin to separate the count up number and the word "Projects" */
}
.checkmark-container {
position: absolute;
top: -2px;
/* Adjusted to lower the checkmark icon and circle */
left: 50%;
transform: translateX(-50%);
opacity: 0;
transition: opacity 0.5s, transform 0.5s;
}
.checkmark-circle {
position: relative;
width: calc(%id=iconSize%px + 5px);
height: calc(%id=iconSize%px + 5px);
background-color: white;
border-radius: 50%;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
}
.checkmark {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
stroke: deepskyblue;
stroke-linecap: round;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
.circle-progress-value {
stroke: deepskyblue;
stroke-dasharray: 283;
stroke-dashoffset: 283;
-webkit-transition: stroke-dashoffset 2s;
}
}
<div class="bar-container">
<div class="circle-progress-container">
<svg class="circle-progress" viewBox="0 0 100 100">
<defs>
<linearGradient id="circle-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:red"/>
<stop offset="100%" style="stop-color:blue"/>
</linearGradient>
</defs>
<circle class="circle-progress-bg" cx="50" cy="50" r="45" stroke-width="1" fill="none" />
<circle class="circle-progress-value" cx="50" cy="50" r="45" stroke-width="1" fill="none" />
</svg>
<div class="circle-progress-text">
<div class="circle-progress-number-container" style="font-size: 40px;">
<span class="circle-progress-number">50</span><span>+</span>
</div>
<div class="circle-progress-label" style="font-size: 14px;">Projects</div>
</div>
<div class="checkmark-container">
<div class="checkmark-circle">
<svg class="checkmark" width="16px" height="16px" viewBox="0 0 20 20">
<polyline points="6,10 9,13 14,8" stroke-width="2" fill="none" />
</svg>
</div>
</div>
</div>
</div>
The problem seems to be that Chrome (and Edge) does not see intersections of the SVG circle.
This snippet puts the observer onto the whole containing element and that seems to be OK.
<!document html>
<html>
<head>
<style>
.circle-progress-container {
position: relative;
display: inline-block;
width: 100%;
padding-bottom: 100%;
overflow: visible;
}
.bar-container {
width: 400px;
}
.circle-progress {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: rotate(-90deg);
}
.circle-progress-bg,
.circle-progress-value {
stroke-linecap: round;
}
.circle-progress-bg {
stroke: lightgray;
}
.circle-progress-value {
stroke: url(#circle-gradient-%id%);
stroke-dasharray: 283;
stroke-dashoffset: 283;
transition: stroke-dashoffset 2s;
}
.circle-progress-text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 24px;
font-weight: bold;
text-align: center;
/* Added to center the text */
}
.circle-progress-number-container {
display: flex;
align-items: baseline;
}
.circle-progress-number {
transition: 2s;
}
.circle-progress-label {
font-size: 12px;
font-weight: normal;
margin-top: -10px;
/* Adjust the margin to separate the count up number and the word "Projects" */
}
.checkmark-container {
position: absolute;
top: -2px;
/* Adjusted to lower the checkmark icon and circle */
left: 50%;
transform: translateX(-50%);
opacity: 0;
transition: opacity 0.5s, transform 0.5s;
}
.checkmark-circle {
position: relative;
width: calc(%id=iconSize%px + 5px);
height: calc(%id=iconSize%px + 5px);
background-color: white;
border-radius: 50%;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
}
.checkmark {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
stroke: deepskyblue;
stroke-linecap: round;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
.circle-progress-value {
stroke: deepskyblue;
stroke-dasharray: 283;
stroke-dashoffset: 283;
-webkit-transition: stroke-dashoffset 2s;
}
}
</style>
</head>
<body>
<div style="height: 150vh; background: pink; font-size: 72px;">PLEASE SCROLL DOWN</div>
<div class="bar-container">
<div class="circle-progress-container">
<svg class="circle-progress" viewBox="0 0 100 100">
<defs>
<linearGradient id="circle-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:red"/>
<stop offset="100%" style="stop-color:blue"/>
</linearGradient>
</defs>
<circle class="circle-progress-bg" cx="50" cy="50" r="45" stroke-width="1" fill="none" />
<circle class="circle-progress-value" cx="50" cy="50" r="45" stroke-width="1" fill="none" />
</svg>
<div class="circle-progress-text">
<div class="circle-progress-number-container" style="font-size: 40px;">
<span class="circle-progress-number">50</span><span>+</span>
</div>
<div class="circle-progress-label" style="font-size: 14px;">Projects</div>
</div>
<div class="checkmark-container">
<div class="checkmark-circle">
<svg class="checkmark" width="16px" height="16px" viewBox="0 0 20 20">
<polyline points="6,10 9,13 14,8" stroke-width="2" fill="none" />
</svg>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const circleProgressBar = document.querySelector('.circle-progress-value');
const circleProgressNumber = document.querySelector('.circle-progress-number');
const checkmarkContainer = document.querySelector('.checkmark-container');
const circleProgressContainer = document.querySelector('.circle-progress-container');
const countUp = (start, end, element, duration) => {
let current = start;
const stepTime = Math.abs(Math.floor(duration / (end - start)));
const timer = setInterval(() => {
if (current <= end) {
element.textContent = current++;
} else {
clearInterval(timer);
}
}, stepTime);
};
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
circleProgressBar.style.transitionDuration = `2000ms`;
circleProgressBar.style.strokeDashoffset = '0';
countUp(0, 50, circleProgressNumber, 2000);
setTimeout(() => {
checkmarkContainer.style.opacity = '1';
}, 2000);
} else {
circleProgressBar.style.strokeDashoffset = '283';
circleProgressNumber.textContent = '0';
checkmarkContainer.style.opacity = '0';
}
});
}, {
threshold: 0.5
});
observer.observe(circleProgressContainer);
});
</script>
</body>
</head>