DEV Community

Cover image for Scroll effects with react and styled-components
Tallan Groberg for ITNEXT

Posted on • Edited on

Scroll effects with react and styled-components

In this tutorial, we will make a react-app that will make a cog that rotates based on a user scrolling. It will scroll one way when a user scrolls down and the other when a user scrolls up.

If you think this is really cool, please share and give this post a heart.

You will learn how to add dynamic values to css attributes with styled-components based on user events. This is where you blur the line between engineering and art.

To ensure I'm not waste your time, here is the github

here is the site

Why is this useful?

Knowing how to add tasteful animations to your websites will impress your users.

It will be artistically fulfilling to command this much more imagination over the art of web development.

It could potentially be profitable and 'wow' a client or make your portfolio that much better.

Prerequisites:

  1. create-react-app

  2. Basic knowledge of react, styled-components and javascript.

  3. Basic command-line for installing npm packages I'll be using bash command-line

To get started make a new react-app called scroll-tutorial


create-react-app scroll-tutorial
Enter fullscreen mode Exit fullscreen mode

open that file directory

cd scroll-tutorial
Enter fullscreen mode Exit fullscreen mode

Open with the text editor of your choice. I use VScode.

code . 
Enter fullscreen mode Exit fullscreen mode

Now let's install styled-components.

npm i styled-components
Enter fullscreen mode Exit fullscreen mode

There may be another way to make this work but the way I found easiest was to create context and capture the scroll position with an event listener and wrap the styled div in context to add the number.

Inside the src folder, let's create a folder called provider and file called ThemeProvider where context will be.

Right-click the src folder.

Alt Text

Alt Text

Now make the file.

Alt Text

Alt Text

You can copy and paste this to make the skeleton for the file.

import React from 'react';

// context variable goes here


const ThemeProvider = () => {
  return (
    <>

    </>
  );
};

//higher order component goes here.


export default ThemeProvider;
Enter fullscreen mode Exit fullscreen mode

We can destructure the context variable since we aren't going to be using the useContext hook and a higher-order component instead.

// context variable goes here

const {Provider,  Consumer } = React.createContext()
Enter fullscreen mode Exit fullscreen mode

Now inside the <> also called fragments add the Provider like so.

return (
    <Provider>

    </Provider>
  );
Enter fullscreen mode Exit fullscreen mode

Add the value object to the opening Provider tag and props.children.

    <Provider value={{

    }}>
      {props.children}
    </Provider>
Enter fullscreen mode Exit fullscreen mode

Don't forget to add props at the top of the react component

const ThemeProvider = (props) => {
Enter fullscreen mode Exit fullscreen mode

We are going to be saving the scroll event to state, so at the top of the file, add useState to the react import.

import React, {useState} from 'react';
Enter fullscreen mode Exit fullscreen mode

Make a piece of state to save the scroll position inside the react component at the top.

const [scrollPosition, setScrollPosition] = useState(0)
Enter fullscreen mode Exit fullscreen mode

Add the scroll postion into the the value object.

 <Provider value={{
      scrollPosition,
    }}>
Enter fullscreen mode Exit fullscreen mode

The best practice is to make sure that context is working before we start making the functionality for it. this means that we will make the consumer, make sure we have a scroll position in the App.js then add the event listener for user scroll events.

Below and outside the component we are going to make the consumer for this provider.

This will be a HOC or higher-order component.

I highly recommend learning more about functional programming paradigms and closures but the details go beyond the scope of this tutorial. (pun intended.)

Let's make the skeleton of this function.

//higher order component goes here.
export const withTheme = C => props => ()
Enter fullscreen mode Exit fullscreen mode

Inside the parenthisis add the Consumer like so.

export const withTheme = C => props => (
  <Consumer>

  </Consumer>
)
Enter fullscreen mode Exit fullscreen mode

Inside the Consumer add the value and the consumer and make so that everything we pass to the withTheme function will be a child to this function.

The whole function should look like this.

//higher order component goes here.
export const withTheme = C => props => (
  <Consumer>
    {value => <C {...value} {...props} /> }
  </Consumer>
)
Enter fullscreen mode Exit fullscreen mode

The whole file should look like this.

import React, {useState} from 'react';

// context variable goes here

const {Provider,  Consumer } = React.createContext()

const ThemeProvider = (props) => {
  const [scrollPosition, setScrollPosition] = useState(0)

  return (
    <Provider value={{
      scrollPosition,
    }}>
      {props.children}
    </Provider>
  );
};

//higher order component goes here.
export const withTheme = C => props => (
  <Consumer>
    {value => <C {...value} {...props} /> }
  </Consumer>
)

export default ThemeProvider;
Enter fullscreen mode Exit fullscreen mode

ThemeProvider.js

Now go to the index.js and wrap your app with the provider.

import ThemeProvider from './provider/ThemeProvider'

ReactDOM.render(
  <React.StrictMode>
    <ThemeProvider>
      <App />
    </ThemeProvider>
  </React.StrictMode>,
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

index.js

In the App.js add props and and console.log them.

function App(props) {
  console.log(props)
Enter fullscreen mode Exit fullscreen mode

With VS code press control + ~ , the button underneath the escape key and in the teminal that pops up. run npm start.

npm start 
Enter fullscreen mode Exit fullscreen mode

command line

The console will return an empty object.

To give us the scrollPosition, import withTheme at the top of our App.js

import {withTheme} from './provider/ThemeProvider'
Enter fullscreen mode Exit fullscreen mode

At the bottom where the export is wrap the App withTheme.

export default withTheme(App);
Enter fullscreen mode Exit fullscreen mode

We should now see a key-value pair with a scrollPosition of 0.

Let's import styled components and make a styled container while we are here.

import styled from 'styled-components'
Enter fullscreen mode Exit fullscreen mode

At the bottom make a styled Container.

const Container = styled.div``
Enter fullscreen mode Exit fullscreen mode

Replace the react boiler plate with an empty Container.

return (
    <Container>

    </Container>
  );
Enter fullscreen mode Exit fullscreen mode

Follow the link and download the one I used for this tutorial.

Right-click on the image

Alt Text

Save it inside the src folder, change the name to cog.png.

Import the image to the App.js

import cog from './cog.png'
Enter fullscreen mode Exit fullscreen mode

Add an img tag with the image inside the Container.

    <Container>
      <img src={cog} />
    </Container>
Enter fullscreen mode Exit fullscreen mode

_Note: _ You may have to shop around for the cog you want to use. I looked up transparent cog icons on google and found something that I liked. the only requirement is that it is the background has to be transparent.

Before we go back to the ThemeProvider, lets set up some CSS for our Container.

First make the height 400vh.

Give the cog an id.

 <img id="cog" src={cog} />
Enter fullscreen mode Exit fullscreen mode

Give cog a position of fixed.

 #cog {
    position: fixed;
  }
Enter fullscreen mode Exit fullscreen mode

The same way that we wrapped the App withTheme do that to the styled.div

const Container = withTheme(styled.div`
height: 400vh;

  #cog {
    position: fixed;
  }
`)
Enter fullscreen mode Exit fullscreen mode

Now our CSS has access to stateful logic.

Make a transform: rotate() attribute on the #cog

#big-cog {
    position: fixed;
    transform: rotate(0deg);
  }
Enter fullscreen mode Exit fullscreen mode

To make it so that it will rotate on scrolling we have to go back to the provider and make the event listener.

Between the return and the useState, add this event listener.

document.addEventListener('scroll', () => {
    console.log(window.scrollY)
  })
Enter fullscreen mode Exit fullscreen mode

ThemeProvider.js

When you scroll with the console open you will see a lot of numbers indicating the vertical scroll position.

Now setScrollPosition to the window.scrollY

document.addEventListener('scroll', () => {
    console.log(window.scrollY)
    setScrollPosition(window.scrollY)
  })
Enter fullscreen mode Exit fullscreen mode

One last thing. we have to connect the number of degrees to be the number of scrollY to do that go back to the App.js and use this from props inside Containers props to be the number of degrees.

We can do this with template literal notation because thats exactly what css is written inside of with styled components.

transform: rotate(${props => props.scrollPosition}deg)
Enter fullscreen mode Exit fullscreen mode

Check it out!!

To make the scroll a little slower we can divide the number like this.

  transform: rotate(${props => props.scrollPosition / 20}deg)
Enter fullscreen mode Exit fullscreen mode

I would like to add that this is great as an art piece by this does cause a lot of rerendering and may not be suitable for bigger apps

this is the github

this is a site I made that adds more cogs to look like they are working together.

If you would like me to show you how to add more cogs and position them I would be happy to do so!!

Top comments (1)

Collapse
 
jackherizsmith profile image
Jack

This is cool!