DEV Community

Cover image for Awwward : Mouse Wheel Event + CSS Perspective, REACT.JS
The coding Freak
The coding Freak

Posted on

2 1

Awwward : Mouse Wheel Event + CSS Perspective, REACT.JS

Awwward : Mouse Wheel Event + CSS Perspective, REACT.JS

I love to spend time studying cool websites on awwward.com, there are always impressive and inspiring effects.

In this tutorial, we are building an interactive vinyl box using React.

Original website here

The plan

  1. Install React, SASS, …

  2. Create your folder structure

  3. Create the Home page and your components

  4. Create data and setup vinyls with CSS

  5. Create your wheel event

  6. Handle click on current vinyl

1. Install React, SASS, …

To use React, you first have to install it using NPM:

npx create-react-app nameOfYourProject
Enter fullscreen mode Exit fullscreen mode

Install SASS

npm install node-sass@4.14.1
Enter fullscreen mode Exit fullscreen mode

Launch your app

cd nameOfYourProject
npm start
Enter fullscreen mode Exit fullscreen mode

2. Create your folder structure

Add the following folders in /src :

  • /components (It will contain our Cursor component)

  • /pages (It will contain our Home page)

  • /services (It will contain utils functions)

  • /styles (It will contain styles of our app)

  • /*assets *(It will contain your images)

3. Create the Home page and your components

Now we need to create a Home page so add Home.jsx **in the pages folder. Don’t forget to also create a home folder in /styles** and add to it Home.scss

import React from 'react';
import "../styles/home/Home.scss";
const Home = (props) => {
return ( <>
<div className="home">
</div>
</> );
}
export default Home;
view raw Home.jsx hosted with ❤ by GitHub
.home{
width: 100vw;
height: 100vh;
background: black;
position: relative;
}
view raw Home.scss hosted with ❤ by GitHub



In index.css **add few lines to your body tag.

body{
width: 100vw;
height: 100vh;
overflow: hidden;
}
view raw index.css hosted with ❤ by GitHub



Then add **Home **in your **app.js

import React from 'react';
import Home from './pages/Home'
function App() {
return (
<div className="App">
<Home />
</div>
);
}
export default App;
view raw app.js hosted with ❤ by GitHub



Easy, isn’t it! Create a new folder /vinyleBox *in */components **and add to it **VinyleBox.jsx. VinyleBox.jsx **contain one parameter which is data. Add your **VinyleBox **component in **Home.jsx. **Do the same thing for the CSS that we did previously.

import React from 'react';
import '../../styles/vinylesBox/VinylesBox.scss';
const VinylesBox = ({data}) => {
return ( <>
<div className="vinyles-box">
</div>
</> );
}
export default VinylesBox;
view raw VinylesBox.jsx hosted with ❤ by GitHub
.vinyles-box{
position: absolute;
left: 50%;
bottom: -100px;
width: 80vw;
height: 500px;
transform: translate(-50%);
transition: 5s;
}
view raw VinylesBox.scss hosted with ❤ by GitHub
import React from 'react';
import VinylesBox from '../components/vinylesBox/VinylesBox';
import "../styles/home/Home.scss";
const Home = (props) => {
return ( <>
<div className="home">
<VinylesBox data="" />
</div>
</> );
}
export default Home;
view raw Home.jsx hosted with ❤ by GitHub



Create a new folder /vinyles in **/components **and add to it **Vinyles.jsx **and save images bellow like **cover.jpg to your folder /assets. Vinyles.jsx **contains two parameters id and styles! Then add your **Vinyles **component in **VinylesBox.jsx.


import React from 'react';
import '../../styles/vinyles/Vinyles.scss';
import cover from '../../assets/cover.jpg';
const Vinyles = ({id, styles}) => {
return ( <>
<div style={styles} className="vinyles">
<span>{id}</span>
<img src={cover} alt="cover-img" />
</div>
</> );
}
export default Vinyles;
view raw Vinyles.jsx hosted with ❤ by GitHub
.vinyles{
position: absolute;
background: white;
transition: cubic-bezier(.4,.33,.47,.95) 0.500s;
left: 50%;
&:nth-child(18){
cursor: pointer;
}
span{
position: absolute;
top: 0.2rem;
left: 0.5rem;
z-index: 99;
font-size: 0.4rem;
font-weight: 600;
color: rgb(12, 12, 12);
text-transform: uppercase;
}
img{
width: 100%;
height: 100%;
mix-blend-mode: unset;
opacity: 0.8;
}
}
view raw Vinyles.scss hosted with ❤ by GitHub
import React from 'react';
import '../../styles/vinylesBox/VinylesBox.scss';
import Vinyles from '../vinyles/Vinyles';
const VinylesBox = ({data}) => {
return ( <>
<div className="vinyles-box" >
<Vinyles />
</div>
</> );
}
export default VinylesBox;
view raw VinylesBox.jsx hosted with ❤ by GitHub

You should have this result :

4. Create data and setup vinyls with CSS

First, we need two functions to generate random ID and background color. So add VinylesServices.js to /services

export const randomId = () => {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
export const randomColor = () => {
return (0x1000000+Math.random()*0xffffff).toString(16).substr(1,6);
}

You’ll initialize a new state data and create a new function initData. Each vinyl contains an id, parameters position, and style.

To place vinyls like on the image with CSS you need to use the property transform and three transform-function. The first one is perspective which will be similar for each vinyls, it’s to sets the distance between the user and the z=0 plane. In other words, have a logic scaling if your item is far or near to you. The second is translateZ to move an element along the z-axis and the last one is translateY to move an element vertically.

So let’s do this in Home.jsx by including the two previous new services functions. And pass data to your VinylesBox **component.

import React, { useEffect, useState } from 'react';
import VinylesBox from '../components/vinylesBox/VinylesBox';
import { randomColor, randomId } from '../services/VinylesServices';
import "../styles/home/Home.scss";
const Home = (props) => {
const [data, setData] = useState([]);
// size is width and height of a square and the perspective value
const size = 160;
// gap is the spacing between two vinyles
const gap = 6;
const initData = () => {
const arrayTmp = [];
for (let i = 0; i < 20; i++) {
arrayTmp.push({
id: randomId(),
tZ: gap*i,
tY: gap*i,
perpective: size,
style :{
width: size+"px",
height: size+"px",
backgroundColor: '#'+randomColor(),
transform: "perspective("+size+"px) translateZ("+gap*i+"px) translateY("+gap*i+"px) rotateX(-1.5deg)",
zIndex: i+1,
marginLeft: -(size/2)+"px"
},
});
}
setData(arrayTmp);
}
useEffect(() => {
initData();
}, [])
return ( <>
<div className="home">
<VinylesBox data={data} />
</div>
</> );
}
export default Home;
view raw Home.jsx hosted with ❤ by GitHub
import React, { useEffect, useState } from 'react';
import VinylesBox from '../components/vinylesBox/VinylesBox';
import { randomColor, randomId } from '../services/VinylesServices';
import "../styles/home/Home.scss";
const Home = (props) => {
const [data, setData] = useState([]);
// size is width and height of a square and the perspective value
const size = 160;
// gap is the spacing between two vinyles
const gap = 6;
const initData = () => {
const arrayTmp = [];
for (let i = 0; i < 20; i++) {
arrayTmp.push({
id: randomId(),
tZ: gap*i,
tY: gap*i,
perpective: size,
style :{
width: size+"px",
height: size+"px",
backgroundColor: '#'+randomColor(),
transform: "perspective("+size+"px) translateZ("+gap*i+"px) translateY("+gap*i+"px) rotateX(-1.5deg)",
zIndex: i+1,
marginLeft: -(size/2)+"px"
},
});
}
setData(arrayTmp);
}
useEffect(() => {
initData();
}, [])
return ( <>
<div className="home">
<VinylesBox data={data} />
</div>
</> );
}
export default Home;
view raw Home.jsx hosted with ❤ by GitHub

Add a map function in **VinylesBox.js.

import React from 'react';
import '../../styles/vinylesBox/VinylesBox.scss';
import Vinyles from '../vinyles/Vinyles';
const VinylesBox = ({data}) => {
return ( <>
<div className="vinyles-box">
{
data.map((item, index) =>
<Vinyles key={item.id} id={item.id} styles={item.style} />
)
}
</div>
</> );
}
export default VinylesBox;
view raw VinylesBox.jsx hosted with ❤ by GitHub

Now you should have this result :

Read the rest of the article on medium

Top comments (0)