How to create an image editor using cropper.js

89
2

Hello friends, today in this blog, we will learn how to create an image editor using cropper.js. In our previous blog, we saw what are the top 5 programming languages to learn in 2023. You can check my other javascript projects after reading this blog.

In today’s digital age, images are an essential part of our lives. Whether it’s for personal use, social media, or business, images play a crucial role in conveying information and emotions. With the rise of social media platforms like Instagram and Snapchat, image editing has become an even more popular pastime. However, the options for editing images can be limited, and often the software can be expensive or difficult to use.

This is where Cropper.js comes in. Cropper.js is a powerful JavaScript library that allows you to easily create your own custom image editor. With Cropper.js, you can add various features like cropping, resizing, filtering, and more, to your image editor. In this blog post, we will take you through the step-by-step process of creating an image editor using Cropper.js. We’ll cover everything from setting up the HTML and CSS files to adding functionality like image upload and download and customizing the interface to make it your own.

By the end of this blog post, you’ll have a functional image editor that you can use to edit images for personal use or even integrate into your website or web application. So let’s get started on this exciting journey of creating your very own image editor with Cropper.js!

You may like these:

Note:
You can check the live demo and download code files here.

Code of HTML, CSS, and JavaScript Files

Here’s the good news: you don’t have to write all the code of this project from scratch! I have created a GitHub repository that contains all the HTML, CSS, and JavaScript code needed to build the app. You can check it out and use it as a starting point for your own project.

HTML CODE

<!DOCTYPE html>  
 <html lang="en">  
 <head>  
   <!-- --------------------- Created By InCoder --------------------- -->  
   <meta charset="UTF-8">  
   <meta http-equiv="X-UA-Compatible" content="IE=edge">  
   <meta name="viewport" content="width=device-width, initial-scale=1.0">  
   <title>Image Editor - InCoderWeb</title>  
   <link rel="stylesheet" href="main.css">  
   <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">  
   <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.css">  
 </head>  
 <body>  
   <div class="header">  
     <div class="title">Image Editor</div>  
     <div class="options">  
       <input type="file" hidden>  
       <button class="inBtn openNewFile" title="Open Image File"><i class="fa-solid fa-file-image"></i></button>  
       <a href="https://github.com/InCoderWeb" target="_blank" title="Github Profile"><button class="inBtn"><i class="fa-brands fa-github"></i></button></a>  
       <button class="inBtn outputDownloadBtn hide"><i class="fa-solid fa-download"></i></button>  
     </div>  
   </div>  
   <div class="mainContainer">  
     <div class="dragOrDropContainer">  
       <div class="icon"><i class="fa-solid fa-cloud-arrow-up"></i></div>  
       <div class="text">Drag & Drop to Upload File</div>  
     </div>  
     <div class="editorOptions">  
       <div class="sideBar active">  
         <button class="sidebarToggleBtn"><i class="fa-solid fa-chevron-left"></i></button>  
         <div class="preview">  
           <p>Preview</p>  
           <div class="previewImage"></div>  
         </div>  
         <div class="options">  
           <div class="zoom">  
             <p>Zoom In / Zoom Out</p>  
             <div class="btnWrapper">  
               <button class="optionBtn zoomIn"><i class="fa-solid fa-magnifying-glass-plus"></i></button>  
               <button class="optionBtn zoomOut"><i class="fa-solid fa-magnifying-glass-minus"></i></button>  
             </div>  
           </div>  
           <div class="rotate">  
             <p>Rotate Image</p>  
             <div class="btnWrapper">  
               <button class="optionBtn rotateLeft"><i class="fa-solid fa-rotate-left"></i></button>  
               <button class="optionBtn rotateRight"><i class="fa-solid fa-rotate-right"></i></button>  
             </div>  
           </div>  
           <div class="flip">  
             <p>Flip Image</p>  
             <div class="btnWrapper">  
               <button class="optionBtn flipLeftRight"><i class="fa-solid fa-arrows-left-right"></i></button>  
               <button class="optionBtn flipUpDown"><i class="fa-solid fa-arrows-up-down"></i></button>  
             </div>  
           </div>  
           <div class="aspectRatio">  
             <p>Aspect Ratio</p>  
             <div class="btnWrapper">  
               <button class="optionBtnSqr">16:9</button>  
               <button class="optionBtnSqr">4:5</button>  
               <button class="optionBtnSqr">1:1</button>  
               <button class="optionBtnSqr">2:3</button>  
               <button class="optionBtnSqr">Free</button>  
             </div>  
           </div>  
           <div class="dragMode">  
             <p>Drag Mode</p>  
             <div class="btnWrapper">  
               <button class="optionBtn dragModeBtn"><i class="fa-solid fa-crop-simple"></i></button>  
               <button class="optionBtn dragModeBtn"><i class="fa-solid fa-arrows-up-down-left-right"></i></button>  
             </div>  
           </div>  
           <div class="controlCropper">  
             <p>Control Cropper</p>  
             <div class="btnWrapper">  
               <button class="optionBtn cropperClear"><i class="fa-solid fa-bars-staggered"></i></button>  
               <button class="optionBtn cropperCrop"><i class="fa-solid fa-crop-simple"></i></button>  
             </div>  
           </div>  
         </div>  
       </div>  
     </div>  
   </div>  
   <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.js"></script>  
   <script src="script.js"></script>  
 </body>  
 </html>  

CSS CODE

@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");  
 /* --------------------- Created By InCoder --------------------- */  
 * {  
  margin: 0;  
  padding: 0;  
  box-sizing: border-box;  
  font-family: "Poppins", sans-serif;  
 }  
 body {  
  height: 100vh;  
  background: linear-gradient(to top left, #000000e6, #404040) no-repeat;  
 }  
 /* width */  
 ::-webkit-scrollbar {  
  width: 5px;  
 }  
 /* Track */  
 ::-webkit-scrollbar-track {  
  background: transparent;  
 }  
 /* Handle */  
 ::-webkit-scrollbar-thumb {  
  border-radius: 5rem;  
  background: #d0d0ce45;  
 }  
 /* Handle on hover */  
 ::-webkit-scrollbar-thumb:hover {  
  background: #555;  
 }  
 .header {  
  top: 0;  
  left: 0;  
  width: 100vw;  
  height: 3rem;  
  display: flex;  
  position: fixed;  
  padding-left: 1.5rem;  
  padding-right: 1.5rem;  
  align-items: center;  
  background: #232323;  
  justify-content: space-between;  
 }  
 .header .title {  
  color: #ffffff;  
 }  
 .header .options .inBtn {  
  border: 0;  
  width: 3rem;  
  height: 3rem;  
  font-size: 1.2rem;  
  cursor: pointer;  
  color: #ffffff;  
  margin-right: 0.5rem;  
  background: transparent;  
 }  
 .header .options .inBtn:hover {  
  background-color: #5903d8;  
 }  
 .mainContainer {  
  height: 100%;  
  width: 100vw;  
  display: flex;  
  align-items: center;  
  justify-content: center;  
 }  
 .dragOrDropContainer {  
  width: 80%;  
  height: 80%;  
  display: flex;  
  flex-direction: column;  
  align-items: center;  
  justify-content: center;  
 }  
 .dragOrDropContainer.hide {  
  display: none;  
 }  
 .dragOrDropContainer .icon {  
  height: 6.5rem;  
  font-size: 5rem;  
  color: #ffffff;  
 }  
 .dragOrDropContainer .text {  
  color: #ffffff;  
  text-align: center;  
  font-size: clamp(1rem, 8vw, 2rem);  
 }  
 .dragOrDropContainer.drag .icon {  
  animation: upload 1s infinite linear alternate;  
 }  
 @keyframes upload {  
  0% {  
   transform: translateY(0rem);  
  }  
  100% {  
   transform: translateY(-1rem);  
  }  
 }  
 .editorOptions .sideBar {  
  top: 4rem;  
  width: 15rem;  
  height: 80vh;  
  right: -15rem;  
  position: fixed;  
  overflow-y: hidden;  
  background: #181818;  
  border-top-left-radius: 0.5rem;  
  border-bottom-left-radius: 0.5rem;  
  transition: right 0.5s cubic-bezier(0, 0.81, 0, 0.95);  
 }  
 .editorOptions .sideBar.active {  
  right: 0;  
 }  
 .editorOptions .sideBar.active .sidebarToggleBtn {  
  right: 15rem;  
 }  
 .editorOptions .sideBar:hover {  
  overflow-y: auto;  
 }  
 .editorOptions .sideBar .preview {  
  padding-top: 0.6rem;  
  padding-left: 1rem;  
  padding-bottom: 0.5rem;  
  border-bottom: 1px solid #ffffff2b;  
 }  
 .editorOptions .sideBar .preview p {  
  color: #ffffffc7;  
 }  
 .editorOptions .sideBar .previewImage {  
  width: 90%;  
  height: 8rem;  
  overflow: hidden;  
  border-radius: 0.2rem;  
  border: 2px dashed #ffffff4a;  
 }  
 .editorOptions .sideBar .options .zoom,  
 .editorOptions .sideBar .options .flip,  
 .editorOptions .sideBar .options .aspectRatio,  
 .editorOptions .sideBar .options .dragMode,  
 .editorOptions .sideBar .options .controlCropper,  
 .editorOptions .sideBar .options .rotate {  
  padding-bottom: 0.6rem;  
  border-bottom: 1px solid #ffffff14;  
 }  
 .editorOptions .sideBar .options .zoom p,  
 .editorOptions .sideBar .options .flip p,  
 .editorOptions .sideBar .options .dragMode p,  
 .editorOptions .sideBar .options .aspectRatio p,  
 .editorOptions .sideBar .options .controlCropper p,  
 .editorOptions .sideBar .options .rotate p {  
  font-size: 0.8rem;  
  padding-top: 0.6rem;  
  padding-left: 1rem;  
  color: #ffffffc7;  
 }  
 .optionBtn,  
 .optionBtnSqr {  
  border: 0;  
  width: 2.5rem;  
  height: 2.5rem;  
  cursor: pointer;  
  font-size: 1rem;  
  border-radius: 50%;  
  color: #ffffffc7;  
  margin-right: 1rem;  
  background: #ffffff0f;  
  transition: all 0.2s ease-in-out;  
 }  
 .optionBtnSqr {  
  width: 3rem;  
  margin-right: 0.5rem;  
  margin-bottom: 0.5rem;  
  height: 2rem !important;  
  font-size: 0.9rem !important;  
  border-radius: 0.5rem !important;  
 }  
 .optionBtnSqr:hover,  
 .optionBtnSqr.selected {  
  background: #ffffff1f;  
 }  
 .optionBtn:hover,  
 .optionBtn.selected {  
  background: #ffffff1f;  
 }  
 .btnWrapper {  
  width: 100%;  
  margin-top: 0.5rem;  
  padding-left: 1rem;  
 }  
 .sidebarToggleBtn {  
  right: 0;  
  top: 5rem;  
  border: 0;  
  width: 2rem;  
  height: 2.5rem;  
  cursor: pointer;  
  position: fixed;  
  color: #ffffff;  
  background: #0000004d;  
  border-top-left-radius: 0.5rem;  
  border-bottom-left-radius: 0.5rem;  
  transition: right 0.5s cubic-bezier(0, 0.81, 0, 0.95);  
 }  
 #imageWorkSpace {  
  max-width: 100%;  
  max-height: 100%;  
 }  
 .cropper-drag-box {  
  opacity: 1!important;  
  background-color: #00000080!important;  
 }  
 .outputDownloadBtn.hide {  
  display: none;  
 }  

JavaScript CODE

// --------------------- Created By InCoder ---------------------  
 const openNewFile = document.querySelector('.openNewFile')  
 dragOrDropContainer = document.querySelector('.dragOrDropContainer')  
 dragBoxText = document.querySelector('.dragOrDropContainer .text')  
 fileInput = document.querySelector('.header .options input[type=file]')  
 sidebarToggleBtn = document.querySelector('.sidebarToggleBtn')  
 sideBar = document.querySelector('.sideBar')  
 outputDownloadBtn = document.querySelector('.outputDownloadBtn')  
 optionBtnSqr = document.querySelectorAll('.optionBtnSqr')  
 dragModeBtn = document.querySelectorAll('.dragModeBtn')  
 let file  
 openNewFile.addEventListener("click", () => {  
   fileInput.click()  
 })  
 fileInput.addEventListener('change', () => {  
   file = fileInput.files[0];  
   uploadFile()  
 })  
 const uploadFile = () => {  
   let fileType = file.type;  
   dragOrDropContainer.style.cursor = 'progress'  
   dragBoxText.innerText = 'Uploading file, Please Wait...'  
   let validExt = ["image/jpeg", "image/jpg", "image/png"];  
   if (validExt.includes(fileType)) {  
     let p = new Promise((resolve, reject) => {  
       let fileReader = new FileReader()  
       fileReader.onload = () => {  
         let fileURL = fileReader.result  
         let imageTag = `<img src="${fileURL}" id="imageWorkSpace" alt="image">`  
         dragOrDropContainer.innerHTML = imageTag  
         dragOrDropContainer.style.cursor = 'auto'  
         outputDownloadBtn.classList.remove('hide')  
         resolve(true)  
       }  
       fileReader.readAsDataURL(file)  
     }).then(() => {  
       let options = {  
         dargMode: "move",  
         preview: ".previewImage",  
         viewMode: 2,  
         modal: false,  
         background: false,  
         ready: () => {  
           // Zoom Image  
           document.querySelector('.zoomIn').onclick = () => cropper.zoom(0.1)  
           document.querySelector('.zoomOut').onclick = () => cropper.zoom(-0.1)  
           // Rotate Image  
           document.querySelector('.rotateLeft').onclick = () => cropper.rotate(90)  
           document.querySelector('.rotateRight').onclick = () => cropper.rotate(-90)  
           // Flip Image  
           let flipX = -1  
           flipY = -1  
           document.querySelector('.flipLeftRight').onclick = () => {  
             cropper.scale(flipX, 1)  
             flipX = -flipX  
           }  
           document.querySelector('.flipUpDown').onclick = () => {  
             cropper.scale(1, flipY)  
             flipY = -flipY  
           }  
           // set Aspect Ratio  
           optionBtnSqr[0].onclick = () => cropper.setAspectRatio(1.7777777777777777)  
           optionBtnSqr[1].onclick = () => cropper.setAspectRatio(1.4444444444444444)  
           optionBtnSqr[2].onclick = () => cropper.setAspectRatio(1)  
           optionBtnSqr[3].onclick = () => cropper.setAspectRatio(0.6666666666666666)  
           optionBtnSqr[4].onclick = () => cropper.setAspectRatio(0)  
           // Cropper Control  
           document.querySelector('.cropperClear').onclick = () => cropper.clear()  
           document.querySelector('.cropperCrop').onclick = () => cropper.crop()  
           // Drag Mode  
           dragModeBtn[0].onclick = () => {  
             dragModeBtn[0].classList.remove('selected')  
             dragModeBtn[0].classList.toggle('selected')  
             cropper.setDragMode("crop")  
           }  
           dragModeBtn[1].onclick = () => {  
             dragModeBtn[0].classList.remove('selected')  
             dragModeBtn[1].classList.toggle('selected')  
             cropper.setDragMode("move")  
           }  
           // download Image  
           outputDownloadBtn.onclick = () => {  
             outputDownloadBtn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>'  
             setTimeout(() => {  
               cropper.getCroppedCanvas().toBlob((blob) => {  
                 let downloadURL = window.URL.createObjectURL(blob)  
                 let a = document.createElement('a')  
                 a.href = downloadURL  
                 a.download = `output-${Date.now()}.jpg`  
                 a.click()  
                 outputDownloadBtn.innerHTML = '<i class="fa-solid fa-download"></i>'   
               })  
             }, 2000)  
           }  
         }  
       }  
       let imageWorkSpace = document.querySelector('.dragOrDropContainer #imageWorkSpace')  
       let cropper = new Cropper(imageWorkSpace, options)  
     })  
   } else {  
     dragOrDropContainer.classList.remove("hide")  
     dragOrDropContainer.classList.remove("drag")  
     dragBoxText.innerText = "Drag & Drop to Upload File"  
     alert("This File is nat valid. Please choose another file and try again.")  
   }  
 }  
 sidebarToggleBtn.addEventListener("click", () => {  
   sideBar.classList.toggle('active')  
   if (sideBar.classList.contains('active')) {  
     sidebarToggleBtn.querySelector('i').classList.remove('fa-chevron-left')  
     sidebarToggleBtn.querySelector('i').classList.add('fa-chevron-right')  
   } else {  
     sidebarToggleBtn.querySelector('i').classList.remove('fa-chevron-right')  
     sidebarToggleBtn.querySelector('i').classList.add('fa-chevron-left')  
   }  
 })  
 dragOrDropContainer.addEventListener('dragover', (e) => {  
   e.preventDefault()  
   dragOrDropContainer.classList.add('drag')  
   dragBoxText.innerText = 'Release to Upload File'  
 })  
 dragOrDropContainer.addEventListener('dragleave', (e) => {  
   e.preventDefault()  
   dragOrDropContainer.classList.remove('drag')  
   dragBoxText.innerText = 'Drag & Drop to Upload File'  
 })  
 dragOrDropContainer.addEventListener('drop', (e) => {  
   e.preventDefault()  
   file = e.dataTransfer.files[0];  
   uploadFile()  
 })  
Ashutosh Tiwari
WRITTEN BY

Ashutosh Tiwari

Hey there, I'm Ashutosh Tiwari AKA InCoder and I'm a front-end and back-end developer. I create blogs, and I love helping people through my knowledgeable content. My content is all about coding and front-end design. When I'm not creating content, you can find me reading books and listening to podcasts. I set up this Buy Me a Coffee account to help support my work, as creating content takes time and effort. Every contribution helps me continue doing what I love and improving the quality of my content. Thank you so much for your support and for being a part of my journey!

2 thoughts on “How to create an image editor using cropper.js