This code snippet helps you to create a canvas image editor. It creates an image editing tool with various filters and transformation options. It defines several variables for brightness, saturation, inversion, grayscale, rotate, flipHorizontal, and flipVertical. It uses event listeners to respond to user actions such as selecting filters, adjusting sliders, and clicking buttons. The applyFilter function applies the selected filters to the image. The updateFilter function updates the filter values when the slider is changed. The resetFilter function resets all filters and transformations to their default values.
The saveImage function creates a canvas and saves the edited image with the selected filters and transformations applied.
How to Create Canvas Image Editor JavaScript
Create the HTML structure for the image editor as follows:
<!DOCTYPE html>
<!-- Coding By Danish Laeeq - patreon.com/danishlaeeq -->
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Image Editor in JavaScript</title>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" />
</head>
<body>
<div style="display:block">
<div class="container disable">
<h2>Easy Image Editor</h2>
<div class="wrapper">
<div class="editor-panel">
<div class="filter">
<label class="title">Filters</label>
<div class="options">
<button id="brightness" class="active">Brightness</button>
<button id="saturation">Saturation</button>
<button id="inversion">Inversion</button>
<button id="grayscale">Grayscale</button>
</div>
<div class="slider">
<div class="filter-info">
<p class="name">Brighteness</p>
<p class="value">100%</p>
</div>
<input type="range" value="100" min="0" max="200">
</div>
</div>
<div class="rotate">
<label class="title">Rotate & Flip</label>
<div class="options">
<button id="left"><i class="fa-solid fa-rotate-left"></i></button>
<button id="right"><i class="fa-solid fa-rotate-right"></i></button>
<button id="horizontal"><i class='bx bx-reflect-vertical'></i></button>
<button id="vertical"><i class='bx bx-reflect-horizontal'></i></button>
</div>
</div>
</div>
<div class="preview-img">
<img src="https://cdn.shopify.com/s/files/1/0533/2089/files/placeholder-images-image_large.png?format=jpg&quality=90&v=1530129081" alt="preview-img">
</div>
</div>
<div class="controls">
<button class="reset-filter">Reset Filters</button>
<div class="row">
<input type="file" class="file-input" accept="image/*" hidden>
<button class="choose-img">Choose Image</button>
<button class="save-img">Save Image</button>
</div>
</div>
</div>
<!-- Support Section -->
<div class="support-box">
<a class="b-btn" target="blank" href="https://www.buymeacoffee.com/danishlaeeq">
<i class="fa fa-coffee"></i> Buy Me a Coffee
</a>
-
<a class="b-btn paypal" target="blank" href="https://technodigitz.com/donate">
<i class="fab fa-paypal"></i> Donate
</a>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Now, style the image editor using the following CSS styles:
/* Import Google font - Poppins */
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Poppins", sans-serif;
}
body {
display: flex;
padding: 10px;
min-height: 100vh;
align-items: center;
justify-content: center;
background: #e3f2fd;
}
.container {
width: 850px;
padding: 30px 35px 35px;
background: #fff;
border-radius: 10px;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}
.container.disable .editor-panel,
.container.disable .controls .reset-filter,
.container.disable .controls .save-img {
opacity: 0.5;
pointer-events: none;
}
.container h2 {
margin-top: -8px;
font-size: 22px;
font-weight: 500;
}
.container .wrapper {
display: flex;
margin: 20px 0;
min-height: 335px;
}
.wrapper .editor-panel {
padding: 15px 20px;
width: 280px;
border-radius: 5px;
border: 1px solid #ccc;
}
.editor-panel .title {
display: block;
font-size: 16px;
margin-bottom: 12px;
}
.editor-panel .options,
.controls {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.editor-panel button {
outline: none;
height: 40px;
font-size: 14px;
color: #6c757d;
background: #fff;
border-radius: 3px;
margin-bottom: 8px;
border: 1px solid #aaa;
}
.editor-panel .filter button {
width: calc(100% / 2 - 4px);
}
.editor-panel button:hover {
background: #f5f5f5;
}
.filter button.active {
color: #fff;
border-color: #5372f0;
background: #5372f0;
}
.filter .slider {
margin-top: 12px;
}
.filter .slider .filter-info {
display: flex;
color: #464646;
font-size: 14px;
justify-content: space-between;
}
.filter .slider input {
width: 100%;
height: 5px;
accent-color: #5372f0;
}
.editor-panel .rotate {
margin-top: 17px;
}
.editor-panel .rotate button {
display: flex;
align-items: center;
justify-content: center;
width: calc(100% / 4 - 3px);
}
.rotate .options button:nth-child(3),
.rotate .options button:nth-child(4) {
font-size: 18px;
}
.rotate .options button:active {
color: #fff;
background: #5372f0;
border-color: #5372f0;
}
.wrapper .preview-img {
flex-grow: 1;
display: flex;
overflow: hidden;
margin-left: 20px;
border-radius: 5px;
align-items: center;
justify-content: center;
}
.preview-img img {
max-width: 490px;
max-height: 335px;
width: 100%;
height: 100%;
object-fit: contain;
}
.controls button {
padding: 11px 20px;
font-size: 14px;
border-radius: 3px;
outline: none;
color: #fff;
cursor: pointer;
background: none;
transition: all 0.3s ease;
text-transform: uppercase;
}
.controls .reset-filter {
color: #6c757d;
border: 1px solid #6c757d;
}
.controls .reset-filter:hover {
color: #fff;
background: #6c757d;
}
.controls .choose-img {
background: #6c757d;
border: 1px solid #6c757d;
}
.controls .save-img {
margin-left: 5px;
background: #5372f0;
border: 1px solid #5372f0;
}
@media screen and (max-width: 760px) {
.container {
padding: 25px;
}
.container .wrapper {
flex-wrap: wrap-reverse;
}
.wrapper .editor-panel {
width: 100%;
}
.wrapper .preview-img {
width: 100%;
margin: 0 0 15px;
}
}
@media screen and (max-width: 500px) {
.controls button {
width: 100%;
margin-bottom: 10px;
}
.controls .row {
width: 100%;
}
.controls .row .save-img {
margin-left: 0px;
}
}
/* Support */
.support-box {
position: relative;
bottom: 0;
text-align: center;
display: block;
}
.b-btn {
color: black;
text-decoration: none;
font-weight: bold;
}
.b-btn.paypal i {
color: blue;
}
.b-btn:hover {
text-decoration: none;
font-weight: bold;
}
.b-btn i {
font-size: 20px;
color: orangered;
margin-top: 2rem;
}
Finally, add the following JavaScript function for its functionality:
const fileInput = document.querySelector(".file-input"),
filterOptions = document.querySelectorAll(".filter button"),
filterName = document.querySelector(".filter-info .name"),
filterValue = document.querySelector(".filter-info .value"),
filterSlider = document.querySelector(".slider input"),
rotateOptions = document.querySelectorAll(".rotate button"),
previewImg = document.querySelector(".preview-img img"),
resetFilterBtn = document.querySelector(".reset-filter"),
chooseImgBtn = document.querySelector(".choose-img"),
saveImgBtn = document.querySelector(".save-img");
let brightness = "100",
saturation = "100",
inversion = "0",
grayscale = "0";
let rotate = 0,
flipHorizontal = 1,
flipVertical = 1;
const loadImage = () => {
let file = fileInput.files[0];
if (!file) return;
previewImg.src = URL.createObjectURL(file);
previewImg.addEventListener("load", () => {
resetFilterBtn.click();
document.querySelector(".container").classList.remove("disable");
});
};
const applyFilter = () => {
previewImg.style.transform = `rotate(${rotate}deg) scale(${flipHorizontal}, ${flipVertical})`;
previewImg.style.filter = `brightness(${brightness}%) saturate(${saturation}%) invert(${inversion}%) grayscale(${grayscale}%)`;
};
filterOptions.forEach((option) => {
option.addEventListener("click", () => {
document.querySelector(".active").classList.remove("active");
option.classList.add("active");
filterName.innerText = option.innerText;
if (option.id === "brightness") {
filterSlider.max = "200";
filterSlider.value = brightness;
filterValue.innerText = `${brightness}%`;
} else if (option.id === "saturation") {
filterSlider.max = "200";
filterSlider.value = saturation;
filterValue.innerText = `${saturation}%`;
} else if (option.id === "inversion") {
filterSlider.max = "100";
filterSlider.value = inversion;
filterValue.innerText = `${inversion}%`;
} else {
filterSlider.max = "100";
filterSlider.value = grayscale;
filterValue.innerText = `${grayscale}%`;
}
});
});
const updateFilter = () => {
filterValue.innerText = `${filterSlider.value}%`;
const selectedFilter = document.querySelector(".filter .active");
if (selectedFilter.id === "brightness") {
brightness = filterSlider.value;
} else if (selectedFilter.id === "saturation") {
saturation = filterSlider.value;
} else if (selectedFilter.id === "inversion") {
inversion = filterSlider.value;
} else {
grayscale = filterSlider.value;
}
applyFilter();
};
rotateOptions.forEach((option) => {
option.addEventListener("click", () => {
if (option.id === "left") {
rotate -= 90;
} else if (option.id === "right") {
rotate += 90;
} else if (option.id === "horizontal") {
flipHorizontal = flipHorizontal === 1 ? -1 : 1;
} else {
flipVertical = flipVertical === 1 ? -1 : 1;
}
applyFilter();
});
});
const resetFilter = () => {
brightness = "100";
saturation = "100";
inversion = "0";
grayscale = "0";
rotate = 0;
flipHorizontal = 1;
flipVertical = 1;
filterOptions[0].click();
applyFilter();
};
const saveImage = () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = previewImg.naturalWidth;
canvas.height = previewImg.naturalHeight;
ctx.filter = `brightness(${brightness}%) saturate(${saturation}%) invert(${inversion}%) grayscale(${grayscale}%)`;
ctx.translate(canvas.width / 2, canvas.height / 2);
if (rotate !== 0) {
ctx.rotate((rotate * Math.PI) / 180);
}
ctx.scale(flipHorizontal, flipVertical);
ctx.drawImage(
previewImg,
-canvas.width / 2,
-canvas.height / 2,
canvas.width,
canvas.height
);
const link = document.createElement("a");
link.download = "image.jpg";
link.href = canvas.toDataURL();
link.click();
};
filterSlider.addEventListener("input", updateFilter);
resetFilterBtn.addEventListener("click", resetFilter);
saveImgBtn.addEventListener("click", saveImage);
fileInput.addEventListener("change", loadImage);
chooseImgBtn.addEventListener("click", () => fileInput.click());
That’s all! hopefully, you have successfully created canvas image editor JavaScript. If you have any questions or suggestions, feel free to comment below.
