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:
Basic knowledge of react, styled-components and javascript.
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
open that file directory
cd scroll-tutorial
Open with the text editor of your choice. I use VScode.
code .
Now let's install styled-components.
npm i styled-components
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.
Now make the file.
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;
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()
Now inside the <> also called fragments add the Provider like so.
return (
<Provider>
</Provider>
);
Add the value object to the opening Provider tag and props.children.
<Provider value={{
}}>
{props.children}
</Provider>
Don't forget to add props at the top of the react component
const ThemeProvider = (props) => {
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';
Make a piece of state to save the scroll position inside the react component at the top.
const [scrollPosition, setScrollPosition] = useState(0)
Add the scroll postion into the the value object.
<Provider value={{
scrollPosition,
}}>
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 => ()
Inside the parenthisis add the Consumer like so.
export const withTheme = C => props => (
<Consumer>
</Consumer>
)
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>
)
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;
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')
);
index.js
In the App.js add props and and console.log them.
function App(props) {
console.log(props)
With VS code press control + ~ , the button underneath the escape key and in the teminal that pops up. run npm start.
npm start
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'
At the bottom where the export is wrap the App withTheme.
export default withTheme(App);
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'
At the bottom make a styled Container.
const Container = styled.div``
Replace the react boiler plate with an empty Container.
return (
<Container>
</Container>
);
Follow the link and download the one I used for this tutorial.
Right-click on the image
Save it inside the src folder, change the name to cog.png.
Import the image to the App.js
import cog from './cog.png'
Add an img tag with the image inside the Container.
<Container>
<img src={cog} />
</Container>
_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} />
Give cog a position of fixed.
#cog {
position: fixed;
}
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;
}
`)
Now our CSS has access to stateful logic.
Make a transform: rotate() attribute on the #cog
#big-cog {
position: fixed;
transform: rotate(0deg);
}
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)
})
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)
})
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)
Check it out!!
To make the scroll a little slower we can divide the number like this.
transform: rotate(${props => props.scrollPosition / 20}deg)
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!!