Skip to content

Commit 0c2a2e9

Browse files
committed
Merge branch 'up/feat/image-resizer' into chore/all-my-stuffs
2 parents b7f17e8 + 04d3fa2 commit 0c2a2e9

File tree

4 files changed

+217
-0
lines changed

4 files changed

+217
-0
lines changed

locales/en.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,13 +432,19 @@ tools:
432432
text-to-binary:
433433
title: Text to ASCII binary
434434
description: Convert text to its ASCII binary representation and vice-versa.
435+
436+
image-resizer:
437+
title: Image resizer
438+
description: Convert the width and height of an image file, preview, and download it in a desired format (.jpg, .jpeg, .png, .bmp, .ico, .svg) json-to-object:
439+
435440
json-to-object:
436441
title: JSON to object
437442
description: Parse and convert JSON to object.
438443

439444
multi-link-downloader:
440445
title: Multi link downloader
441446
description: Asynchronously downloads from multiple links into a zip file while a single link downloads directly. (Requires an internet connection)
447+
442448
unicode-to-java-entities:
443449
title: Unicode Characters to Java Entities Converter
444450
description: Unicode Characters to Java Entities Converter and vice-versa
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
<script setup lang="ts">
2+
import { ref, watch } from 'vue';
3+
4+
// State variables
5+
const imageFile = ref<File | null>(null);
6+
const imageUrl = ref<string | null>(null);
7+
const originalImageUrl = ref<string | null>(null); // To store original image data
8+
const imageWidth = ref(500); // Default width
9+
const imageHeight = ref(350); // Default height
10+
const originalImageWidth = ref<number | null>(null); // Store original image width
11+
const originalImageHeight = ref<number | null>(null); // Store original image height
12+
const resizedImageUrl = ref<string | null>(null);
13+
14+
// Watch width to trigger resizing
15+
watch(imageWidth, () => {
16+
resizeImage();
17+
});
18+
19+
// Watch height to trigger resizing
20+
watch(imageHeight, () => {
21+
resizeImage();
22+
});
23+
24+
// Handle file upload
25+
async function handleFileUpload(uploadedFile: File) {
26+
if (!uploadedFile) {
27+
return;
28+
}
29+
30+
imageFile.value = uploadedFile;
31+
32+
try {
33+
// Read the file as a Data URL
34+
const reader = new FileReader();
35+
const fileDataUrl = await new Promise<string>((resolve, reject) => {
36+
reader.onload = () => resolve(reader.result as string);
37+
reader.onerror = error => reject(error);
38+
reader.readAsDataURL(uploadedFile);
39+
});
40+
41+
imageUrl.value = fileDataUrl; // Preview image
42+
originalImageUrl.value = fileDataUrl; // Store original image for resizing
43+
resizedImageUrl.value = null; // Clear previous resized image
44+
45+
// Create an image to get original dimensions
46+
const img = new Image();
47+
img.src = fileDataUrl;
48+
49+
await new Promise<void>((resolve) => {
50+
img.onload = () => {
51+
// Set original image dimensions
52+
originalImageWidth.value = img.naturalWidth;
53+
originalImageHeight.value = img.naturalHeight;
54+
55+
// Automatically resize if width and height are set
56+
if (imageWidth.value > 0 && imageHeight.value > 0) {
57+
resizeImage();
58+
}
59+
60+
resolve();
61+
};
62+
});
63+
}
64+
catch (error) {
65+
console.error('Error reading file:', error);
66+
}
67+
}
68+
69+
// Function to resize the image
70+
async function resizeImage() {
71+
if (!originalImageUrl.value) {
72+
return; // Ensure there's an original image to work with
73+
}
74+
75+
const img = new Image();
76+
img.src = originalImageUrl.value; // Use the original image for resizing
77+
78+
img.onload = () => {
79+
const canvas = document.createElement('canvas');
80+
canvas.width = imageWidth.value;
81+
canvas.height = imageHeight.value;
82+
83+
const ctx = canvas.getContext('2d');
84+
ctx?.drawImage(img, 0, 0, imageWidth.value, imageHeight.value);
85+
resizedImageUrl.value = canvas.toDataURL('image/png');
86+
};
87+
}
88+
89+
// Function to download resized image
90+
function downloadImage(format: string) {
91+
if (!resizedImageUrl.value || !imageFile.value) {
92+
return;
93+
}
94+
95+
const originalFilename = imageFile.value.name.replace(/\.[^/.]+$/, ''); // Remove file extension
96+
const newFilename = `${originalFilename}-${imageWidth.value}x${imageHeight.value}.${format}`;
97+
const link = document.createElement('a');
98+
link.href = resizedImageUrl.value;
99+
link.download = newFilename;
100+
link.click();
101+
}
102+
</script>
103+
104+
<template>
105+
<n-card>
106+
<div>
107+
<!-- File input -->
108+
<c-file-upload mb-2 accept=".jpg,.jpeg,.png,.bmp,.ico,.svg" title="Drag and drop a .jpg, .jpeg, .png, .bmp, .ico, .svg file here" @file-upload="handleFileUpload" />
109+
110+
<!-- Original image dimensions -->
111+
<div v-if="originalImageWidth && originalImageHeight">
112+
<p>Original Image Dimensions: {{ originalImageWidth }}x{{ originalImageHeight }}px</p>
113+
</div>
114+
115+
<!-- Width and height inputs -->
116+
<div class="input-group">
117+
<label for="widthInput">Width (px):</label>
118+
<n-input-number id="widthInput" v-model:value="imageWidth" placeholder="Width (px)" mb-3 />
119+
</div>
120+
121+
<div class="input-group">
122+
<label for="heightInput">Height (px):</label>
123+
<n-input-number id="heightInput" v-model:value="imageHeight" placeholder="Height (px)" mb-1 />
124+
</div>
125+
</div>
126+
127+
<!-- Image preview -->
128+
<div v-if="resizedImageUrl" class="image-container" style="text-align: center; margin-top: 20px;">
129+
<div class="image-wrapper">
130+
<img :src="resizedImageUrl" :alt="`Resized Preview (${imageWidth}px x ${imageHeight}px)`" :style="{ width: `${imageWidth}px`, height: `${imageHeight}px` }">
131+
</div>
132+
<p>Preview: {{ imageWidth }}x{{ imageHeight }}px</p>
133+
134+
<!-- Download options -->
135+
<h3>Download Options:</h3>
136+
<div class="download-grid">
137+
<n-button @click.prevent="downloadImage('jpg')">
138+
Download JPG
139+
</n-button>
140+
<n-button @click.prevent="downloadImage('png')">
141+
Download PNG
142+
</n-button>
143+
<n-button @click.prevent="downloadImage('bmp')">
144+
Download BMP
145+
</n-button>
146+
<n-button @click.prevent="downloadImage('svg')">
147+
Download SVG
148+
</n-button>
149+
<n-button @click.prevent="downloadImage('ico')">
150+
Download ICO
151+
</n-button>
152+
</div>
153+
</div>
154+
</n-card>
155+
</template>
156+
157+
<style scoped>
158+
.input-group {
159+
display: flex;
160+
flex-direction: column;
161+
justify-content: center;
162+
align-items: left;
163+
}
164+
165+
.input-group label {
166+
margin-right: 10px;
167+
width: 100px;
168+
}
169+
170+
.image-container {
171+
max-width: 100%;
172+
overflow: auto;
173+
}
174+
175+
.image-wrapper {
176+
max-width: 100%;
177+
max-height: 500px;
178+
overflow: auto;
179+
}
180+
181+
.download-grid {
182+
display: flex;
183+
justify-content: center;
184+
flex-wrap: wrap;
185+
gap: 10px;
186+
margin-top: 10px;
187+
}
188+
189+
a {
190+
color: #007bff;
191+
text-decoration: none;
192+
}
193+
194+
a:hover {
195+
text-decoration: underline;
196+
}
197+
</style>

src/tools/image-resizer/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { IconResize } from '@tabler/icons-vue';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Image resizer',
6+
path: '/image-resizer',
7+
description: '',
8+
keywords: ['image', 'resizer', 'favicon', 'jpg', 'jpeg', 'png', 'bmp', 'ico', 'svg'],
9+
component: () => import('./image-resizer.vue'),
10+
icon: IconResize,
11+
createdAt: new Date('2024-10-22'),
12+
});

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { tool as base64FileConverter } from './base64-file-converter';
22
import { tool as base64StringConverter } from './base64-string-converter';
33
import { tool as basicAuthGenerator } from './basic-auth-generator';
4+
import { tool as imageResizer } from './image-resizer';
45
import { tool as multiLinkDownloader } from './multi-link-downloader';
56
import { tool as aspectRatioCalculator } from './aspect-ratio-calculator';
67
import { tool as extractTextFromHtml } from './extract-text-from-html';
@@ -391,6 +392,7 @@ export const toolsByCategory: ToolCategory[] = [
391392
imageToAsciiArt,
392393
imageExifReader,
393394
aspectRatioCalculator,
395+
imageResizer,
394396
],
395397
},
396398
{

0 commit comments

Comments
 (0)