Image desqueeze
by James Doyle
HTML
<div class="container">
<header>
<h1>Image Width Stretch Tool</h1>
<p class="subtitle">
Upload an image and adjust the width slider to stretch it horizontally.
Click on the output image to download the edited version.
</p>
</header>
<div class="upload-container">
<div class="upload-icon">📁</div>
<p>Upload an image to get started</p>
<input type="file" id="fileInput" class="file-input" accept="image/*" />
<button class="upload-btn" id="uploadBtn">Choose Image</button>
</div>
<div class="controls" id="controls">
<label for="stretchSlider"
>Stretch Factor: <span id="factorValue">1.00</span>x</label
>
<div class="slider-container">
<input
type="range"
min="1"
max="2"
step="0.01"
value="1"
class="slider"
id="stretchSlider"
/>
</div>
<div class="value-display">
Width: <span id="widthValue">100%</span> of original
</div>
</div>
<div class="canvas-container" id="canvasContainer">
<canvas id="imageCanvas"></canvas>
<div class="download-hint">Click on the image to download</div>
</div>
<div class="instructions">
<h2>How to Use</h2>
<ol>
<li>Click "Choose Image" to upload any image from your device</li>
<li>
Use the slider to adjust the horizontal stretch factor (1.00x to 2.00x)
</li>
<li>Watch the image stretch in real-time as you move the slider</li>
<li>Click on the output image to download the edited version</li>
<li>
All processing happens in your browser - your image never leaves your
computer
</li>
</ol>
</div>
</div>
<footer>
<p>Client-Side Image Processing | No Server Required</p>
</footer>
CSS
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c);
color: white;
min-height: 100vh;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
.container {
max-width: 900px;
width: 100%;
background: rgba(0, 0, 0, 0.7);
border-radius: 15px;
padding: 30px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
margin-top: 20px;
}
header {
text-align: center;
margin-bottom: 30px;
}
h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
.subtitle {
font-size: 1.1rem;
opacity: 0.9;
max-width: 600px;
margin: 0 auto;
}
.upload-container {
background: rgba(255, 255, 255, 0.1);
border: 2px dashed #4a90e2;
border-radius: 10px;
padding: 30px;
text-align: center;
margin-bottom: 30px;
transition: all 0.3s ease;
}
.upload-container:hover {
background: rgba(255, 255, 255, 0.15);
border-color: #5cb85c;
}
.upload-icon {
font-size: 3rem;
margin-bottom: 15px;
color: #4a90e2;
}
.file-input {
display: none;
}
.upload-btn {
background: #4a90e2;
color: white;
padding: 12px 25px;
border-radius: 50px;
font-size: 1.1rem;
cursor: pointer;
transition: all 0.3s ease;
border: none;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
}
.upload-btn:hover {
background: #357abd;
transform: translateY(-2px);
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.3);
}
.controls {
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
padding: 25px;
margin-bottom: 30px;
display: none;
}
.controls.active {
display: block;
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.slider-container...
JavaScript
const fileInput = document.getElementById("fileInput");
const uploadBtn = document.getElementById("uploadBtn");
const stretchSlider = document.getElementById("stretchSlider");
const factorValue = document.getElementById("factorValue");
const widthValue = document.getElementById("widthValue");
const controls = document.getElementById("controls");
const canvasContainer = document.getElementById("canvasContainer");
const canvas = document.getElementById("imageCanvas");
const ctx = canvas.getContext("2d");
let originalImage = null;
let currentStretchFactor = 1;
// Trigger file input when upload button is clicked
uploadBtn.addEventListener("click", () => {
fileInput.click();
});
// Handle file selection
fileInput.addEventListener("change", function (e) {
if (this.files && this.files[0]) {
const reader = new FileReader();
reader.onload = function (event) {
originalImage = new Image();
originalImage.onload = function () {
// Show controls and canvas
controls.classList.add("active");
canvasContainer.classList.add("active");
// Reset slider to default
stretchSlider.value = 1;
factorValue.textContent = "1.00";
widthValue.textContent = "100%";
currentStretchFactor = 1;
// Draw the original image
drawStretchedImage(1);
};
originalImage.src = event.target.result;
};
reader.readAsDataURL(this.files[0]);
}
});
// Handle slider changes
stretchSlider.addEventListener("input", function () {
const factor = parseFloat(this.value);
currentStretchFactor = factor;
factorValue.textContent = factor.toFixed(2);
widthValue.textContent = Math.round(factor * 100) + "%";
if (originalImage) {
drawStretchedImage(factor);
}
});
// Download image when canvas is clicked
canvas.addEventListener("click", function () {
if (originalImage) {
downloadImage();
}
});
// Function to...