Introduction
This article shows how the Nextjs framework can be implemented to produce a 3D illusion photo from an image.
Codesandbox
Check the sandbox demo on Codesandbox.
You can also get the project Github repo using Github.
Prerequisites
Entry-level javascript and React/Nextjs knowledge.
Setting Up the Sample Project
In your respective folder, create a new nextjs app using npx create-next-app 3dimage
in your terminal.
Head to your project root directory cd 3dimage
Nextjs has its own serverside rendering backend which we will use for our media files upload. Set up Cloudinary for our backend.
Create your Cloudinary account using this Link. Log in to a dashboard containing the environment variable keys necessary for the Cloudinary integration in our project.
Include Cloudinary in your project dependencies: npm install cloudinary
.
Create a new file named .env.local
and paste the following guide to fill your environment variables. You locate your variables from the Cloudinary dashboard.
1CLOUDINARY_CLOUD_NAME =23CLOUDINARY_API_KEY =45CLOUDINARY_API_SECRET =
Restart your project: npm run dev
.
In the pages/api
directory, create a new file named upload.js
.
Start by configuring the environment keys and libraries.
1var cloudinary = require("cloudinary").v2;23cloudinary.config({4 cloud_name: process.env.CLOUDINARY_NAME,5 api_key: process.env.CLOUDINARY_API_KEY,6 api_secret: process.env.CLOUDINARY_API_SECRET,7});
Create a handler function to execute the POST request. The function will receive media file data and post it to the Cloudinary website. It then captures the media file's Cloudinary link and sends it back as a response.
1export default async function handler(req, res) {2 if (req.method === "POST") {3 let url = ""4 try {5 let fileStr = req.body.data;6 const uploadedResponse = await cloudinary.uploader.upload_large(7 fileStr,8 {9 resource_type: "video",10 chunk_size: 6000000,11 }12 );13 url = uploadedResponse.url14 } catch (error) {15 res.status(500).json({ error: "Something wrong" });16 }1718 res.status(200).json({data: url});19 }20}
The code above concludes our backend.
We'll use styled-components to create a card that can rotate 180 degrees. Include it in the dependencies:
npm install styled-components
We use the following code to create our card component. We first show the front and hide the back card. The back will listen to the flip
state hook to rotate 180 degrees to reveal the back.
1"pages/index"23import styled from "styled-components";4import { useState } from "react";56const Container = styled("div")(() => ({7 display: "flex",8 flexDirection: "row",9 justifyContent: "center"10}));1112const Card = styled.div`13 position: relative;14 flex-basis: 100%;15 max-width: 220px;16`;1718const CardTemplate = styled("div")(() => ({19 width: "100%",20 backfaceVisibility: "hidden",21 height: "400px",22 borderRadius: "6px",23 transformStyle: "preserve-3d",24 transition: "transform 1s cubic-bezier(0.8, 0.3, 0.3, 1)"25}));2627const CardFront = styled(CardTemplate)(({ flip }) => ({28 backgroundImage: "url('images/cardimage.jpg')",29 backgroundSize: "cover",30 backgroundPosition: "center",31 transform: flip ? "rotateY(-180deg)" : "rotateY(0deg)"32}));3334const CardBack = styled(CardTemplate)(({ flip }) => ({35 position: "absolute",36 top: 0,37 left: 0,38 background: "linear-gradient(0deg, #00adb5 20%,#596a72 100%)",39 transform: flip ? "rotateY(0deg)" : "rotateY(180deg)"40}));4142const CardContent = styled("div")(() => ({43 top: "50%",44 position: "absolute",45 left: 0,46 width: "100%",47 backfaceVisibility: "hidden",48 transform: "translateZ(70px) scale(0.90)"49}));5051const BGFade = styled("div")(() => ({52 position: "absolute",53 right: 0,54 bottom: 0,55 left: 0,56 height: "200px",57 background: "linear-gradient(to bottom, rgba(0,0,0,0) 20%,rgba(0,0,0,.8) 90%)"58}));5960export default function Home() {61 const [flip, setFlip] = useState(false);6263 return (64 <div className="App">6566 </div>67 )68}
Finally, paste the following in your return statement:
1return (2 <div className="App">3 <h1>Nextjs 3d Photo Effect</h1>4 <Container>5 <Card6 onMouseEnter={() => setFlip(true)}7 onMouseLeave={() => setFlip(false)}8 >9 <CardFront flip={flip}>10 <CardContent>11 <h1>Front</h1>12 </CardContent>13 <BGFade />14 </CardFront>15 <CardBack flip={flip}>16 <CardContent>17 <h1>Back</h1>18 </CardContent>19 </CardBack>20 </Card>21 </Container>22 </div>23 )
That's it! Your UI should look like the below:
Use the following code to command your upload button. The code will be uploaded to the backend for Cloudinary upload.
1const uploadHandler = async () => {2 console.log(cardRef.current)3 await html2canvas(cardRef.current).then(function (canvas) {4 try {5 fetch('/api/upload', {6 method: 'POST',7 body: JSON.stringify({ data: canvas.toDataURL() }),8 headers: { 'Content-Type': 'application/json' },9 })10 .then((response) => response.json())11 .then((data) => {12 console.log(data.data);13 setSampleSelected(true)14 });15 } catch (error) {16 console.error(error);17 }18 })19 }
That accomplishes the project. Enjoy selecting pictures for your card. An example is in the front card.