├── demo.gif
├── README.md
├── .gitignore
├── index.html
├── style
└── style.css
└── scripts
└── main.js
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayhan/image-resizer/HEAD/demo.gif
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Image Resizer
2 |
3 | You can use this to resize your images. Crop, Rotation, optimization etc. features coming soon...
4 |
5 | See Live : https://ayhan.github.io/image-resizer -- [Codepen](https://codepen.io/ayhankrkc/pen/mdxOyNQ)
6 |
7 | 
8 |
9 | ### Sources:
10 | Colors: https://flatuicolors.com/palette/defo
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Image Resizer
9 |
10 |
11 |
12 |
13 | Image Resizer
14 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/style/style.css:
--------------------------------------------------------------------------------
1 | :root{
2 | --clouds: #ecf0f1;
3 | --silver: #bdc3c7;
4 | --concrete: #95a5a6;
5 | --asbestos: #7f8c8d;
6 |
7 | --wetAsphalt: #34495e;
8 | --midnightBlue: #2c3e50;
9 |
10 | --alizarin: #e74c3c;
11 | --pomegranate: #c0392b;
12 |
13 | --emerald: #2ecc71;
14 | --nephritis: #27ae60;
15 | }
16 |
17 | * {
18 | font-family: sans-serif;
19 | }
20 |
21 | ::-webkit-scrollbar {
22 | width: .5rem;
23 | }
24 |
25 | ::-webkit-scrollbar-track {
26 | background: var(--silver);
27 | border-radius: .75rem;
28 | }
29 |
30 | ::-webkit-scrollbar-thumb {
31 | background: var(--wetAsphalt);
32 | border-radius: .75rem;
33 | }
34 |
35 | ::-webkit-scrollbar-thumb:hover {
36 | background: var(--midnightBlue);
37 | }
38 |
39 | .container {
40 | max-width: 920px;
41 | margin: auto;
42 | padding: 1.25rem;
43 | border-radius: 10px;
44 | background: var(--clouds);
45 | }
46 |
47 | .file-form {
48 | position: relative;
49 | padding: 2.5rem;
50 | border: 2px dashed var(--silver);
51 | }
52 |
53 | .file-form:hover {
54 | border-color: var(--asbestos);
55 | }
56 |
57 | .file-form.highlight {
58 | border-color: var(--asbestos);
59 | }
60 |
61 | .drop-content {
62 | position: absolute;
63 | top: 0;
64 | left: 0;
65 | right: 0;
66 | bottom: 0;
67 | cursor: pointer;
68 | text-align: center;
69 | padding-top: 2rem;
70 | font-weight: bold;
71 | color: var(--wetAsphalt);
72 | }
73 |
74 | #fileInput {
75 | display: none;
76 | }
77 |
78 | #uploadedImage {
79 | margin-top: 1.25rem;
80 | max-height: 750px;
81 | overflow-y: auto;
82 | display: flex;
83 | flex-wrap: wrap;
84 | }
85 |
86 | .d-none {
87 | display: none !important;
88 | }
89 |
90 | #actionContainer {
91 | display: flex;
92 | justify-content: space-between;
93 | margin: 1.25rem 1rem;
94 | }
95 |
96 | #actionContainer input {
97 | padding: .75rem;
98 | border: none;
99 | margin-right: .5rem;
100 | color: var(--midnightBlue);
101 | border-radius: .5rem;
102 | }
103 | #actionContainer input:focus {
104 | outline: none !important;
105 | box-shadow: 0 1px 5px var(--wetAsphalt);
106 | }
107 |
108 | #actionContainer button {
109 | border: none;
110 | border-radius: .5rem;
111 | font-size: 12px;
112 | cursor: pointer;
113 | color: var(--clouds);
114 | padding: .75rem;
115 | }
116 |
117 | #executeBtn{
118 | background-color: var(--wetAsphalt);
119 | }
120 | #executeBtn:hover {
121 | background-color: var(--midnightBlue);
122 | }
123 | #executeBtn:active {
124 | background-color: var(--midnightBlue);
125 | transform: scale(1.1);
126 | }
127 |
128 |
129 | #clearAllBtn{
130 | color: var(--clouds);
131 | background-color: var(--alizarin);
132 | }
133 |
134 | #clearAllBtn:active {
135 | background-color: var(--pomegranate);
136 | transform: scale(1.1);
137 | }
138 |
139 | #clearAllBtn:hover {
140 | background-color: var(--pomegranate);
141 | }
142 |
143 | .image-content {
144 | cursor: pointer;
145 | display: flex;
146 | flex-direction: column;
147 | align-items: center;
148 | border: 1px dashed var(--silver);
149 | border-radius: .25rem;
150 | margin: .70rem;
151 | padding: .25rem;
152 | }
153 |
154 | .image-wrapper {
155 | position: relative;
156 | }
157 |
158 | .image-wrapper img {
159 | transition: 1s;
160 | width: 150px;
161 | }
162 |
163 | .image-wrapper:hover img {
164 | filter: blur(2px);
165 | }
166 |
167 | .image-wrapper span {
168 | display: none;
169 | position: absolute;
170 | top: 50%;
171 | left: 50%;
172 | transform: translate(-50%, -50%);
173 | color: white;
174 | font-weight: bold;
175 | font-size: 1.5rem;
176 | background: #454545;
177 | padding: 1rem;
178 | border-radius: 50%;
179 | line-height: 1rem;
180 | }
181 |
182 | .image-wrapper:hover span {
183 | display: block;
184 | }
185 |
186 | .download-button {
187 | background-color: var(--emerald);
188 | padding: .75rem;
189 | font-size: 0.75rem;
190 | text-decoration: none;
191 | border-radius: .5rem;
192 | animation: 1s;
193 | color: var(--clouds);
194 | margin-bottom: 0.5rem;
195 | margin-top: 0.5rem;
196 | }
197 |
198 | .download-button:hover {
199 | background-color: var(--nephritis);
200 | color: var(--clouds);
201 | }
202 |
203 | .download-button:active {
204 | transform: scale(1.1);
205 | }
206 |
207 | .title{
208 | text-align: center;
209 | margin-top: 4rem;
210 | color: var(--wetAsphalt);
211 | }
212 |
213 | @media only screen and (max-width: 620px) {
214 | #actionContainer .execute-section {
215 | width: 50%;
216 | }
217 | #actionContainer input, #actionContainer button {
218 | margin: .25rem;
219 | }
220 | #uploadedImage {
221 | justify-content: center;
222 | }
223 | }
--------------------------------------------------------------------------------
/scripts/main.js:
--------------------------------------------------------------------------------
1 | const app = {
2 | selector: {
3 | dropArea: document.getElementById("dropSection"),
4 | actionContainer: document.getElementById("actionContainer"),
5 | uploadedImages: document.getElementById("uploadedImage"),
6 | widthValue: document.getElementById("widthValue"),
7 | heightValue: document.getElementById("heightValue")
8 | },
9 | actions: {
10 | highlightAdd: () => {
11 | app.selector.dropArea.classList.add('highlight')
12 | },
13 | highlightRemove: () => {
14 | app.selector.dropArea.classList.remove('highlight')
15 | },
16 | handleFiles: (files) => {
17 | files = [...files]
18 | files.forEach(app.actions.previewFile)
19 | },
20 | handleDrop: (e) => {
21 | let dt = e.dataTransfer
22 | let files = dt.files
23 |
24 | app.actions.handleFiles(files)
25 | },
26 | previewFile: (file) => {
27 | let reader = new FileReader()
28 | reader.readAsDataURL(file)
29 | reader.onloadend = () => {
30 | let elems = `
X `;
31 | app.selector.uploadedImages.insertAdjacentHTML("beforeend", elems);
32 | app.selector.actionContainer.classList.remove('d-none')
33 | }
34 | },
35 | imageDelete: (scope) => {
36 | scope.parentNode.parentNode.remove();
37 | app.selector.uploadedImages.innerHTML == '' && app.selector.actionContainer.classList.add('d-none');
38 | },
39 | clearAll: () => {
40 | app.selector.uploadedImages.innerHTML = '';
41 | app.selector.actionContainer.classList.add('d-none')
42 | },
43 | preventDefaults: (e) => {
44 | e.preventDefault()
45 | e.stopPropagation()
46 | },
47 | aspectRatio: (w, h, mw, mh) => {
48 | let ratio = w / h;
49 | if (mh * ratio < mw) {
50 | return [mw, mw / ratio];
51 | } else {
52 | return [mh * ratio, mh];
53 | }
54 | },
55 | resizeImages: (base64Str, maxWidth, maxHeight) => {
56 | return new Promise((resolve) => {
57 | const img = new Image()
58 | img.src = base64Str
59 | img.onload = () => {
60 | const canvas = document.createElement('canvas')
61 | let width = img.width
62 | let height = img.height
63 |
64 | const newSize = app.actions.aspectRatio(width, height, maxWidth, maxHeight);
65 |
66 | width = newSize[0];
67 | height = newSize[1];
68 |
69 | canvas.width = width
70 | canvas.height = height
71 | const ctx = canvas.getContext('2d')
72 | ctx.drawImage(img, 0, 0, width, height)
73 | resolve(canvas.toDataURL())
74 | }
75 | })
76 | },
77 | execute: () => {
78 | const images = Array.from(app.selector.uploadedImages.querySelectorAll('img'));
79 | const width = app.selector.widthValue.value || 400;
80 | const height = app.selector.heightValue.value || 400;
81 |
82 | images.forEach(item => {
83 | app.actions.resizeImages(item.getAttribute('src'), width, height).then((result) => {
84 | let downloadBtn = `Download`;
85 | item.parentNode.insertAdjacentHTML("afterend", downloadBtn);
86 | });
87 | })
88 | }
89 | },
90 | init: () => {
91 | app.selector.dropArea.addEventListener('drop', app.actions.handleDrop, false);
92 |
93 | ;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
94 | app.selector.dropArea.addEventListener(eventName, app.actions.preventDefaults, false)
95 | document.body.addEventListener(eventName, app.actions.preventDefaults, false)
96 | })
97 |
98 | ;['dragenter', 'dragover'].forEach(eventName => {
99 | app.selector.dropArea.addEventListener(eventName, app.actions.highlightAdd, false)
100 | })
101 |
102 | ;['dragleave', 'drop'].forEach(eventName => {
103 | app.selector.dropArea.addEventListener(eventName, app.actions.highlightRemove, false)
104 | })
105 | }
106 | }
107 |
108 | app.init();
109 |
110 |
111 | // function gcd(a, b) {
112 | // return (b == 0) ? a : gcd(b, a % b);
113 | // }
114 | // function ratio(x, y) {
115 | // c = gcd(x, y); return `${x / c}:${y / c}`
116 | // }
117 |
--------------------------------------------------------------------------------