8.2 Sticky and Parallax Icon

Watch the video course

Overview

What you'll learn:

  • Creating and customizing the sticky and parallax effects with useTransform
  • Using my use-parallax library.

Sticky and Parallax Effects

We'll now make our icon "sticky" — sticking to a certain position — when it enters a certain range.

Sticky Scroll Effect

See for yourself! Scroll Effects.

Think about how we would do this.

Usually, when we scroll up, everything on the page will move along with it, so how can we make something stay in position?

By applying a y offset that is equal and opposite to our scroll, our item will seem like it is staying in position.

To illustrate, if we move up the page by 300 pixels, we simply need to move our icon down by 300 pixels.

Back in our code, we'll create a new MotionValue called iconY.

function App() {
  const offset = useMotionValue(0)
  const iconOpacity = useTransform(offset, [0, -500], [1, 0])
  const iconBorderRadius = useTransform(offset, [0, -500], [20, 100])
const iconY = useTransform(offset, [0, -300], [0, 300])
return( ... ) }

Then in our icon <Frame>, we'll set y={iconY} and comment out opacity={iconOpacity} for now.

<Frame
  y={iconY}
  opacity={iconOpacity}
  backgroundColor="white"
  size={200}
  top={400}
  borderRadius={iconBorderRadius}
  center="x"
  image="/starship.png"
/>
sticky icon

We can play with the useTransform numbers to get different effects.

[0, 100]

function App() {
  ...
const iconY = useTransform(offset, [0, -300], [0, 100])
return( ... ) }

Since [0, 300] was the "balance" point so that our icon could stick, a smaller number will cause a net, negative offset to occur. As a result, [0, 100] creates a net, negative offset in the direction of scrolling.

100 scrolling

Our icon moves slower than the page.

[0, 500]

function App() {
  ...
const iconY = useTransform(offset, [0, -300], [0, 500])
return( ... ) }

We have a net, positive offset. Therefore, our icon will move in the opposite direction we scroll since by scrolling to a negative value, we'll get a positive icon offset.

500 scrolling

[0, -100]

function App() {
  ...
const iconY = useTransform(offset, [0, -300], [0, -100])
return( ... ) }

With -100, we have a net negative offset, but this time, the net offset is greater than just our scrolling offset. To illustrate, not only are we scrolling with the icon, but the icon is also giving an additional negative offset. Combined, our icon moves faster than our scroll.

-100 scrolling

By changing the numbers, we can achieve many different parallax effects.

[0, -150, -300], [0, 50, 200]

Furthermore, we can more than two numbers in the array to create some more effects.

function App() {
  ...
const iconY = useTransform(offset, [0, -150, -300], [0, 50, 200])
return( ... ) }
three number scrolling

Initially, the icon is moving slower than our scroll then the icon sticks and finally moves with the page.

You can play around with the numbers to achieve the effect you are looking for.

However, sometimes we have to use some math to get our correct movement.

use-parallax Library

To make it easier, I wrote a small library that includes a few useful custom hooks.

Let's add my dependency called use-parallax.

use-parallax dependency

Let's import a hook.

import "./styles.css"

I wrote usePositiveOffset because it was a hassle constantly adjusting the useTransform values and whether they should be negative or positive!

function App() {
const offset = useMotionValue(0)
const pOffset = usePositiveOffset(offset) ... return( ... ) }

With usePositiveOffset we no longer need to worry about the negative values. We can instead use positive numbers in the range as long as our input was initialized with usePositiveOffset.

function App() {
  const offset = useMotionValue(0)
  const pOffset = usePositiveOffset(offset)
const iconOpacity = useTransform(offset, [0, -500], [1, 0])
const iconBorderRadius = useTransform(pOffset, [0, 500], [20, 100])
const iconY = useTransform(pOffset, [0, 150, 300], [0, 50, 200]) return( ... ) }

This works just as before:

three number scrolling

I created another hook called useSticky.

import "./styles.css"

With useSticky we can replace useTransform and giving useSticky a list of ranges where we want our element to be sticky.

function App() {
  const offset = useMotionValue(0)
  const pOffset = usePositiveOffset(offset)
  const iconOpacity = useTransform(offset, [0, -500], [1, 0])
const iconBorderRadius = useTransform(pOffset, [0, 500], [20, 100])
const iconY = useSticky(pOffset, [0, 100], [200, 400], [600, 800]) return( ... ) }

Remember that we need to use pOffset inside of useSticky!

We now have there ranges in which our icon sticks.

Without these custom hooks, it would take some time to re-create these effects. However, I encourage you to try create some of theses effects from scratch as you will gain a deeper understanding of what is happening.

Conclusion

We've now covered how to create a sticky scroll effect and customize a scrolling parallax effect. You should now know the underlying basics behind many of those design effects on popular websites!

If you have trouble with the sticky or parallax effect, remember to use my use-parallax library! It has a lot of code to help you design!

In the next post, we'll cover some more parallax hooks from my library.