Skip to content

Commit 30a718b

Browse files
authored
Merge pull request #903 from AshishGupta18/ImageDitor-AshishGupta18
An Image Editing Webapp
2 parents 1a7b514 + e42cea2 commit 30a718b

File tree

7 files changed

+391
-0
lines changed

7 files changed

+391
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Image-Editor : image-ditor.vercel.app
2+
A Webapp for editing an image by uploading pictures.
3+
4+
![Alt text](<Image-Ditor.png>)
2.24 MB
Loading

ImageEditor/AshishGupta18/app.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
const fileInput = document.querySelector(".file-input"),
2+
filterOptions = document.querySelectorAll(".filter button"),
3+
filterName = document.querySelector(".filter-info .name"),
4+
filterValue = document.querySelector(".filter-info .value"),
5+
filterSlider = document.querySelector(".slider input"),
6+
rotateOptions = document.querySelectorAll(".rotate button"),
7+
previewImg = document.querySelector(".preview-img img"),
8+
resetFilterBtn = document.querySelector(".reset-filter"),
9+
chooseImgBtn = document.querySelector(".choose-img"),
10+
saveImgBtn = document.querySelector(".save-img");
11+
12+
let brightness = "100", saturation = "100", inversion = "0", grayscale = "0";
13+
let rotate = 0, flipHorizontal = 1, flipVertical = 1;
14+
15+
const loadImage = () => {
16+
let file = fileInput.files[0];
17+
if(!file) return;
18+
previewImg.src = URL.createObjectURL(file);
19+
previewImg.addEventListener("load", () => {
20+
resetFilterBtn.click();
21+
document.querySelector(".container").classList.remove("disable");
22+
});
23+
}
24+
25+
const applyFilter = () => {
26+
previewImg.style.transform = `rotate(${rotate}deg) scale(${flipHorizontal}, ${flipVertical})`;
27+
previewImg.style.filter = `brightness(${brightness}%) saturate(${saturation}%) invert(${inversion}%) grayscale(${grayscale}%)`;
28+
}
29+
30+
filterOptions.forEach(option => {
31+
option.addEventListener("click", () => {
32+
document.querySelector(".active").classList.remove("active");
33+
option.classList.add("active");
34+
filterName.innerText = option.innerText;
35+
36+
if(option.id === "brightness") {
37+
filterSlider.max = "200";
38+
filterSlider.value = brightness;
39+
filterValue.innerText = `${brightness}%`;
40+
} else if(option.id === "saturation") {
41+
filterSlider.max = "200";
42+
filterSlider.value = saturation;
43+
filterValue.innerText = `${saturation}%`
44+
} else if(option.id === "inversion") {
45+
filterSlider.max = "100";
46+
filterSlider.value = inversion;
47+
filterValue.innerText = `${inversion}%`;
48+
} else {
49+
filterSlider.max = "100";
50+
filterSlider.value = grayscale;
51+
filterValue.innerText = `${grayscale}%`;
52+
}
53+
});
54+
});
55+
56+
const updateFilter = () => {
57+
filterValue.innerText = `${filterSlider.value}%`;
58+
const selectedFilter = document.querySelector(".filter .active");
59+
60+
if(selectedFilter.id === "brightness") {
61+
brightness = filterSlider.value;
62+
} else if(selectedFilter.id === "saturation") {
63+
saturation = filterSlider.value;
64+
} else if(selectedFilter.id === "inversion") {
65+
inversion = filterSlider.value;
66+
} else {
67+
grayscale = filterSlider.value;
68+
}
69+
applyFilter();
70+
}
71+
72+
rotateOptions.forEach(option => {
73+
option.addEventListener("click", () => {
74+
if(option.id === "left") {
75+
rotate -= 90;
76+
} else if(option.id === "right") {
77+
rotate += 90;
78+
} else if(option.id === "horizontal") {
79+
flipHorizontal = flipHorizontal === 1 ? -1 : 1;
80+
} else {
81+
flipVertical = flipVertical === 1 ? -1 : 1;
82+
}
83+
applyFilter();
84+
});
85+
});
86+
87+
const resetFilter = () => {
88+
brightness = "100"; saturation = "100"; inversion = "0"; grayscale = "0";
89+
rotate = 0; flipHorizontal = 1; flipVertical = 1;
90+
filterOptions[0].click();
91+
applyFilter();
92+
}
93+
94+
const saveImage = () => {
95+
const canvas = document.createElement("canvas");
96+
const ctx = canvas.getContext("2d");
97+
canvas.width = previewImg.naturalWidth;
98+
canvas.height = previewImg.naturalHeight;
99+
100+
ctx.filter = `brightness(${brightness}%) saturate(${saturation}%) invert(${inversion}%) grayscale(${grayscale}%)`;
101+
ctx.translate(canvas.width / 2, canvas.height / 2);
102+
if(rotate !== 0) {
103+
ctx.rotate(rotate * Math.PI / 180);
104+
}
105+
ctx.scale(flipHorizontal, flipVertical);
106+
ctx.drawImage(previewImg, -canvas.width / 2, -canvas.height / 2, canvas.width, canvas.height);
107+
108+
const link = document.createElement("a");
109+
link.download = "image.jpg";
110+
link.href = canvas.toDataURL();
111+
link.click();
112+
}
113+
114+
filterSlider.addEventListener("input", updateFilter);
115+
resetFilterBtn.addEventListener("click", resetFilter);
116+
saveImgBtn.addEventListener("click", saveImage);
117+
fileInput.addEventListener("change", loadImage);
118+
chooseImgBtn.addEventListener("click", () => fileInput.click());
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<!DOCTYPE html>
2+
<html lang="en" dir="ltr">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Image-Editor</title>
6+
<link rel="icon" href="logo1.png ">
7+
<link rel="stylesheet" href="style.css">
8+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
9+
<link rel="stylesheet" href="https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css">
10+
<link rel="stylesheet" href="style.css">
11+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css"/>
12+
</head>
13+
<body>
14+
<div class="container disable">
15+
<h2>Image Editor</h2>
16+
<div class="wrapper">
17+
<div class="editor-panel">
18+
<div class="filter">
19+
<label class="title">Filters</label>
20+
<div class="options">
21+
<button id="brightness" class="active">Brightness</button>
22+
<button id="saturation">Saturation</button>
23+
<button id="inversion">Inversion</button>
24+
<button id="grayscale">Grayscale</button>
25+
</div>
26+
<div class="slider">
27+
<div class="filter-info">
28+
<p class="name">Brighteness</p>
29+
<p class="value">100%</p>
30+
</div>
31+
<input type="range" value="100" min="0" max="200">
32+
</div>
33+
</div>
34+
<div class="rotate">
35+
<label class="title">Rotate & Flip</label>
36+
<div class="options">
37+
<button id="left"><i class="fa-solid fa-rotate-left"></i></button>
38+
<button id="right"><i class="fa-solid fa-rotate-right"></i></button>
39+
<button id="horizontal"><i class='bx bx-reflect-vertical'></i></button>
40+
<button id="vertical"><i class='bx bx-reflect-horizontal' ></i></button>
41+
</div>
42+
</div>
43+
</div>
44+
<div class="preview-img">
45+
<img src="logo3.jpeg" alt="Select Image To Proceed">
46+
</div>
47+
</div>
48+
<div class="controls">
49+
<button class="reset-filter">Reset Filters</button>
50+
<div class="row">
51+
<input type="file" class="file-input" accept="image/*" hidden>
52+
<button class="choose-img">Choose Image</button>
53+
<button class="save-img">Save Image</button>
54+
</div>
55+
</div>
56+
</div>
57+
58+
<script src="app.js"></script>
59+
60+
</body>
61+
</html>
932 KB
Loading
130 KB
Loading
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap');
2+
*{
3+
margin: 0;
4+
padding: 0;
5+
box-sizing: border-box;
6+
font-family: 'Poppins', sans-serif;
7+
}
8+
:root{
9+
/* Base font size*/
10+
font-size: 10px;
11+
/* Set neon color*/
12+
/* --neon-text-color: skyblue; */
13+
--neon-border-color: rgb(104, 65, 183);
14+
}
15+
body{
16+
display: flex;
17+
padding: 10px;
18+
min-height: 100vh;
19+
align-items: center;
20+
justify-content: center;
21+
background: radial-gradient(circle, rgba(238,174,202,1) 0%, rgba(2,131,153,1) 0%, rgba(0,45,255,1) 0%, rgba(132,150,212,0.9906556372549019) 67%, rgba(0,47,227,1) 100%, rgba(228,217,228,1) 100%);
22+
}
23+
24+
.container{
25+
width: 850px;
26+
padding: 30px 35px 35px;
27+
background: skyblue;
28+
border-radius: 10px;
29+
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
30+
animation: flicker 1.5s infinite alternate;
31+
}
32+
.container::-moz-selection {
33+
background-color: var(--neon-border-color);
34+
color: var(--neon-text-color);
35+
}
36+
37+
.container::selection {
38+
background-color: var(--neon-border-color);
39+
color: var(--neon-text-color);
40+
}
41+
42+
.container:focus {
43+
outline: none;
44+
}
45+
46+
47+
48+
.container.disable .editor-panel,
49+
.container.disable .controls .reset-filter,
50+
.container.disable .controls .save-img{
51+
opacity: 0.69;
52+
pointer-events: none;
53+
}
54+
.container h2{
55+
margin-top: -8px;
56+
font-size: 22px;
57+
font-weight: 500;
58+
text-align: center;
59+
font-style:normal;
60+
color: #0f1315;
61+
}
62+
.container .wrapper{
63+
display: flex;
64+
margin: 20px 0;
65+
min-height: 335px;
66+
}
67+
.wrapper .editor-panel{
68+
padding: 15px 20px;
69+
width: 280px;
70+
border-radius: 5px;
71+
border: 4px solid rgb(32, 106, 185);
72+
}
73+
.editor-panel .title{
74+
display: block;
75+
font-size: 16px;
76+
margin-bottom: 12px;
77+
}
78+
.editor-panel .options, .controls{
79+
display: flex;
80+
flex-wrap: wrap;
81+
justify-content: space-between;
82+
}
83+
.editor-panel button{
84+
outline: none;
85+
height: 40px;
86+
font-size: 14px;
87+
color: #6C757D;
88+
background: #fff;
89+
border-radius: 3px;
90+
margin-bottom: 8px;
91+
border: 1px solid #aaa;
92+
}
93+
.editor-panel .filter button{
94+
width: calc(100% / 2 - 4px);
95+
}
96+
.editor-panel button:hover{
97+
background: #f5f5f5;
98+
}
99+
.filter button.active{
100+
color: #fff;
101+
border-color: #5372F0;
102+
background: #5372F0;
103+
}
104+
.filter .slider{
105+
margin-top: 12px;
106+
}
107+
.filter .slider .filter-info{
108+
display: flex;
109+
color: #464646;
110+
font-size: 14px;
111+
justify-content: space-between;
112+
}
113+
.filter .slider input{
114+
width: 100%;
115+
height: 5px;
116+
accent-color: #5372F0;
117+
}
118+
.editor-panel .rotate{
119+
margin-top: 17px;
120+
}
121+
.editor-panel .rotate button{
122+
display: flex;
123+
align-items: center;
124+
justify-content: center;
125+
width: calc(100% / 4 - 3px);
126+
}
127+
.rotate .options button:nth-child(3),
128+
.rotate .options button:nth-child(4){
129+
font-size: 18px;
130+
}
131+
.rotate .options button:active{
132+
color: #fff;
133+
background: #5372F0;
134+
border-color: #5372F0;
135+
}
136+
.wrapper .preview-img{
137+
flex-grow: 1;
138+
display: flex;
139+
overflow: hidden;
140+
margin-left: 20px;
141+
border-radius: 5px;
142+
align-items: center;
143+
justify-content: center;
144+
}
145+
.preview-img img{
146+
max-width: 490px;
147+
max-height: 335px;
148+
width: 100%;
149+
height: 100%;
150+
object-fit: contain;
151+
}
152+
.controls button{
153+
padding: 11px 20px;
154+
font-size: 14px;
155+
border-radius: 3px;
156+
outline: none;
157+
color: #fff;
158+
cursor: pointer;
159+
background: none;
160+
transition: all 0.3s ease;
161+
text-transform: uppercase;
162+
}
163+
.controls .reset-filter{
164+
color: #6C757D;
165+
border: 1px solid #6C757D;
166+
}
167+
.controls .reset-filter:hover{
168+
color: #fff;
169+
background: #6C757D;
170+
}
171+
.controls .choose-img{
172+
background: #6C757D;
173+
border: 1px solid #6C757D;
174+
}
175+
.controls .save-img{
176+
margin-left: 5px;
177+
background: #5372F0;
178+
border: 1px solid #5372F0;
179+
}
180+
181+
@media screen and (max-width: 760px) {
182+
.container{
183+
padding: 25px;
184+
}
185+
.container .wrapper{
186+
flex-wrap: wrap-reverse;
187+
}
188+
.wrapper .editor-panel{
189+
width: 100%;
190+
}
191+
.wrapper .preview-img{
192+
width: 100%;
193+
margin: 0 0 15px;
194+
}
195+
}
196+
197+
@media screen and (max-width: 500px) {
198+
.controls button{
199+
width: 100%;
200+
margin-bottom: 10px;
201+
}
202+
.controls .row{
203+
width: 100%;
204+
}
205+
.controls .row .save-img{
206+
margin-left: 0px;
207+
}
208+
}

0 commit comments

Comments
 (0)