imagem / index.html
ocirema's picture
Add 2 files
99fd31f verified
<!DOCTYPE html>
<html lang="pt-PT">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Editor de Imagens Completo</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.image-container {
max-width: 100%;
max-height: 80vh; /* Aumentado de 70vh para 80vh */
overflow: auto;
border: 2px dashed #ccc;
position: relative;
}
#canvas {
max-width: 100%;
max-height: 80vh; /* Aumentado para corresponder ao container */
display: block;
}
.slider-container {
width: 100%;
}
.slider {
-webkit-appearance: none;
width: 100%;
height: 8px;
border-radius: 5px;
background: #d3d3d3;
outline: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #4f46e5;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: #4f46e5;
cursor: pointer;
}
.filter-option {
transition: all 0.2s;
}
.filter-option:hover {
transform: scale(1.05);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.filter-option.active {
border: 2px solid #4f46e5;
}
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 120px;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -60px;
opacity: 0;
transition: opacity 0.3s;
font-size: 12px;
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
</style>
</head>
<body class="bg-gray-100 font-sans">
<div class="container mx-auto px-4 py-8">
<h1 class="text-3xl font-bold text-center text-indigo-700 mb-8">Editor de Imagens Completo</h1>
<div class="bg-white rounded-lg shadow-lg overflow-hidden">
<!-- Header -->
<div class="bg-indigo-600 text-white px-6 py-4 flex justify-between items-center">
<h2 class="text-xl font-semibold">Editar Imagem</h2>
<div class="flex space-x-2">
<button id="resetBtn" class="bg-indigo-700 hover:bg-indigo-800 px-4 py-2 rounded-md text-sm">
<i class="fas fa-undo-alt mr-1"></i> Reiniciar
</button>
<button id="downloadBtn" class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded-md text-sm">
<i class="fas fa-download mr-1"></i> Descarregar
</button>
</div>
</div>
<div class="flex flex-col md:flex-row">
<!-- Sidebar -->
<div class="w-full md:w-1/4 bg-gray-50 p-4 border-r">
<div class="mb-6">
<h3 class="font-medium text-gray-700 mb-2">Carregar Imagem</h3>
<div class="flex flex-col space-y-2">
<input type="file" id="fileInput" accept="image/*" class="hidden">
<label for="fileInput" class="bg-indigo-100 text-indigo-700 hover:bg-indigo-200 px-4 py-2 rounded-md text-center cursor-pointer">
<i class="fas fa-folder-open mr-2"></i> Escolher Imagem
</label>
</div>
</div>
<div class="tabs mb-6">
<div class="flex border-b">
<button class="tab-btn active px-4 py-2 text-indigo-600 border-b-2 border-indigo-600" data-tab="filters">Filtros</button>
<button class="tab-btn px-4 py-2 text-gray-600" data-tab="adjust">Ajustes</button>
<button class="tab-btn px-4 py-2 text-gray-600" data-tab="tools">Ferramentas</button>
</div>
<div id="filters" class="tab-content active mt-4">
<div class="grid grid-cols-3 gap-2">
<div class="filter-option p-2 bg-white rounded cursor-pointer" data-filter="none">
<div class="w-full h-16 bg-gray-200 rounded mb-1"></div>
<p class="text-xs text-center">Original</p>
</div>
<div class="filter-option p-2 bg-white rounded cursor-pointer" data-filter="grayscale">
<div class="w-full h-16 bg-gray-400 rounded mb-1"></div>
<p class="text-xs text-center">Preto e Branco</p>
</div>
<div class="filter-option p-2 bg-white rounded cursor-pointer" data-filter="sepia">
<div class="w-full h-16 bg-yellow-300 rounded mb-1"></div>
<p class="text-xs text-center">Sépia</p>
</div>
<div class="filter-option p-2 bg-white rounded cursor-pointer" data-filter="invert">
<div class="w-full h-16 bg-indigo-200 rounded mb-1"></div>
<p class="text-xs text-center">Inverter</p>
</div>
<div class="filter-option p-2 bg-white rounded cursor-pointer" data-filter="blur">
<div class="w-full h-16 bg-blue-100 rounded mb-1"></div>
<p class="text-xs text-center">Desfocado</p>
</div>
<div class="filter-option p-2 bg-white rounded cursor-pointer" data-filter="hue-rotate">
<div class="w-full h-16 bg-purple-200 rounded mb-1"></div>
<p class="text-xs text-center">Matiz</p>
</div>
</div>
</div>
<div id="adjust" class="tab-content mt-4">
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Brilho</label>
<div class="slider-container">
<input type="range" min="-100" max="100" value="0" class="slider" id="brightnessSlider">
</div>
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>-100</span>
<span>0</span>
<span>100</span>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Contraste</label>
<div class="slider-container">
<input type="range" min="-100" max="100" value="0" class="slider" id="contrastSlider">
</div>
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>-100</span>
<span>0</span>
<span>100</span>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Saturação</label>
<div class="slider-container">
<input type="range" min="0" max="200" value="100" class="slider" id="saturationSlider">
</div>
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>0</span>
<span>100</span>
<span>200</span>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Temperatura</label>
<div class="slider-container">
<input type="range" min="-100" max="100" value="0" class="slider" id="temperatureSlider">
</div>
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>Frio</span>
<span>Neutro</span>
<span>Quente</span>
</div>
</div>
</div>
</div>
<div id="tools" class="tab-content mt-4">
<div class="grid grid-cols-2 gap-3">
<button id="cropBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
<i class="fas fa-crop mr-1"></i> Cortar
</button>
<button id="rotateBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
<i class="fas fa-undo mr-1"></i> Rodar
</button>
<button id="flipHBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
<i class="fas fa-arrows-alt-h mr-1"></i> Inverter H
</button>
<button id="flipVBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
<i class="fas fa-arrows-alt-v mr-1"></i> Inverter V
</button>
<button id="textBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
<i class="fas fa-font mr-1"></i> Texto
</button>
<button id="drawBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
<i class="fas fa-pencil-alt mr-1"></i> Desenhar
</button>
</div>
<div id="textOptions" class="mt-4 hidden">
<input type="text" id="textInput" placeholder="Introduza o texto" class="w-full px-3 py-2 border rounded mb-2">
<div class="flex space-x-2 mb-2">
<input type="color" id="textColor" value="#000000" class="w-8 h-8">
<select id="textFont" class="flex-1 px-2 py-1 border rounded">
<option value="Arial">Arial</option>
<option value="Times New Roman">Times New Roman</option>
<option value="Courier New">Courier New</option>
<option value="Georgia">Georgia</option>
<option value="Verdana">Verdana</option>
</select>
</div>
<div class="slider-container mb-2">
<label class="block text-xs text-gray-700 mb-1">Tamanho</label>
<input type="range" min="10" max="72" value="24" class="slider" id="textSizeSlider">
</div>
<button id="addTextBtn" class="w-full bg-indigo-600 text-white px-3 py-2 rounded-md text-sm">
Adicionar Texto
</button>
</div>
<div id="drawOptions" class="mt-4 hidden">
<div class="flex space-x-2 mb-2">
<input type="color" id="drawColor" value="#000000" class="w-8 h-8">
<div class="slider-container flex-1">
<label class="block text-xs text-gray-700 mb-1">Espessura</label>
<input type="range" min="1" max="20" value="5" class="slider" id="drawSizeSlider">
</div>
</div>
<div class="grid grid-cols-3 gap-2">
<button id="drawStartBtn" class="bg-indigo-600 text-white px-3 py-2 rounded-md text-sm">
Começar
</button>
<button id="drawClearBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-md text-sm">
Limpar
</button>
<button id="drawSaveBtn" class="bg-green-600 text-white px-3 py-2 rounded-md text-sm">
Guardar
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Main Content - Área de imagem aumentada -->
<div class="w-full md:w-3/4 p-6">
<div class="image-container mx-auto bg-gray-100 flex items-center justify-center" id="imageContainer">
<canvas id="canvas"></canvas>
<p class="text-gray-500 text-center p-8" id="placeholderText">Carregue uma imagem para começar a editar</p>
</div>
<div class="mt-4 flex justify-center space-x-4" id="cropControls" style="display: none;">
<button id="cropConfirmBtn" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-md">
<i class="fas fa-check mr-1"></i> Confirmar
</button>
<button id="cropCancelBtn" class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-md">
<i class="fas fa-times mr-1"></i> Cancelar
</button>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Elementos DOM
const fileInput = document.getElementById('fileInput');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const imageContainer = document.getElementById('imageContainer');
const placeholderText = document.getElementById('placeholderText');
const resetBtn = document.getElementById('resetBtn');
const downloadBtn = document.getElementById('downloadBtn');
// Elementos de filtros
const filterOptions = document.querySelectorAll('.filter-option');
// Elementos de ajustes
const brightnessSlider = document.getElementById('brightnessSlider');
const contrastSlider = document.getElementById('contrastSlider');
const saturationSlider = document.getElementById('saturationSlider');
const temperatureSlider = document.getElementById('temperatureSlider');
// Elementos de ferramentas
const cropBtn = document.getElementById('cropBtn');
const rotateBtn = document.getElementById('rotateBtn');
const flipHBtn = document.getElementById('flipHBtn');
const flipVBtn = document.getElementById('flipVBtn');
const textBtn = document.getElementById('textBtn');
const drawBtn = document.getElementById('drawBtn');
// Elementos de texto
const textOptions = document.getElementById('textOptions');
const textInput = document.getElementById('textInput');
const textColor = document.getElementById('textColor');
const textFont = document.getElementById('textFont');
const textSizeSlider = document.getElementById('textSizeSlider');
const addTextBtn = document.getElementById('addTextBtn');
// Elementos de desenho
const drawOptions = document.getElementById('drawOptions');
const drawColor = document.getElementById('drawColor');
const drawSizeSlider = document.getElementById('drawSizeSlider');
const drawStartBtn = document.getElementById('drawStartBtn');
const drawClearBtn = document.getElementById('drawClearBtn');
const drawSaveBtn = document.getElementById('drawSaveBtn');
// Elementos de crop
const cropControls = document.getElementById('cropControls');
const cropConfirmBtn = document.getElementById('cropConfirmBtn');
const cropCancelBtn = document.getElementById('cropCancelBtn');
// Tabs
const tabBtns = document.querySelectorAll('.tab-btn');
const tabContents = document.querySelectorAll('.tab-content');
// Variáveis de estado
let originalImage = null;
let currentImage = null;
let isDrawing = false;
let isCropping = false;
let cropStartX, cropStartY, cropEndX, cropEndY;
let rotationAngle = 0;
let flipH = false;
let flipV = false;
let filters = {
brightness: 0,
contrast: 0,
saturation: 100,
temperature: 0,
filter: 'none'
};
// Event Listeners
fileInput.addEventListener('change', handleImageUpload);
resetBtn.addEventListener('click', resetImage);
downloadBtn.addEventListener('click', downloadImage);
// Filtros
filterOptions.forEach(option => {
option.addEventListener('click', () => {
document.querySelector('.filter-option.active')?.classList.remove('active');
option.classList.add('active');
filters.filter = option.dataset.filter;
applyFilters();
});
});
// Ajustes
brightnessSlider.addEventListener('input', () => {
filters.brightness = parseInt(brightnessSlider.value);
applyFilters();
});
contrastSlider.addEventListener('input', () => {
filters.contrast = parseInt(contrastSlider.value);
applyFilters();
});
saturationSlider.addEventListener('input', () => {
filters.saturation = parseInt(saturationSlider.value);
applyFilters();
});
temperatureSlider.addEventListener('input', () => {
filters.temperature = parseInt(temperatureSlider.value);
applyFilters();
});
// Ferramentas
cropBtn.addEventListener('click', startCropping);
rotateBtn.addEventListener('click', rotateImage);
flipHBtn.addEventListener('click', flipHorizontal);
flipVBtn.addEventListener('click', flipVertical);
textBtn.addEventListener('click', toggleTextOptions);
drawBtn.addEventListener('click', toggleDrawOptions);
// Texto
addTextBtn.addEventListener('click', addTextToImage);
// Desenho
drawStartBtn.addEventListener('click', startDrawing);
drawClearBtn.addEventListener('click', clearDrawing);
drawSaveBtn.addEventListener('click', saveDrawing);
// Crop
cropConfirmBtn.addEventListener('click', confirmCrop);
cropCancelBtn.addEventListener('click', cancelCrop);
// Tabs
tabBtns.forEach(btn => {
btn.addEventListener('click', () => {
const tabId = btn.dataset.tab;
// Remover active de todos os botões e conteúdos
tabBtns.forEach(b => b.classList.remove('active', 'text-indigo-600', 'border-indigo-600'));
tabContents.forEach(c => c.classList.remove('active'));
// Adicionar active ao botão e conteúdo selecionado
btn.classList.add('active', 'text-indigo-600', 'border-indigo-600');
document.getElementById(tabId).classList.add('active');
});
});
// Canvas events for drawing
canvas.addEventListener('mousedown', startDraw);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', endDraw);
canvas.addEventListener('mouseout', endDraw);
// Canvas events for touch devices
canvas.addEventListener('touchstart', handleTouchStart);
canvas.addEventListener('touchmove', handleTouchMove);
canvas.addEventListener('touchend', handleTouchEnd);
// Canvas events for cropping
canvas.addEventListener('mousedown', startCrop);
canvas.addEventListener('mousemove', moveCrop);
canvas.addEventListener('mouseup', endCrop);
// Funções
function handleImageUpload(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(event) {
const img = new Image();
img.onload = function() {
originalImage = img;
currentImage = img;
setupCanvas(img);
placeholderText.style.display = 'none';
};
img.src = event.target.result;
};
reader.readAsDataURL(file);
}
function setupCanvas(img) {
// Ajustar tamanho do canvas para a imagem
const maxWidth = imageContainer.clientWidth - 40;
const maxHeight = imageContainer.clientHeight - 40;
let width = img.width;
let height = img.height;
if (width > maxWidth) {
const ratio = maxWidth / width;
width = maxWidth;
height = height * ratio;
}
if (height > maxHeight) {
const ratio = maxHeight / height;
height = maxHeight;
width = width * ratio;
}
canvas.width = width;
canvas.height = height;
// Redefinir todos os filtros e transformações
rotationAngle = 0;
flipH = false;
flipV = false;
filters = {
brightness: 0,
contrast: 0,
saturation: 100,
temperature: 0,
filter: 'none'
};
// Redefinir controles deslizantes
brightnessSlider.value = 0;
contrastSlider.value = 0;
saturationSlider.value = 100;
temperatureSlider.value = 0;
// Aplicar filtro "none" (remover qualquer filtro ativo)
document.querySelector('.filter-option.active')?.classList.remove('active');
document.querySelector('.filter-option[data-filter="none"]').classList.add('active');
filters.filter = 'none';
drawImage();
}
function drawImage() {
if (!currentImage) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Aplicar flip horizontal e vertical
if (flipH || flipV) {
ctx.save();
if (flipH && flipV) {
ctx.scale(-1, -1);
ctx.drawImage(currentImage, -canvas.width, -canvas.height, canvas.width, canvas.height);
} else if (flipH) {
ctx.scale(-1, 1);
ctx.drawImage(currentImage, -canvas.width, 0, canvas.width, canvas.height);
} else if (flipV) {
ctx.scale(1, -1);
ctx.drawImage(currentImage, 0, -canvas.height, canvas.width, canvas.height);
}
ctx.restore();
}
// Aplicar rotação
else if (rotationAngle !== 0) {
ctx.save();
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(rotationAngle * Math.PI / 180);
ctx.drawImage(currentImage, -canvas.width / 2, -canvas.height / 2, canvas.width, canvas.height);
ctx.restore();
}
// Desenhar normalmente
else {
ctx.drawImage(currentImage, 0, 0, canvas.width, canvas.height);
}
applyFilters();
}
function applyFilters() {
if (!currentImage) return;
let filterString = '';
// Brightness (brilho)
if (filters.brightness !== 0) {
filterString += `brightness(${100 + filters.brightness}%) `;
}
// Contrast (contraste)
if (filters.contrast !== 0) {
filterString += `contrast(${100 + filters.contrast}%) `;
}
// Saturation (saturação)
if (filters.saturation !== 100) {
filterString += `saturate(${filters.saturation}%) `;
}
// Temperature (temperatura)
if (filters.temperature !== 0) {
const temp = filters.temperature;
if (temp > 0) {
// Quente (mais vermelho)
filterString += `sepia(${temp}%) hue-rotate(-${temp/2}deg) saturate(${100 + temp}%) `;
} else {
// Frio (mais azul)
filterString += `sepia(${Math.abs(temp)}%) hue-rotate(${Math.abs(temp)}deg) saturate(${100 + Math.abs(temp)}%) `;
}
}
// Filtros específicos
if (filters.filter !== 'none') {
switch(filters.filter) {
case 'grayscale':
filterString += 'grayscale(100%) ';
break;
case 'sepia':
filterString += 'sepia(100%) ';
break;
case 'invert':
filterString += 'invert(100%) ';
break;
case 'blur':
filterString += 'blur(2px) ';
break;
case 'hue-rotate':
filterString += 'hue-rotate(90deg) ';
break;
}
}
canvas.style.filter = filterString.trim();
}
function resetImage() {
if (!originalImage) return;
currentImage = originalImage;
setupCanvas(originalImage);
drawImage();
// Esconder opções de texto e desenho se estiverem visíveis
textOptions.classList.add('hidden');
drawOptions.classList.add('hidden');
isDrawing = false;
isCropping = false;
cropControls.style.display = 'none';
}
function downloadImage() {
if (!currentImage) {
alert('Por favor, carregue uma imagem primeiro.');
return;
}
const link = document.createElement('a');
link.download = 'imagem-editada.png';
link.href = canvas.toDataURL('image/png');
link.click();
}
function rotateImage() {
rotationAngle += 90;
if (rotationAngle >= 360) rotationAngle = 0;
// Trocar largura e altura para rotações de 90 graus
if (rotationAngle % 180 === 90) {
const temp = canvas.width;
canvas.width = canvas.height;
canvas.height = temp;
}
drawImage();
}
function flipHorizontal() {
flipH = !flipH;
drawImage();
}
function flipVertical() {
flipV = !flipV;
drawImage();
}
function toggleTextOptions() {
if (textOptions.classList.contains('hidden')) {
textOptions.classList.remove('hidden');
drawOptions.classList.add('hidden');
isDrawing = false;
} else {
textOptions.classList.add('hidden');
}
}
function toggleDrawOptions() {
if (drawOptions.classList.contains('hidden')) {
drawOptions.classList.remove('hidden');
textOptions.classList.add('hidden');
} else {
drawOptions.classList.add('hidden');
isDrawing = false;
}
}
function addTextToImage() {
if (!textInput.value.trim()) {
alert('Por favor, introduza um texto.');
return;
}
ctx.font = `${textSizeSlider.value}px ${textFont.value}`;
ctx.fillStyle = textColor.value;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const x = canvas.width / 2;
const y = canvas.height / 2;
ctx.fillText(textInput.value, x, y);
// Atualizar currentImage para incluir o texto
const img = new Image();
img.src = canvas.toDataURL();
img.onload = function() {
currentImage = img;
};
}
function startDrawing() {
isDrawing = true;
canvas.style.cursor = 'crosshair';
}
function clearDrawing() {
drawImage(); // Redesenha a imagem original, limpando o desenho
}
function saveDrawing() {
isDrawing = false;
canvas.style.cursor = 'default';
// Atualizar currentImage para incluir o desenho
const img = new Image();
img.src = canvas.toDataURL();
img.onload = function() {
currentImage = img;
};
}
function startDraw(e) {
if (!isDrawing) return;
isDrawing = true;
ctx.beginPath();
ctx.moveTo(e.offsetX, e.offsetY);
ctx.strokeStyle = drawColor.value;
ctx.lineWidth = drawSizeSlider.value;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
}
function draw(e) {
if (!isDrawing) return;
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
}
function endDraw() {
if (!isDrawing) return;
isDrawing = false;
}
// Funções para touch events
function handleTouchStart(e) {
if (!isDrawing) return;
e.preventDefault();
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousedown', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}
function handleTouchMove(e) {
if (!isDrawing) return;
e.preventDefault();
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousemove', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}
function handleTouchEnd(e) {
if (!isDrawing) return;
e.preventDefault();
const mouseEvent = new MouseEvent('mouseup', {});
canvas.dispatchEvent(mouseEvent);
}
// Funções para crop
function startCropping() {
isCropping = true;
cropControls.style.display = 'flex';
canvas.style.cursor = 'crosshair';
}
function startCrop(e) {
if (!isCropping) return;
const rect = canvas.getBoundingClientRect();
cropStartX = e.clientX - rect.left;
cropStartY = e.clientY - rect.top;
}
function moveCrop(e) {
if (!isCropping) return;
const rect = canvas.getBoundingClientRect();
cropEndX = e.clientX - rect.left;
cropEndY = e.clientY - rect.top;
// Desenhar retângulo de seleção
drawImage(); // Redesenha a imagem original
ctx.strokeStyle = '#4f46e5';
ctx.lineWidth = 2;
ctx.setLineDash([5, 5]);
ctx.strokeRect(
cropStartX,
cropStartY,
cropEndX - cropStartX,
cropEndY - cropStartY
);
ctx.setLineDash([]);
}
function endCrop(e) {
if (!isCropping) return;
const rect = canvas.getBoundingClientRect();
cropEndX = e.clientX - rect.left;
cropEndY = e.clientY - rect.top;
}
function confirmCrop() {
if (!isCropping) return;
// Garantir que as coordenadas estão corretas (pode arrastar em qualquer direção)
const x = Math.min(cropStartX, cropEndX);
const y = Math.min(cropStartY, cropEndY);
const width = Math.abs(cropEndX - cropStartX);
const height = Math.abs(cropEndY - cropStartY);
// Criar um canvas temporário para a imagem cortada
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = width;
tempCanvas.height = height;
// Desenhar a parte cortada no canvas temporário
tempCtx.drawImage(
canvas,
x, y, width, height,
0, 0, width, height
);
// Atualizar o canvas principal
canvas.width = width;
canvas.height = height;
ctx.drawImage(tempCanvas, 0, 0);
// Atualizar currentImage
const img = new Image();
img.src = canvas.toDataURL();
img.onload = function() {
currentImage = img;
};
// Resetar estado de crop
cancelCrop();
}
function cancelCrop() {
isCropping = false;
cropControls.style.display = 'none';
canvas.style.cursor = 'default';
drawImage(); // Redesenha a imagem sem o retângulo de seleção
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=ocirema/imagem" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
</html>