├── 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 | ![](demo.gif) 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 |
15 |
16 | 17 | 25 |
26 |
27 |
28 | 29 | 30 | 31 |
32 |
33 | 34 |
35 |
36 |
37 |
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 = `
${file.name}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 | --------------------------------------------------------------------------------