canvas based cropping
All checks were successful
Build Images and Deploy / Update-PROD-Stack (push) Successful in 1m34s
All checks were successful
Build Images and Deploy / Update-PROD-Stack (push) Successful in 1m34s
This commit is contained in:
@@ -125,6 +125,8 @@
|
||||
.video-preview {
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.video-preview video {
|
||||
@@ -132,6 +134,47 @@
|
||||
max-height: 400px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.crop-canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
cursor: crosshair;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.crop-canvas.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.crop-mode-btn {
|
||||
margin: 10px 0;
|
||||
padding: 10px 20px;
|
||||
background: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.crop-mode-btn:hover {
|
||||
background: #218838;
|
||||
}
|
||||
|
||||
.crop-mode-btn.active {
|
||||
background: #dc3545;
|
||||
}
|
||||
|
||||
.crop-hint {
|
||||
color: #28a745;
|
||||
font-size: 0.9em;
|
||||
margin: 5px 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.video-info {
|
||||
@@ -312,7 +355,10 @@
|
||||
|
||||
<div class="video-preview">
|
||||
<video id="video-preview" controls></video>
|
||||
<canvas id="crop-canvas" class="crop-canvas"></canvas>
|
||||
</div>
|
||||
<button class="crop-mode-btn" id="crop-mode-btn">🎯 Click to Draw Crop Area</button>
|
||||
<p class="crop-hint" id="crop-hint" style="display: none;">Click and drag on the video to select crop area</p>
|
||||
|
||||
<div class="video-info" id="video-info"></div>
|
||||
|
||||
@@ -404,12 +450,20 @@
|
||||
<script>
|
||||
let currentFileId = null;
|
||||
let videoInfo = null;
|
||||
let cropMode = false;
|
||||
let cropStartX = 0;
|
||||
let cropStartY = 0;
|
||||
let cropRect = null;
|
||||
|
||||
// Upload area handling
|
||||
const uploadArea = document.getElementById('upload-area');
|
||||
const videoInput = document.getElementById('video-input');
|
||||
const uploadLoader = document.getElementById('upload-loader');
|
||||
const processLoader = document.getElementById('process-loader');
|
||||
const cropCanvas = document.getElementById('crop-canvas');
|
||||
const cropCtx = cropCanvas.getContext('2d');
|
||||
const cropModeBtn = document.getElementById('crop-mode-btn');
|
||||
const cropHint = document.getElementById('crop-hint');
|
||||
|
||||
uploadArea.addEventListener('click', () => videoInput.click());
|
||||
|
||||
@@ -474,6 +528,9 @@
|
||||
// Set default values
|
||||
document.getElementById('end-time').value = videoInfo.duration.toFixed(2);
|
||||
|
||||
// Setup canvas for cropping
|
||||
setupCropCanvas();
|
||||
|
||||
// Show edit section
|
||||
document.getElementById('upload-section').classList.add('hidden');
|
||||
document.getElementById('edit-section').classList.remove('hidden');
|
||||
@@ -485,6 +542,131 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Setup crop canvas
|
||||
function setupCropCanvas() {
|
||||
const video = document.getElementById('video-preview');
|
||||
|
||||
// Wait for video to load metadata
|
||||
video.addEventListener('loadedmetadata', () => {
|
||||
resizeCropCanvas();
|
||||
});
|
||||
|
||||
// Resize canvas when window resizes
|
||||
window.addEventListener('resize', resizeCropCanvas);
|
||||
}
|
||||
|
||||
function resizeCropCanvas() {
|
||||
const video = document.getElementById('video-preview');
|
||||
cropCanvas.width = video.offsetWidth;
|
||||
cropCanvas.height = video.offsetHeight;
|
||||
}
|
||||
|
||||
// Crop mode toggle
|
||||
cropModeBtn.addEventListener('click', () => {
|
||||
cropMode = !cropMode;
|
||||
if (cropMode) {
|
||||
cropCanvas.classList.add('active');
|
||||
cropModeBtn.classList.add('active');
|
||||
cropModeBtn.textContent = '❌ Cancel Crop Selection';
|
||||
cropHint.style.display = 'block';
|
||||
document.getElementById('video-preview').pause();
|
||||
} else {
|
||||
cropCanvas.classList.remove('active');
|
||||
cropModeBtn.classList.remove('active');
|
||||
cropModeBtn.textContent = '🎯 Click to Draw Crop Area';
|
||||
cropHint.style.display = 'none';
|
||||
clearCropCanvas();
|
||||
}
|
||||
});
|
||||
|
||||
// Canvas drawing for crop selection
|
||||
let isDrawing = false;
|
||||
let startX, startY;
|
||||
|
||||
cropCanvas.addEventListener('mousedown', (e) => {
|
||||
if (!cropMode) return;
|
||||
isDrawing = true;
|
||||
const rect = cropCanvas.getBoundingClientRect();
|
||||
startX = e.clientX - rect.left;
|
||||
startY = e.clientY - rect.top;
|
||||
});
|
||||
|
||||
cropCanvas.addEventListener('mousemove', (e) => {
|
||||
if (!cropMode || !isDrawing) return;
|
||||
const rect = cropCanvas.getBoundingClientRect();
|
||||
const currentX = e.clientX - rect.left;
|
||||
const currentY = e.clientY - rect.top;
|
||||
|
||||
drawCropRect(startX, startY, currentX - startX, currentY - startY);
|
||||
});
|
||||
|
||||
cropCanvas.addEventListener('mouseup', (e) => {
|
||||
if (!cropMode || !isDrawing) return;
|
||||
isDrawing = false;
|
||||
|
||||
const rect = cropCanvas.getBoundingClientRect();
|
||||
const endX = e.clientX - rect.left;
|
||||
const endY = e.clientY - rect.top;
|
||||
|
||||
const width = Math.abs(endX - startX);
|
||||
const height = Math.abs(endY - startY);
|
||||
const x = Math.min(startX, endX);
|
||||
const y = Math.min(startY, endY);
|
||||
|
||||
// Calculate actual video coordinates
|
||||
const video = document.getElementById('video-preview');
|
||||
const scaleX = videoInfo.width / video.offsetWidth;
|
||||
const scaleY = videoInfo.height / video.offsetHeight;
|
||||
|
||||
const actualX = Math.round(x * scaleX);
|
||||
const actualY = Math.round(y * scaleY);
|
||||
const actualWidth = Math.round(width * scaleX);
|
||||
const actualHeight = Math.round(height * scaleY);
|
||||
|
||||
// Update form fields
|
||||
document.getElementById('crop-x').value = actualX;
|
||||
document.getElementById('crop-y').value = actualY;
|
||||
document.getElementById('crop-width').value = actualWidth;
|
||||
document.getElementById('crop-height').value = actualHeight;
|
||||
|
||||
// Exit crop mode
|
||||
cropMode = false;
|
||||
cropModeBtn.classList.remove('active');
|
||||
cropModeBtn.textContent = '🎯 Click to Draw Crop Area';
|
||||
cropHint.style.display = 'none';
|
||||
|
||||
// Keep the rectangle visible for a moment
|
||||
setTimeout(() => {
|
||||
cropCanvas.classList.remove('active');
|
||||
}, 500);
|
||||
});
|
||||
|
||||
function drawCropRect(x, y, width, height) {
|
||||
clearCropCanvas();
|
||||
|
||||
// Draw semi-transparent overlay
|
||||
cropCtx.fillStyle = 'rgba(0, 0, 0, 0.5)';
|
||||
cropCtx.fillRect(0, 0, cropCanvas.width, cropCanvas.height);
|
||||
|
||||
// Clear the selected area
|
||||
cropCtx.clearRect(x, y, width, height);
|
||||
|
||||
// Draw border around selection
|
||||
cropCtx.strokeStyle = '#667eea';
|
||||
cropCtx.lineWidth = 3;
|
||||
cropCtx.strokeRect(x, y, width, height);
|
||||
|
||||
// Draw dimensions text
|
||||
cropCtx.fillStyle = '#667eea';
|
||||
cropCtx.font = 'bold 14px sans-serif';
|
||||
const text = `${Math.abs(Math.round(width))} × ${Math.abs(Math.round(height))}`;
|
||||
cropCtx.fillText(text, x + 5, y - 5);
|
||||
}
|
||||
|
||||
function clearCropCanvas() {
|
||||
cropCtx.clearRect(0, 0, cropCanvas.width, cropCanvas.height);
|
||||
}
|
||||
|
||||
// Resolution presets
|
||||
document.querySelectorAll('.preset-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
@@ -576,9 +758,16 @@
|
||||
|
||||
currentFileId = null;
|
||||
videoInfo = null;
|
||||
cropMode = false;
|
||||
cropRect = null;
|
||||
|
||||
document.getElementById('video-preview').src = '';
|
||||
videoInput.value = '';
|
||||
clearCropCanvas();
|
||||
cropCanvas.classList.remove('active');
|
||||
cropModeBtn.classList.remove('active');
|
||||
cropModeBtn.textContent = '🎯 Click to Draw Crop Area';
|
||||
cropHint.style.display = 'none';
|
||||
|
||||
document.getElementById('upload-section').classList.remove('hidden');
|
||||
document.getElementById('edit-section').classList.add('hidden');
|
||||
|
||||
Reference in New Issue
Block a user