Get screen opacity to be 0.1 at the top of scroll, but 0.95 at bottom and calculating everything in between according to the current scroll position

Viewed 54

I understand this may be more of a math question than a programming one, but it is worth trying my luck here: I have an app which a part of the screen's opacity is dependant on at what point the scroll is at.

The further down we scroll, the larger the opacity is. The starting point (at scrollTop 0), is to have opacity set to 0.1, and when on the bottom of the page, we want opacity to be set to 0.95, with a continuous scroll in between.

As it stands, I am using the following formula:

const calculateOpacityOnScroll = (evt) => {
  const { scrollHeight, scrollTop, clientHeight } = evt.target;
  const opacityValue = (0.95*clientHeight)/(scrollHeight - scrollTop);
  setOpacity(opacityValue);
};

This works fine, but if I go back to the top, the opacity goes not reach 0.1 as required.

I also know an alternative would be simply to do manually set it to 0.1 if scrollTop is 0, for example:

const calculateOpacityOnScroll = (evt) => {
  const { scrollHeight, scrollTop, clientHeight } = evt.target;
  const opacityValue = (0.95*clientHeight)/(scrollHeight - scrollTop);
  if (scrollTop) {
    setOpacity(opacityValue);
  } else {
    setOpacity(0.1)
  }
};

However, this removed the smoothness of the transition, so I would like to avoid it.

So, under these circumstances what would be the proper way to calculate the opacity, with the top of the scroll setting it to 0.1 but the bottom to 0.95?

Here is a sample app which I currently have everything working as intended:

const App = () => {
  const [opacity, setOpacity] = React.useState(0.1);
  const [params, setParams] = React.useState({})
  const calculateOpacityOnScroll = (evt) => {
    const { scrollHeight, scrollTop, clientHeight } = evt.target;
    const opacityValue = (0.95*clientHeight)/(scrollHeight - scrollTop);
    setOpacity(opacityValue);
        setParams({scrollHeight, scrollTop, clientHeight})
  };
  
  const incorrectMessage = opacity !== 0.1 && params.scrollTop === 0
    ? <span style={{ color: 'red' }}> This is incorrect! At the top, it must be 0.1!</span>
    : null

  return (
  <div>
   
    <div className='params'>
           <div
      className='sample'
      style={{
        background: 'blue',
        opacity
      }}>Sample</div>
      <h4>Opacity: {opacity} {incorrectMessage}</h4>
      <h4>scrollTop: {params.scrollTop || 0}</h4>
      <h4>scrollHeight: {params.scrollHeight || '?'}</h4>
      <h4>clientHeight: {params.clientHeight || '?'}</h4>
    </div>
       
    <div className='content' onScroll={calculateOpacityOnScroll}>
      <h4 style={{ zIndex: 200 }}>Scroll here!</h4>
      <p style={{ zIndex: 200 }}>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </p>

    <p style={{ zIndex: 200 }}>Et tortor consequat id porta nibh venenatis. Consequat id porta nibh venenatis cras sed felis. Porttitor rhoncus dolor purus non. Eu turpis egestas pretium aenean pharetra magna. Eget velit aliquet sagittis id consectetur purus ut. In mollis nunc sed id semper risus in hendrerit gravida. Nascetur ridiculus mus mauris vitae ultricies leo integer malesuada nunc. Dictumst vestibulum rhoncus est pellentesque elit ullamcorper dignissim. Bibendum enim facilisis gravida neque convallis a cras semper. Elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus. Semper auctor neque vitae tempus quam pellentesque nec nam aliquam. Gravida neque convallis a cras semper auctor neque vitae. Vulputate eu scelerisque felis imperdiet proin fermentum leo vel orci. Ornare massa eget egestas purus viverra accumsan in. Venenatis cras sed felis eget velit aliquet. Felis imperdiet proin fermentum leo vel orci porta. Commodo elit at imperdiet dui accumsan sit amet nulla. Etiam tempor orci eu lobortis elementum nibh tellus molestie nunc.
    </p>
    </div>
  </div>
  )
}


ReactDOM.render(
    <App />,
    document.getElementById('app')
);
.params {
  position: fixed;
  top: 0;
  left: 250px;
}

.sample {
  width: 100px;
  height: 100px;
}

.content {
  border: solid 1px red;
  z-index: 200;
  width: 200px;
  height: 300px;
  
  overflow: scroll;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>

1 Answers

The first thing I do is to scale the opacity from 0 to 1, then I can figure out how to adjust the range from 0.1 to 0.95.

The scrollHeight and clientHeight are fixed values while the scrollTop changes. I am seeing:

scrollHeight: 1155
clientHeight: 283
scrollTop: from 0 to 872

Take note of that 872 maximum for scrollTop. It is the difference between scrollHeight and clientHeight (1155 - 283 = 872).

So now we can calculate from 0 to 1 based on where scrollTop lies between 0 and scrollHeight - clientHeight.

const opacityValue = scrollTop / (scrollHeight - clientHeight);

Next we need to tweak this to go from 0.1 to 0.95. The spread is 0.85 so we can multiply by 0.85 and then add 0.1.

const opacityValue = 0.1 + (0.85 * scrollTop) / (scrollHeight - clientHeight);

Here's the modified demo:

const App = () => {
  const [opacity, setOpacity] = React.useState(0.1);
  const [params, setParams] = React.useState({})
  const calculateOpacityOnScroll = (evt) => {
    const { scrollHeight, scrollTop, clientHeight } = evt.target;
    const opacityValue = 0.1 + (0.85 * scrollTop) / (scrollHeight - clientHeight);
    setOpacity(opacityValue);
        setParams({scrollHeight, scrollTop, clientHeight})
  };
  
  const incorrectMessage = opacity !== 0.1 && params.scrollTop === 0
    ? <span style={{ color: 'red' }}> This is incorrect! At the top, it must be 0.1!</span>
    : null

  return (
  <div>
   
    <div className='params'>
           <div
      className='sample'
      style={{
        background: 'blue',
        opacity
      }}>Sample</div>
      <h4>Opacity: {opacity} {incorrectMessage}</h4>
      <h4>scrollTop: {params.scrollTop || 0}</h4>
      <h4>scrollHeight: {params.scrollHeight || '?'}</h4>
      <h4>clientHeight: {params.clientHeight || '?'}</h4>
    </div>
       
    <div className='content' onScroll={calculateOpacityOnScroll}>
      <h4 style={{ zIndex: 200 }}>Scroll here!</h4>
      <p style={{ zIndex: 200 }}>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </p>

    <p style={{ zIndex: 200 }}>Et tortor consequat id porta nibh venenatis. Consequat id porta nibh venenatis cras sed felis. Porttitor rhoncus dolor purus non. Eu turpis egestas pretium aenean pharetra magna. Eget velit aliquet sagittis id consectetur purus ut. In mollis nunc sed id semper risus in hendrerit gravida. Nascetur ridiculus mus mauris vitae ultricies leo integer malesuada nunc. Dictumst vestibulum rhoncus est pellentesque elit ullamcorper dignissim. Bibendum enim facilisis gravida neque convallis a cras semper. Elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus. Semper auctor neque vitae tempus quam pellentesque nec nam aliquam. Gravida neque convallis a cras semper auctor neque vitae. Vulputate eu scelerisque felis imperdiet proin fermentum leo vel orci. Ornare massa eget egestas purus viverra accumsan in. Venenatis cras sed felis eget velit aliquet. Felis imperdiet proin fermentum leo vel orci porta. Commodo elit at imperdiet dui accumsan sit amet nulla. Etiam tempor orci eu lobortis elementum nibh tellus molestie nunc.
    </p>
    </div>
  </div>
  )
}


ReactDOM.render(
    <App />,
    document.getElementById('app')
);
.params {
  position: fixed;
  top: 0;
  left: 250px;
}

.sample {
  width: 100px;
  height: 100px;
}

.content {
  border: solid 1px red;
  z-index: 200;
  width: 200px;
  height: 300px;
  
  overflow: scroll;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>

Related