Search code examples
javascriptcssanimationsvgcss-transforms

getBoundingClientRect seems inaccurate when element is being transformed


I'm trying to use a CSS transform to translate an absolutely positioned SVG element diagonally down/right across the viewport on scroll, and I need the individual paths to change their fill colour as they cross over the next element, but it seems like getBoundingClientRect isn't returning the correct values during scroll.

Here's a demo: https://codepen.io/ahollister/pen/mdyvQLN

And here's the JS:

window.addEventListener('scroll', function(e) {
  let scrollPercent = ( window.scrollY ) / ( document.body.clientHeight - window.innerHeight );
  let scrollPercentRounded = Math.round( scrollPercent * 100 );
  document.querySelector('.arrows-container').style.transform = `translate(${scrollPercentRounded}%, ${scrollPercentRounded}%)`

  const arrowsArray = document.querySelectorAll('svg path');
  const el = document.querySelector('.bottom');
    for ( const a of arrowsArray ) {
    if ( a.getBoundingClientRect().bottom > el.offsetTop ) {
            a.style.fill = 'red';
    }
    }
});

I'm trying to get each arrow to change fill colour as they cross the line into the .bottom element, if you comment out the transform line it seems to calculate everything correctly:

document.querySelector('.arrows-container').style.transform = `translate(${scrollPercentRounded}%, ${scrollPercentRounded}%)`

Anyone come across this issue before? How can I get getBoundingClientRect to return the correct values in this instance?


Solution

  • Figured it out, in the end I wasn't taking the scrollY position into account. Ended up with a sort of rudimentary collision detection function which looks like this:

    function colorOnCollision( svgPaths, el, color1, color2 ) {
        for ( const path of svgPaths ) {
            // If path is over el1 or el2
            if ( path.getBoundingClientRect().bottom < ( el.offsetHeight - window.scrollY ) ) {
                path.style.fill = color1;
            } else {
                path.style.fill = color2;
            }
        }
    }