Create a split effect using Next.js and splitJS

Eugene Musebe

Introduction

In this article, we use Splitjs to create an image split effect in nextjs.

Codesandbox

Find the project demo on Codesandbox.

You can also find the Github repo using Github.

Prerequisites

Entry-level knowledge in javascript and React/Nextjs.

Setting Up the Sample Project

Use npx create-next-app split to create a new Next.js project and open the project directory using cd split Our project will involve importing pictures and creating a split-screen effect using the split.js library. A user can then caption the photo at any point and save it online.

Saving the project online will require Cloudinary integration. Let's begin by coding the integration in the nextjs backend.

Use this link to access the cloudinary website login page. Create a new account or login if you have one and access your dashboard page. Here you will find the necessary environment variables for your project. To use them you will be required to install cloudinary as a dependency in your project. Head to your project terminal and install it using npm install cloudinary. After installation head to your project root directory and create a file named .env.local and paste the following codes below. Fill the blanks with the respective environment variable from your dashboard.

1".env.local"
2
3CLOUDINARY_CLOUD_NAME =
4
5CLOUDINARY_API_KEY =
6
7CLOUDINARY_API_SECRET=

Restart your project npm run dev

Create a new file in the pages/api directory named upload.js.

We start by configuring cloudinary environment keys and libraries. Paste the following code.

1"pages/api/upload.js"
2
3var cloudinary = require("cloudinary").v2;
4
5cloudinary.config({
6 cloud_name: process.env.CLOUDINARY_NAME,
7 api_key: process.env.CLOUDINARY_API_KEY,
8 api_secret: process.env.CLOUDINARY_API_SECRET,
9});

Create a handler function to execute POST requests.

1"pages/api/upload.js"
2
3export default async function handler(req, res) {
4 if (req.method === "POST") {
5 let url = ""
6 try {
7 let fileStr = req.body.data;
8 const uploadedResponse = await cloudinary.uploader.upload_large(
9 fileStr,
10 {
11 resource_type: "video",
12 chunk_size: 6000000,
13 }
14 );
15 url = uploadedResponse.url
16 } catch (error) {
17 res.status(500).json({ error: "Something wrong" });
18 }
19
20 res.status(200).json({data: url});
21 }
22}

The function above will upload media files from post request body and then upload them to cloudinary. It will also send back a response containing the file's cloudinary link.

Our backend is complete. We can now begin coding our effect.

In the pages/index directory, start by importing the following

1"pages/index"
2
3import { forwardRef, useState, createRef, useEffect, useRef } from 'react';
4import Split from 'split.js'

Notice the Split import. Include it in your dependencies using npm install --save split.js to avoid errors.

Inside the root, component declares the following variables.

1"pages/index"
2
3 const captionRef = createRef();

The variables above will be attached to our DOM elements via the ref attribute so they can be referenced throughout our component.

Create a function defaultPictureHandler as follows

1"pages/index"
2
3const defaultPictureHandler = async (e) => {
4 const file = e.target.files?.item(0);
5 console.log(file)
6 await readFile(file).then((encoded_file) => {
7 document.getElementById("default").style.backgroundImage = `url(${encoded_file})`;
8 document.getElementById("picture").style.backgroundImage = `url(${encoded_file})`;
9 })
10}
11
12
13 function readFile(file) {
14 // console.log("readFile()=>", file);
15 return new Promise(function (resolve, reject) {
16 let fr = new FileReader();
17
18 fr.onload = function () {
19 resolve(fr.result);
20 };
21
22 fr.onerror = function () {
23 reject(fr);
24 };
25
26 fr.readAsDataURL(file);
27 });
28 }

We shall have an input element to upload the image file required as a background. The split effect shall ensure two sections of the same or different colored versions that can be split horizontally using a gutter.

The first upload will apply to both sections thus the function name defaultPictureHandler. The function will upload an image file and convert it to base64 using a file reader then replace the background of both sections with the uploaded image.

The second function will be pictureHandler which will simply replace the second section of the gutter with the uploaded image file.

1"pages/index"
2
3 const pictureHandler = async (e) => {
4 const file = e.target.files?.item(0);
5 await readFile(file).then((encoded_file) => {
6 document.getElementById("picture").style.backgroundImage = `url(${encoded_file})`;
7 })
8 }

At this point, head to your root function return method and replace it with the following :

1"pages/index"
2
3return(
4 <div className="container">
5 <div className="flex">
6 <div className="default">A</div>
7 <div className="picture">B</div>
8 </div>
9 </div>
10)

In the class names default and picture, we shall include the respective images uploaded by the functions created earlier. Meanwhile, you can tweak your css properties to look like below to view your sections.

1"styles/global.css"
2
3.container {
4 width: 100%;
5 height: 100%;
6 text-align: center;
7 justify-content: center;
8}
9
10
11.flex {
12 display: flex;
13 flex-direction: row;
14 height: 80vh;
15 width: 120vh;
16 margin-top: 5%;
17 margin-left: 25%;
18 /* border: 1px solid */
19}
20#default {
21 filter: grayscale(60%);
22 background-color: lightseagreen;
23
24}
25
26#picture {
27 background-color: lightseagreen;
28}

The above should result to the view below:

Introducing our split library will create a horizontal gutter line between the two sections which will be used to split the image. The Split method will be included inside a useEffect hook.

1"pages/index.js"
2
3 useEffect(() => {
4 var split = Split(["#default", "#picture"], {
5 gutterSize: 5,
6 sizes: [25, 50]
7 })
8 }, [])

Once the code above runs you should see a display like the below:

Split.js lets you handle the gutter manually because they do not wish to interfere with your layout. After they figure out the size and include an invisible gutter, the rest is up to you.

1"styles/global.css"
2
3.gutter.gutter-horizontal {
4 background-color: #143040;
5 cursor: e-resize;
6}

The gutter class above will configure the invisible gutter below and to give it a color and configure the cursor shape

There are two more properties in our split method namely gutterSize and sizes. These are used to control the line size and the sizes of each partition respectively. To learn more about these properties and others you can implement, see the documentation.

At this point, you can dow edit the sections by applying any images you see fit. In our case, we will include a background for our sections to showcase a demo as well as turn the first section into a greyscale view. Our two-colored versions will therefore be greyscale and an originally colored photo.

More about configuring the css properties can be found in the Github repo.

The final part of the project is to ensure that we can upload a caption of our split photo to cloudinary. We had already figured out our backend integration. Start by downloading the dependencies html2canvas and use-react-screenshot using npm install html2canvas use-react-screenshot Inport useScreenshot from use-react-screenshot: import { useScreenshot } from 'use-react-screenshot';

Introduce the following hook inside the root component: const [caption, takeScreenshot] = useScreenshot();

Then create the uploadHandler function as shown below:

1"pages/index.js"
2
3
4 const uploadHandler = async (e) => {
5 await takeScreenshot(captionRef.current).then((screenshot) => {
6 try {
7 fetch('/api/upload', {
8 method: 'POST',
9 body: JSON.stringify({ data: screenshot }),
10 headers: { 'Content-Type': 'application/json' },
11 })
12 .then((response) => response.json())
13 .then((data) => {
14 console.log(data.data)
15 })
16 } catch (error) {
17 console.error(error);
18 }
19 })
20 }

The function above will caption the split image and upload it online. The image will be saved in your cloudinary account media library. You can also view it in your browser`s console.log.

That's it! We have created our own image split effect. Ensure to go through the project to enjoy the experience. Happy coding!

Eugene Musebe

Software Developer

I’m a full-stack software developer, content creator, and tech community builder based in Nairobi, Kenya. I am addicted to learning new technologies and loves working with like-minded people.