15. Scroll: Progress
This example uses Motion values and useTransform()
to change the width of the bar at the bottom.
Code component
Here, we have a scrollY
Motion value that we pass to the draggable div’s y
.
This way, the scrollY
Motion value will update whenever you drag the div, and we can change it with useTransform()
to another Motion value that changes the width
of the bottom div.
We’re using CSS’s calc()
to set the value to a maximum of the available width (100%
) minus two times 20px
for the margins left and right.
Code overrides
The useMotionValue()
hook only works inside functions, so when sharing a Motion value between overrides, we use motionValue()
instead.
Also, here, we’re using a prototyping Scroll component, so we pass the scrollY
Motion value to its contentOffsetY
property. (There’s also contentOffsetX
for when you’re scrolling horizontally.)
The override for the progress bar takes this scrollY
Motion value and changes it to a CSS calc()
value that changes this layer’s width
.
Version for a native scroll
You can make this work with a native scroll when you add an onScroll()
event that takes the scrollTop
value and saves it to the Motion value (using its set()
function) every time it changes.
And also, since a native scroll uses a positive value for the scroll distance, you have to make the scrollDistance
positive. (It was a negative value; I flipped it by adding a -
.)
A version that reads sizes
Please find this version in the file: CO_15B_Scroll_Progress.tsx
.
The above versions use a (positive or negative) scrollDistance
that’s calculated from predefined contentHeight
(total of all the scrollable content) and scrollSize
(height of the scrollable area) variables. The version below gets those numbers directly from the scroll layer and its content.
This is again a version for a non-native scroll (so no added onScroll()
). Here, we have a ref
, created using React’s useRef()
hook, passed to the scroll component.
We now have access to the HTML element through the ref
, enabling us to get its height
by calling getBoundingClientRect()
on the reference. Inside an useEffect()
because we want to do this when everything is drawn on the screen.
useRef()
, Manipulating the DOM with Refs
getBoundingClientRect()
And we can save the returned value in the data store as scrollHeight
.
But we also need the height of the scrollable content. So there’s now an extra (and similar) override for this. It saves that value in the data store’s contentHeight
.
In the code override for the progress bar, we can now take scrollHeight
and contentHeight
and calculate the correct scrollDistance
.
(It’s again a negative value because we’re using a non-native scroll.)