↩️ Назад

Категории

Продвинутый редактор изображений размер пропорции качество масштаб

23.01.2026 | коды из категории: Создание сайтов

🎨 Продвинутый редактор изображений

📁

Загрузите изображение

Перетащите файлы или нажмите для выбора

Текущий размер: -
Оригинальный размер: -
Изменение: -

html

<div class="upload-area" id="dropArea">
      <input type="file" id="imageUpload" accept="image/*" style="display: none;">
      <div style="font-size: 24px; margin-bottom: 10px;">📁</div>
      <h3 style="margin: 10px 0; color: #667eea;">Загрузите изображение</h3>
      <p style="color: #666; margin-bottom: 20px;">Перетащите файлы или нажмите для выбора</p>
      <button onclick="document.getElementById('imageUpload').click()" style="padding: 12px 24px; background: #667eea; color: white; border: none; border-radius: 8px; cursor: pointer;">
        Выбрать файл
      </button>
    </div>
    
    <div id="resizeControls" class="hidden">
      <div class="controls-grid">
        
        <!-- Основные размеры -->
        <div class="control-group">
          <h3 style="margin-top: 0; color: #667eea;">📐 Размеры</h3>
          
          <div class="dimension-inputs">
            <div>
              <label for="widthInput">Ширина (px):</label>
              <input type="number" id="widthInput" min="1" max="5000" value="300">
            </div>
            <div>
              <label for="heightInput">Высота (px):</label>
              <input type="number" id="heightInput" min="1" max="5000" value="200">
            </div>
          </div>
          
          <label for="widthSlider">Ширина: <span id="widthValue">300</span> px</label>
          <input type="range" id="widthSlider" min="50" max="2000" value="300">
          
          <label for="heightSlider">Высота: <span id="heightValue">200</span> px</label>
          <input type="range" id="heightSlider" min="50" max="2000" value="200">
          
          <div class="checkbox-group">
            <input type="checkbox" id="lockAspect" checked>
            <label for="lockAspect" style="display: inline; margin: 0;">Сохранять пропорции</label>
          </div>
        </div>
        
        <!-- Дополнительные настройки -->
        <div class="control-group">
          <h3 style="margin-top: 0; color: #667eea;">⚙️ Дополнительно</h3>
          
          <label>Предустановленные размеры:</label>
          <div class="preset-buttons">
            <button class="preset-btn" onclick="setPreset(800, 600)">800×600</button>
            <button class="preset-btn" onclick="setPreset(1024, 768)">1024×768</button>
            <button class="preset-btn" onclick="setPreset(1920, 1080)">Full HD</button>
            <button class="preset-btn" onclick="setPreset(2560, 1440)">2K</button>
            <button class="preset-btn" onclick="setPreset(1080, 1080)">Instagram</button>
          </div>
          
          <label for="scalePercent">Масштаб (%):</label>
          <input type="range" id="scalePercent" min="10" max="200" value="100">
          <div style="text-align: center; margin-top: 5px;">
            <span id="scaleValue">100%</span>
          </div>
          
          <label for="formatSelect">Формат файла:</label>
          <select id="formatSelect">
            <option value="png">PNG (лучшее качество)</option>
            <option value="jpeg" selected>JPEG (малый размер)</option>
            <option value="webp">WebP (современный)</option>
          </select>
          
          <div id="qualityControl">
            <label for="qualitySlider">Качество: <span id="qualityValue">85%</span></label>
            <input type="range" id="qualitySlider" min="10" max="100" value="85">
          </div>
        </div>
      </div>
      
      <!-- Имя файла -->
      <div class="filename-input">
        <label for="filenameInput">Имя файла (без расширения):</label>
        <input type="text" id="filenameInput" value="измененное-изображение">
      </div>
      
      <!-- Кнопки -->
      <div class="button-group">
        <button id="downloadBtn">💾 Скачать изображение</button>
        <button id="resetBtn">🔄 Сбросить к оригиналу</button>
        <button id="copyBtn" onclick="copyToClipboard()">📋 Копировать в буфер</button>
      </div>
      
      <!-- Статус -->
      <div id="statusBar" class="status-bar hidden">
        Готово!
      </div>
    </div>
    
    <!-- Превью -->
    <div class="preview-area">
      <img id="resizableImage" src="" alt="Ваше изображение" class="hidden">
      <div class="image-info">
        <div>Текущий размер: <span id="currentSize">-</span></div>
        <div>Оригинальный размер: <span id="originalSize">-</span></div>
        <div>Изменение: <span id="scaleDiff">-</span></div>
      </div>
    </div>

java-script

// Элементы DOM
const elements = {
  fileInput: document.getElementById('imageUpload'),
  dropArea: document.getElementById('dropArea'),
  img: document.getElementById('resizableImage'),
  controls: document.getElementById('resizeControls'),
  widthSlider: document.getElementById('widthSlider'),
  heightSlider: document.getElementById('heightSlider'),
  widthInput: document.getElementById('widthInput'),
  heightInput: document.getElementById('heightInput'),
  widthValue: document.getElementById('widthValue'),
  heightValue: document.getElementById('heightValue'),
  downloadBtn: document.getElementById('downloadBtn'),
  resetBtn: document.getElementById('resetBtn'),
  lockAspect: document.getElementById('lockAspect'),
  scalePercent: document.getElementById('scalePercent'),
  scaleValue: document.getElementById('scaleValue'),
  formatSelect: document.getElementById('formatSelect'),
  qualitySlider: document.getElementById('qualitySlider'),
  qualityValue: document.getElementById('qualityValue'),
  qualityControl: document.getElementById('qualityControl'),
  filenameInput: document.getElementById('filenameInput'),
  statusBar: document.getElementById('statusBar'),
  currentSize: document.getElementById('currentSize'),
  originalSize: document.getElementById('originalSize'),
  scaleDiff: document.getElementById('scaleDiff')
};

// Переменные состояния
let state = {
  originalWidth: 0,
  originalHeight: 0,
  aspectRatio: 1,
  isLocked: true,
  currentScale: 100,
  isImageLoaded: false
};

// Drag & Drop
elements.dropArea.addEventListener('dragover', (e) => {
  e.preventDefault();
  elements.dropArea.classList.add('highlight');
});

elements.dropArea.addEventListener('dragleave', () => {
  elements.dropArea.classList.remove('highlight');
});

elements.dropArea.addEventListener('drop', (e) => {
  e.preventDefault();
  elements.dropArea.classList.remove('highlight');
  
  if (e.dataTransfer.files.length) {
    elements.fileInput.files = e.dataTransfer.files;
    handleFileSelect(e.dataTransfer.files[0]);
  }
});

elements.dropArea.addEventListener('click', () => {
  elements.fileInput.click();
});

elements.fileInput.addEventListener('change', (e) => {
  if (e.target.files[0]) {
    handleFileSelect(e.target.files[0]);
  }
});

// Обработка выбора файла - ИСПРАВЛЕННАЯ ВЕРСИЯ
function handleFileSelect(file) {
  if (!file.type.startsWith('image/')) {
    showStatus('Пожалуйста, выберите изображение!', 'error');
    return;
  }

  const reader = new FileReader();
  
  // Сбрасываем состояние
  state.isImageLoaded = false;
  
  reader.onload = (event) => {
    elements.img.src = event.target.result;
    elements.img.classList.remove('hidden');
    elements.controls.classList.remove('hidden');
    
    // Обработчик onload устанавливаем ТОЛЬКО если изображение еще не загружено
    if (!state.isImageLoaded) {
      elements.img.onload = () => {
        state.isImageLoaded = true;
        
        // Сохраняем оригинальные размеры
        state.originalWidth = elements.img.naturalWidth;
        state.originalHeight = elements.img.naturalHeight;
        state.aspectRatio = state.originalWidth / state.originalHeight;
        
        // Обновляем информацию
        updateSizeInfo();
        
        // Устанавливаем начальные значения
        updateSlidersFromImage();
        
        // Обновляем имя файла
        const name = file.name.substring(0, file.name.lastIndexOf('.'));
        elements.filenameInput.value = name + '-измененное';
        
        showStatus('Изображение загружено!', 'success');
      };
    }
  };
  
  reader.onerror = () => {
    showStatus('Ошибка при чтении файла', 'error');
  };
  
  reader.readAsDataURL(file);
}

// Обновление слайдеров по изображению
function updateSlidersFromImage() {
  const width = elements.img.naturalWidth;
  const height = elements.img.naturalHeight;
  
  // Устанавливаем максимальные значения слайдеров
  const maxSize = Math.max(width, height) * 3;
  elements.widthSlider.max = maxSize;
  elements.heightSlider.max = maxSize;
  elements.widthInput.max = maxSize;
  elements.heightInput.max = maxSize;
  
  // Устанавливаем текущие значения
  elements.widthSlider.value = width;
  elements.heightSlider.value = height;
  elements.widthInput.value = width;
  elements.heightInput.value = height;
  elements.widthValue.textContent = width;
  elements.heightValue.textContent = height;
  
  // Устанавливаем масштаб
  state.currentScale = 100;
  elements.scalePercent.value = 100;
  elements.scaleValue.textContent = '100%';
  
  // Применяем размеры
  updateImageSize(width, height);
}

// Обновление размера изображения
function updateImageSize(width, height) {
  elements.img.style.width = width + 'px';
  elements.img.style.height = height + 'px';
  
  // Обновляем значения в интерфейсе
  elements.widthValue.textContent = width;
  elements.heightValue.textContent = height;
  elements.widthInput.value = width;
  elements.heightInput.value = height;
  
  // Обновляем информацию о размере
  updateSizeInfo();
}

// Обновление информации о размере
function updateSizeInfo() {
  const currentWidth = parseInt(elements.img.style.width) || state.originalWidth;
  const currentHeight = parseInt(elements.img.style.height) || state.originalHeight;
  
  elements.currentSize.textContent = `${currentWidth} × ${currentHeight}`;
  elements.originalSize.textContent = `${state.originalWidth} × ${state.originalHeight}`;
  
  const scale = Math.round((currentWidth / state.originalWidth) * 100);
  elements.scaleDiff.textContent = `${scale > 100 ? '+' : ''}${scale - 100}%`;
  state.currentScale = scale;
  elements.scalePercent.value = scale;
  elements.scaleValue.textContent = `${scale}%`;
}

// События слайдеров
elements.widthSlider.addEventListener('input', () => {
  let width = parseInt(elements.widthSlider.value);
  let height = parseInt(elements.heightSlider.value);
  
  if (elements.lockAspect.checked) {
    height = Math.round(width / state.aspectRatio);
    elements.heightSlider.value = height;
  }
  
  updateImageSize(width, height);
});

elements.heightSlider.addEventListener('input', () => {
  let width = parseInt(elements.widthSlider.value);
  let height = parseInt(elements.heightSlider.value);
  
  if (elements.lockAspect.checked) {
    width = Math.round(height * state.aspectRatio);
    elements.widthSlider.value = width;
  }
  
  updateImageSize(width, height);
});

// События числовых полей
elements.widthInput.addEventListener('change', () => {
  let width = parseInt(elements.widthInput.value);
  let height = parseInt(elements.heightInput.value);
  
  if (elements.lockAspect.checked) {
    height = Math.round(width / state.aspectRatio);
    elements.heightInput.value = height;
    elements.heightSlider.value = height;
  }
  
  elements.widthSlider.value = width;
  updateImageSize(width, height);
});

elements.heightInput.addEventListener('change', () => {
  let width = parseInt(elements.widthInput.value);
  let height = parseInt(elements.heightInput.value);
  
  if (elements.lockAspect.checked) {
    width = Math.round(height * state.aspectRatio);
    elements.widthInput.value = width;
    elements.widthSlider.value = width;
  }
  
  elements.heightSlider.value = height;
  updateImageSize(width, height);
});

// Событие масштаба
elements.scalePercent.addEventListener('input', () => {
  const scale = parseInt(elements.scalePercent.value);
  elements.scaleValue.textContent = `${scale}%`;
  
  const width = Math.round(state.originalWidth * (scale / 100));
  const height = Math.round(state.originalHeight * (scale / 100));
  
  updateImageSize(width, height);
});

// Формат файла
elements.formatSelect.addEventListener('change', () => {
  const format = elements.formatSelect.value;
  elements.qualityControl.style.display = (format === 'jpeg' || format === 'webp') ? 'block' : 'none';
});

// Качество
elements.qualitySlider.addEventListener('input', () => {
  elements.qualityValue.textContent = `${elements.qualitySlider.value}%`;
});

// Сброс к оригиналу
elements.resetBtn.addEventListener('click', () => {
  updateImageSize(state.originalWidth, state.originalHeight);
  showStatus('Размер сброшен к оригиналу', 'info');
});

// Предустановки
function setPreset(width, height) {
  updateImageSize(width, height);
  showStatus(`Установлен размер ${width}×${height}`, 'info');
}

// Скачивание
elements.downloadBtn.addEventListener('click', () => {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  
  const displayWidth = parseInt(elements.img.style.width);
  const displayHeight = parseInt(elements.img.style.height);
  
  canvas.width = displayWidth;
  canvas.height = displayHeight;
  
  // Фон для JPEG
  if (elements.formatSelect.value === 'jpeg') {
    ctx.fillStyle = 'white';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
  }
  
  ctx.drawImage(elements.img, 0, 0, displayWidth, displayHeight);
  
  // Параметры экспорта
  const format = elements.formatSelect.value;
  const quality = parseInt(elements.qualitySlider.value) / 100;
  let mimeType, extension;
  
  switch(format) {
    case 'jpeg':
      mimeType = 'image/jpeg';
      extension = 'jpg';
      break;
    case 'webp':
      mimeType = 'image/webp';
      extension = 'webp';
      break;
    case 'png':
    default:
      mimeType = 'image/png';
      extension = 'png';
      break;
  }
  
  // Создаем имя файла
  let filename = elements.filenameInput.value.trim();
  if (!filename) filename = 'измененное-изображение';
  if (!filename.endsWith('.' + extension)) {
    filename += '.' + extension;
  }
  
  // Скачивание
  const link = document.createElement('a');
  link.download = filename;
  link.href = canvas.toDataURL(mimeType, quality);
  link.click();
  
  showStatus(`Изображение скачано как ${filename}`, 'success');
});

// Копирование в буфер
function copyToClipboard() {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  
  const displayWidth = parseInt(elements.img.style.width);
  const displayHeight = parseInt(elements.img.style.height);
  
  canvas.width = displayWidth;
  canvas.height = displayHeight;
  
  ctx.drawImage(elements.img, 0, 0, displayWidth, displayHeight);
  
  canvas.toBlob(blob => {
    navigator.clipboard.write([
      new ClipboardItem({
        [blob.type]: blob
      })
    ]).then(() => {
      showStatus('Изображение скопировано в буфер обмена!', 'success');
    }).catch(err => {
      showStatus('Не удалось скопировать в буфер', 'error');
      console.error(err);
    });
  });
}

// Показать статус
function showStatus(message, type = 'info') {
  elements.statusBar.textContent = message;
  elements.statusBar.className = 'status-bar ' + type;
  elements.statusBar.classList.remove('hidden');
  
  setTimeout(() => {
    elements.statusBar.classList.add('hidden');
  }, 3000);
}

// Инициализация
document.addEventListener('DOMContentLoaded', () => {
  showStatus('Загрузите изображение для начала работы', 'info');
  elements.qualityControl.style.display = 'block';
});  

css

.upload-area {
  margin-bottom: 30px;
  text-align: center;
  padding: 40px;
  border: 3px dashed #7f8c8d;
  border-radius: 15px;
  background: rgba(127, 140, 141, 0.1);
  transition: all 0.3s;
  cursor: pointer;
}

.upload-area:hover {
  background: rgba(127, 140, 141, 0.2);
  border-color: #95a5a6;
}

.upload-area.highlight {
  background: rgba(46, 204, 113, 0.1);
  border-color: #27ae60;
}

.upload-area h3 {
  color: #bdc3c7;
  margin: 10px 0;
}

.upload-area p {
  color: #95a5a6;
  margin-bottom: 20px;
}

.upload-area button {
  padding: 12px 24px;
  background: #7f8c8d;
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-weight: 600;
  transition: all 0.3s;
}

.upload-area button:hover {
  background: #95a5a6;
  transform: translateY(-2px);
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}

.controls-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 25px;
  margin: 30px 0;
}

.control-group {
  background: rgba(52, 73, 94, 0.7);
  padding: 20px;
  border-radius: 12px;
  border-left: 4px solid #7f8c8d;
  border: 1px solid #3d566e;
}

.control-group h3 {
  margin-top: 0;
  color: #ecf0f1;
  font-size: 1.3em;
}

label {
  display: block;
  margin: 12px 0 6px;
  font-weight: 600;
  color: #bdc3c7;
}

input[type="range"] {
  width: 100%;
  height: 8px;
  background: #3d566e;
  border-radius: 4px;
  outline: none;
  -webkit-appearance: none;
}

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 22px;
  height: 22px;
  background: #95a5a6;
  border-radius: 50%;
  cursor: pointer;
  transition: all 0.3s;
  border: 2px solid #2c3e50;
}

input[type="range"]::-webkit-slider-thumb:hover {
  background: #bdc3c7;
}

input[type="number"], 
select, 
input[type="text"] {
  width: 100%;
  padding: 10px 14px;
  border: 2px solid #3d566e;
  border-radius: 8px;
  font-size: 16px;
  transition: all 0.3s;
  background: #34495e;
  color: #ecf0f1;
  box-sizing: border-box;
}

input[type="number"]:focus, 
select:focus, 
input[type="text"]:focus {
  outline: none;
  border-color: #95a5a6;
}

.dimension-inputs {
  display: flex;
  gap: 15px;
  margin-top: 15px;
}

.dimension-inputs > div {
  flex: 1;
}

.checkbox-group {
  display: flex;
  align-items: center;
  margin: 15px 0;
}

.checkbox-group input {
  margin-right: 10px;
  width: auto;
  accent-color: #95a5a6;
}

.checkbox-group label {
  display: inline;
  margin: 0;
  color: #bdc3c7;
}

.button-group {
  display: flex;
  gap: 15px;
  margin-top: 30px;
  flex-wrap: wrap;
}

#downloadBtn {
  background: #7f8c8d;
  color: white;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}

#downloadBtn:hover {
  background: #95a5a6;
  transform: translateY(-3px);
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
}

#resetBtn {
  background: #5d6d7e;
  color: #ecf0f1;
  border: 1px solid #5d6d7e;
}

#resetBtn:hover {
  background: #6c7b8a;
  transform: translateY(-2px);
}

#copyBtn {
  background: #6c7b8a;
  color: white;
}

#copyBtn:hover {
  background: #7d8c9b;
  transform: translateY(-3px);
}

.preview-area {
  margin-top: 40px;
  text-align: center;
}

#resizableImage {
  max-width: 100%;
  height: auto;
  border: 3px solid #3d566e;
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
  margin: 0 auto;
  display: block;
  background: rgba(0, 0, 0, 0.1);
}

.image-info {
  display: flex;
  justify-content: center;
  gap: 30px;
  margin-top: 20px;
  color: #95a5a6;
  font-size: 14px;
  background: rgba(52, 73, 94, 0.7);
  padding: 15px;
  border-radius: 10px;
  border: 1px solid #3d566e;
}

.image-info div {
  padding: 0 10px;
}

.hidden {
  display: none;
}

.preset-buttons {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 15px;
}

.preset-btn {
  padding: 8px 16px;
  background: rgba(127, 140, 141, 0.2);
  border: 1px solid #7f8c8d;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.2s;
  color: #bdc3c7;
  font-size: 14px;
}

.preset-btn:hover {
  background: rgba(127, 140, 141, 0.4);
  transform: translateY(-2px);
}

.filename-input {
  margin-top: 20px;
  background: rgba(52, 73, 94, 0.7);
  padding: 15px;
  border-radius: 10px;
  border: 1px solid #3d566e;
}

.status-bar {
  padding: 15px;
  border-radius: 8px;
  margin-top: 20px;
  text-align: center;
  font-weight: 600;
  transition: all 0.3s;
}

.status-bar.success {
  background: rgba(39, 174, 96, 0.2);
  color: #2ecc71;
  border: 1px solid rgba(46, 204, 113, 0.3);
}

.status-bar.error {
  background: rgba(231, 76, 60, 0.2);
  color: #e74c3c;
  border: 1px solid rgba(231, 76, 60, 0.3);
}

.status-bar.info {
  background: rgba(127, 140, 141, 0.2);
  color: #bdc3c7;
  border: 1px solid rgba(127, 140, 141, 0.3);
}

select option {
  background: #34495e;
  color: #ecf0f1;
}

/* Адаптивность */
@media (max-width: 768px) {
  .controls-grid {
    grid-template-columns: 1fr;
  }
  
  button {
    min-width: 100%;
  }
  
  .image-info {
    flex-direction: column;
    gap: 10px;
    text-align: center;
  }
  
  .dimension-inputs {
    flex-direction: column;
    gap: 10px;
  }
}




Категории:

Категории

Комментарии

Пока нет комментариев. Будьте первым!

Оставить комментарий

← Назад к списку

Посетителей сегодня: 0
о блоге | карта блога

© Digital Specialist | Не являемся сотрудниками Google, Яндекса и NASA